/// <summary> /// Clean up after BackgroundWorker finished. /// Throws Exceptions, but cannot interupt main thread. /// </summary> /// <exception cref="FileFormatException"> /// Thrown, if file at <see cref="Path" /> is not a valid Excel sheet. /// </exception> public void LoadExcelLanguageFileAsyncCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { _logger.Log(LogLevel.Debug, "Loading of the language file was stoped."); } else if (e.Error != null) { if (e.Error.HResult == -2146827284) { ExceptionLoggingUtils.Throw(_logger, new FileFormatException(new Uri(Path), "Expected Excel file format.", e.Error), "File at given path may be corrupted or not have correct format. " + "Expected Excel sheet (.xlsx, .xls, ...)."); } else if (e.Error.GetType() == typeof(CultureNotFoundException)) { _logger.Log(LogLevel.Error, e.Error, "One of the cells in the first row was expected to " + "contain a language code, but did not."); } else { _logger.Log(LogLevel.Error, e.Error, "Unknown error occurred during language file loading."); } } else { _logger.Log(LogLevel.Debug, "BackgroundWorker successfully finished loading the language file."); } }
/// <summary> /// Creates the instance of the ExcelFileProvider, which reads and persists all translations as Excel-files. /// A backup Excel-file will be created for all edits. /// </summary> /// <param name="translationFilePath">Path to the file containing the translations.</param> /// <param name="glossaryTag"> /// (Optional) Entries in the Excel table that start with this tag will be interpreted as part of the glossary. /// </param> /// <param name="oldTranslationFilePath"> /// (Optional) The path to where the original translation file will be copied as a backup. /// </param> /// <exception cref="ArgumentNullException"> /// Thrown, if <paramref name="translationFilePath" /> is null. /// </exception> /// <exception cref="UnauthorizedAccessException"> /// Thrown, if the permissions are missing that are needed to create the directory for /// <paramref name="translationFilePath" /> / <paramref name="oldTranslationFilePath" /> or one of them /// is write-only, read-only, a directory, hidden, the needed permissions for opening or writing are /// missing or the operation is not supported on the current platform. /// </exception> /// <exception cref="System.Security.SecurityException"> /// Thrown, if certain permissions are missing. (CLR level) /// </exception> /// <exception cref="FileNotFoundException"> /// Thrown, if <paramref name="translationFilePath" /> / <paramref name="oldTranslationFilePath" /> /// does not exist or cannot be found. /// </exception> /// <exception cref="IOException"> /// Thrown, if an unknown I/O-Error occurs. /// </exception> /// <exception cref="NotSupportedException"> /// Thrown, if <paramref name="translationFilePath" /> / <paramref name="oldTranslationFilePath" /> /// contains a colon anywhere other than as part of a volume identifier ("C:\"). /// </exception> /// <exception cref="PathTooLongException"> /// Thrown, if <paramref name="translationFilePath" /> / <paramref name="oldTranslationFilePath" /> /// is too long. /// </exception> /// <exception cref="DirectoryNotFoundException"> /// Thrown, if the directory was not found. /// For example because it is on an unmapped device. /// </exception> public ExcelFileProvider(string translationFilePath, string glossaryTag = null, string oldTranslationFilePath = null) { //set Status. Status = ProviderStatus.InitializationInProgress; //easy initializations. _logger = GlobalSettings.LibraryLoggerFactory.CreateLogger <ExcelFileProvider>(); _logger.Log(LogLevel.Trace, "Initializing ExcelFileProvider."); _fileHandler = new ExcelFileHandler(typeof(ExcelFileProvider), //make sure all possible not-null values for glossaryTag can be recognized in sheet. glossaryTag == string.Empty ? null : glossaryTag); //null check. ExceptionLoggingUtils.ThrowIfNull(_logger, (object)translationFilePath, nameof(translationFilePath), "Unable to open null path.", "ExcelFileProvider received null parameter in constructor."); //start difficult initializations. if (oldTranslationFilePath != null) { _fileHandler.VerifyPath(oldTranslationFilePath); _backupPath = oldTranslationFilePath; } _fileHandler.VerifyPath(translationFilePath); _path = translationFilePath; _fileHandler.Path = _path; Initialize(); }
/// <summary> /// Goes through all elements nested inside <paramref name="rootElement" /> and writes the current /// translation into each element. /// </summary> /// <param name="rootElement"> /// The element, based on which all nested elements will recursively be translated. /// <paramref name="rootElement" /> itself will also be translated. /// </param> public static void TranslateGui(FrameworkElement rootElement) { //null check. ExceptionLoggingUtils.ThrowIfNull(Logger, rootElement, nameof(rootElement), "Unable to translate null UserControl / Window.", "TranslateGui received null as root element for translation."); //TranslateGuiElement will do nothing if visual is a non translatable like Grid. TranslateGuiElement(rootElement); foreach (var childVisual in LogicalTreeHelper.GetChildren(rootElement)) { //if View or Window contain a TabControl - break foreach to prevent translating sub View. if (childVisual is TabControl) { break; } if (childVisual is FrameworkElement element) { //also iterate all childs of visual, if they are FrameworkElements. //as DataGridColumns are not FrameworkElements, they require special treatment in TranslateGuiElement. TranslateGui(element); } } }
/// <exception cref="ResourcesNotFoundException"> /// Thrown, if both <see cref="GlobalSettings.ResourcesAssembly" /> is not set and the entry assembly /// cannot be accesed. /// </exception> private void ReadDicts() { var rm = ResourcesManagerProvider.GetResourcesManager(); Dictionary <string, string> invariantFallback = null; //collect all Resource entries. var langs = CultureInfo.GetCultures(CultureTypes.AllCultures); foreach (var lang in langs) { try { //tryParents is false and will be handled in CultureInfoUtils insted, to avoid registering //same dict multiple times. var resourceSet = rm.GetResourceSet(lang, true, false); if (resourceSet == null) { continue; } if (lang.Equals(CultureInfo.InvariantCulture)) { invariantFallback = resourceSet.Cast <DictionaryEntry>().ToDictionary( r => r.Key.ToString(), r => r.Value.ToString()); } else { _dictOfDicts.Add(lang, resourceSet.Cast <DictionaryEntry>().ToDictionary( r => r.Key.ToString(), r => r.Value.ToString())); } } catch (CultureNotFoundException) { //all non-existent languages will be ignored. } } TryAddChangesIntoDictOfDicts(); if (_dictOfDicts.ContainsKey(InputLanguage)) { _dictOfDicts.Add(CultureInfo.InvariantCulture, invariantFallback); } //if Inputlanguage is not present, use invariant as replacement instead, because //InputLanguage is expected to always exist. else { ExceptionLoggingUtils.ThrowIf <InputLanguageNotFoundException>(invariantFallback == null, _logger, $"The given input language ({InputLanguage.EnglishName}) was not found in the " + "Resources files."); _dictOfDicts.Add(InputLanguage, invariantFallback); } _status = ProviderStatus.Initialized; }
/// <summary> /// Returns the internal dictionary of translations. /// </summary> /// <exception cref="FileProviderNotInitializedException"> /// Will be thrown if the object has not found a language file to pull translations from. /// </exception> public Dictionary <CultureInfo, Dictionary <string, string> > GetDictionary() { if (Status == ProviderStatus.Empty || Status == ProviderStatus.Initialized) { return(_dictOfDicts); } //ExcelFileProvider is still initializing, cancelling or cancelled. ExceptionLoggingUtils.Throw <FileProviderNotInitializedException>(_logger, "Dictionary was accessed, without ExcelFileProvider being initialized."); throw new NotSupportedException("unreachable code."); }
/// <summary> /// Handles Exception logging for the given <paramref name="path" />. /// Returns the given <paramref name="path" /> with the appropriate ending, if it was not present. /// </summary> /// <param name="path">The path that should be verified.</param> /// <exception cref="ArgumentNullException">Thrown, if <paramref name="path" /> is null.</exception> /// <exception cref="ArgumentException"> /// Thrown, if <paramref name="path" /> contains only white space, includes /// unsupported characters or if the system fails to get the fully qualified /// location for the given path. /// </exception> /// <exception cref="System.Security.SecurityException"> /// Thrown, if the permissions for accessing the full path are missing. /// </exception> /// <exception cref="NotSupportedException"> /// Thrown, if <paramref name="path" /> contains a colon anywhere other than as part of a /// volume identifier ("C:\"). /// </exception> /// <exception cref="PathTooLongException"> /// Thrown, if <paramref name="path" /> is too long. /// </exception> /// <exception cref="UnauthorizedAccessException"> /// Thrown, if permissions to create the directory are missing. /// </exception> /// <exception cref="DirectoryNotFoundException"> /// Thrown, if the directory was not found. /// For example because it is on an unmapped device. /// </exception> /// <exception cref="IOException"> /// Thrown, if a file with the name of the dictionary that should be created already exists. /// </exception> /// <exception cref="FileNotFoundException"> /// Thrown, if <paramref name="path" /> is a dictionary. /// </exception> public void VerifyPath(string path) { ExceptionLoggingUtils.ThrowIfNull(_logger, nameof(VerifyPath), (object)path, nameof(path), "Unable to open path, because it is null."); var fullPath = GetFullPathWrapper(path); if (File.Exists(fullPath)) { return; } ExceptionLoggingUtils.ThrowIf(Directory.Exists(fullPath), _logger, new FileNotFoundException( "Unable to find file, because directory with same name exists.", path), "Given path is directory instead of file."); CreateDirectoryWrapper(fullPath); }
/// <summary> /// Updates the internal dictionary of translations at <paramref name="key" /> with the given dictionary. /// Only languages contained in <paramref name="texts" /> will be updated. /// Will automatically write to file, if this is the first Update call /// and no file existed upon creation of this object. /// </summary> /// <exception cref="ArgumentNullException">Thrown, if <paramref name="key" /> is null.</exception> /// <param name="key">The entry for which translations should be updated.</param> /// <param name="texts"> /// The new translations. If list is null or empty, no changes will be made to the dictionary. /// </param> public void Update(string key, IEnumerable <TextLocalization> texts) { //null checks. ExceptionLoggingUtils.ThrowIfNull(_logger, nameof(Update), (object)key, nameof(key), "Unable to update dictionary for null key."); if (texts == null) { //no exception has to be thrown here, because null is treated like empty list and //no translations will be updated. _logger.Log(LogLevel.Debug, "Unable to update dictionary for null translations. " + "No translations were updated."); return; } //logging. IList <TextLocalization> textsEnumerated = texts.ToList(); var textsString = string.Join(", ", textsEnumerated.Select(l => l.ToString())); _logger.Log(LogLevel.Trace, $"Update was called with {{{textsString}}} as translations for key ({key})."); //dictionary updates. if (!UpdateDictionary(key, textsEnumerated)) { _logger.Log(LogLevel.Debug, "Did not update dictionary."); return; } //create file based on first entry, //if dictionary was updated and the file was created by JsonFileProvider itself. if (Status == ProviderStatus.Empty) { _logger.Log(LogLevel.Debug, "First update after empty sheet was created."); _fileHandler.ExcelWriteActions(_dictOfDicts); Status = ProviderStatus.Initialized; _logger.Log(LogLevel.Information, "Finished updating dictionary. " + "ExcelFileProvider is now in State Initialized."); } else { _logger.Log(LogLevel.Debug, "Finished updating dictionary."); } }
/// <summary> /// Creates the instance of the JsonFileProvider, which reads and persists all translations from Json-files. /// </summary> /// <param name="translationFilePath">The path under which the dictionary will be saved.</param> /// <exception cref="ArgumentNullException"> /// Thrown, if <paramref name="translationFilePath" /> is null. /// </exception> /// <exception cref="UnauthorizedAccessException"> /// Thrown, if the permissions are missing that are needed to create the directory for /// <paramref name="translationFilePath" /> or <paramref name="translationFilePath" /> is write-only, /// read-only, a directory, hidden, the needed permissions for opening or writing are missing or /// the operation is not supported on the current platform. /// </exception> /// <exception cref="System.Security.SecurityException"> /// Thrown, if certain permissions are missing. (CLR level) /// </exception> /// <exception cref="FileNotFoundException"> /// Thrown, if <paramref name="translationFilePath" /> does not exist or cannot be found. /// For example because it is a direcory. /// </exception> /// <exception cref="IOException"> /// Thrown, if an unknown I/O-Error occurs. /// </exception> /// <exception cref="NotSupportedException"> /// Thrown, if <paramref name="translationFilePath" /> contains a colon anywhere other than as part of a /// volume identifier ("C:\"). /// </exception> /// <exception cref="PathTooLongException"> /// Thrown, if <paramref name="translationFilePath" /> is too long. /// </exception> /// <exception cref="DirectoryNotFoundException"> /// Thrown, if the directory was not found. /// For example because it is on an unmapped device. /// </exception> public JsonFileProvider(string translationFilePath) { //set Status. Status = ProviderStatus.InitializationInProgress; //easy initializations. _logger = GlobalSettings.LibraryLoggerFactory.CreateLogger <JsonFileProvider>(); _logger.Log(LogLevel.Trace, "Initializing JsonFileProvider."); _fileHandler = new UniversalFileHandler(typeof(JsonFileProvider)); //null check. ExceptionLoggingUtils.ThrowIfNull(_logger, (object)translationFilePath, nameof(translationFilePath), "Unable to open null path.", "JsonFileProvider received null parameter in constructor."); //start proper initialization. _fileHandler.VerifyPath(translationFilePath); _path = translationFilePath; Initialize(); }
/// <summary> /// Work for BackgroundWorker. /// Loads the excel sheet at <see cref="Path" /> and saves it as its result. /// <see cref="Path" /> has to be set to a value, before this handler is used. /// </summary> /// <exception cref="ArgumentNullException"> /// Thrown, if <paramref name="sender" /> or <paramref name="e" /> is null. /// </exception> /// <exception cref="ArgumentException"> /// Thrown, if <paramref name="sender" /> is not of type BackgroundWorker. /// </exception> /// <exception cref="NotSupportedException"> /// Thrown, if <see cref="Path" /> is not set before this function is called /// - or - if <see cref="Path" /> contains a colon anywhere other than as part of a /// volume identifier ("C:\"). /// </exception> /// <exception cref="System.Security.SecurityException"> /// Thrown, if the permissions for accessing the full path are missing. /// </exception> /// <exception cref="PathTooLongException"> /// Thrown, if <see cref="Path" /> is too long. /// </exception> /// <exception cref="UnauthorizedAccessException"> /// Thrown, if permissions to create the directory are missing. /// </exception> /// <exception cref="DirectoryNotFoundException"> /// Thrown, if the directory was not found. /// For example because it is on an unmapped device. /// </exception> /// <exception cref="IOException"> /// Thrown, if a file with the name of the dictionary that should be created already exists. /// </exception> /// <exception cref="FileNotFoundException"> /// Thrown, if <see cref="Path" /> is a dictionary. /// </exception> public void LoadExcelLanguageFileAsync(object sender, DoWorkEventArgs e) { var bw = sender as BackgroundWorker; //null and argument checks. ExceptionLoggingUtils.VerifyMultiple(e, nameof(e)) .AlsoVerify(sender, nameof(sender)) .ThrowIfNull(_logger, nameof(LoadExcelLanguageFileAsync), "Parameter for DoWork event handler cannot be null."); ExceptionLoggingUtils.ThrowIf(bw == null, _logger, new ArgumentException( "Sender for DoWork event handler is not of type BackgroundWorker.", nameof(sender)), "LoadExcelLanguageFileAsync functions was called without BackgroundWorker."); ExceptionLoggingUtils.ThrowIf(Path == null, _logger, new NotSupportedException("Path property for DoWork event handler was not set."), "LoadExcelLanguageFileAsync functions was called without Path property being set beforehand."); _logger.Log(LogLevel.Trace, "LoadExcelLanguageFileAsync functions was called by BackgroundWorker."); VerifyPath(Path); LoadExcelLanguageFileAsyncInternal(bw, e, Path); }