/// ------------------------------------------------------------------------------------ private void SaveFileForLangId(string langId, bool forceCreation) { var tmxDoc = CreateEmptyStringFile(); tmxDoc.Header.SourceLang = langId; tmxDoc.Header.SetPropValue(LocalizationManager.kAppVersionPropTag, OwningManager.AppVersion); foreach (var tu in TmxDocument.Body.TransUnits) { var tuv = tu.GetVariantForLang(langId); if (tuv == null) { continue; } var newTu = new TMXTransUnit { Id = tu.Id }; tmxDoc.AddTransUnit(newTu); newTu.AddOrReplaceVariant(tu.GetVariantForLang(LocalizationManager.kDefaultLang)); newTu.AddOrReplaceVariant(tuv); newTu.Notes = tu.CopyNotes(); newTu.Props = tu.CopyProps(); } tmxDoc.Body.TransUnits.Sort(TuComparer); if (forceCreation || OwningManager.DoesCustomizedTranslationExistForLanguage(langId)) { tmxDoc.Save(OwningManager.GetTmxPathForLanguage(langId, true)); } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Saves the cache to the file from which the cache was originally loaded, but only /// if the cache is dirty. If the cache is dirty and saved, then true is returned. /// Otherwise, false is returned. /// </summary> /// ------------------------------------------------------------------------------------ internal void SaveIfDirty(ICollection <string> langIdsToForceCreate) { if (!IsDirty) { return; } StringBuilder errorMsg = null; // ToArray() prevents exception "Collection was modified" in rare cases (e.g., Bloom BL-2400). foreach (var langId in TmxDocument.GetAllVariantLanguagesFound().ToArray()) { try { SaveFileForLangId(langId, langIdsToForceCreate != null && langIdsToForceCreate.Contains(langId)); } catch (Exception e) { if (e is SecurityException || e is UnauthorizedAccessException || e is IOException) { if (errorMsg == null) { errorMsg = new StringBuilder(); errorMsg.AppendLine("Failed to save localization changes in the following files:"); } errorMsg.AppendLine(); errorMsg.Append("File: "); errorMsg.AppendLine(OwningManager.GetTmxPathForLanguage(langId, true)); errorMsg.Append("Error Type: "); errorMsg.AppendLine(e.GetType().ToString()); errorMsg.Append("Message: "); errorMsg.AppendLine(e.Message); } } } if (errorMsg != null) { throw new IOException(errorMsg.ToString()); } IsDirty = false; }
/// ------------------------------------------------------------------------------------ private void MergeTmxFilesIntoCache(IEnumerable <string> tmxFiles) { var defaultTmxDoc = TMXDocument.Read(OwningManager.DefaultStringFilePath); foreach (var tu in defaultTmxDoc.Body.TransUnits) { TmxDocument.Body.AddTransUnit(tu); // If this TMXTransUnit is marked NoLongerUsed in the English tmx, load its ID into the hashset // so we don't display this string in case the currentUI tmx doesn't have it marked as // NoLongerUsed. if (tu.GetPropValue(kNoLongerUsedPropTag) != null) { _englishTuIdsNoLongerUsed.Add(tu.Id); } } // It's possible (I think when there is no customizable TMX, as on first install, but the version in the installed TMX // is out of date with the app) that we don't have all the info from the installed TMX in the customizable one. // We want to make sure that (a) any new dynamic strings in the installed one are considered valid by default // (b) any newly obsolete IDs are noted. if (File.Exists(OwningManager.DefaultInstalledStringFilePath)) { var defaultInstalledTmxDoc = TMXDocument.Read(OwningManager.DefaultInstalledStringFilePath); foreach (var tu in defaultInstalledTmxDoc.Body.TransUnits) { TmxDocument.Body.AddTransUnitOrVariantFromExisting(tu, "en"); // also needed in this, to prevent things we find here from being considered orphans. defaultTmxDoc.Body.AddTransUnitOrVariantFromExisting(tu, "en"); // If this TMXTransUnit is marked NoLongerUsed in the English tmx, load its ID into the hashset // so we don't display this string in case the currentUI tmx doesn't have it marked as // NoLongerUsed. if (tu.GetPropValue(kNoLongerUsedPropTag) != null) { _englishTuIdsNoLongerUsed.Add(tu.Id); } } } // Map the default language onto itself. LocalizationManagerInternal <TMXDocument> .MapToExistingLanguage[LocalizationManager.kDefaultLang] = LocalizationManager.kDefaultLang; Exception error = null; var stillNeedToLoadNoLongerUsed = _englishTuIdsNoLongerUsed.Count == 0; foreach (var file in tmxFiles.Where(f => Path.GetFileName(f) != OwningManager.DefaultStringFilePath)) { try { var tmxDoc = TMXDocument.Read(file); var langId = tmxDoc.Header.SourceLang; // Provide a mapping from a specific variant of a language to the base language. // For example, "es-ES" can provide translations for "es" if we don't have "es" specifically. var pieces = langId.Split('-'); if (pieces.Length > 1) { if (!LocalizationManagerInternal <TMXDocument> .MapToExistingLanguage.ContainsKey (pieces[0])) { LocalizationManagerInternal <TMXDocument> .MapToExistingLanguage.TryAdd(pieces[0], langId); } } // Identity mapping always wins. Storing it simplifies code elsewhere. LocalizationManagerInternal <TMXDocument> .MapToExistingLanguage[langId] = langId; foreach (var tu in tmxDoc.Body.TransUnits) { // This block attempts to find 'orphans', that is, localizations that have been done using an obsolete ID. // We assume the default language TMX has only current IDs, and therefore don't look for orphans in that case. // This guards against cases such as recently occurred in Bloom, where a dynamic ID EditTab.AddPageDialog.Title // was regarded as an obsolete id for PublishTab.Upload.Title if (langId != LocalizationManager.kDefaultLang && defaultTmxDoc.GetTransUnitForId(tu.Id) == null && !tu.Id.EndsWith(kToolTipSuffix) && !tu.Id.EndsWith(kShortcutSuffix)) { //if we couldn't find it, maybe the id just changed and then if so re-id it. var movedUnit = defaultTmxDoc.GetTransUnitForOrphan(tu); if (movedUnit == null) { if (tu.GetPropValue(kDiscoveredDynamically) == "true") { //ok, no big deal, that what we expect with dynamic strings, by definition... that we won't find them during a static code scan } else { tu.AddProp(kNoLongerUsedPropTag, "true"); } } else { tu.Id = movedUnit.Id; } } TmxDocument.Body.AddTransUnitOrVariantFromExisting(tu, langId); if (stillNeedToLoadNoLongerUsed && langId == LocalizationManager.kDefaultLang && tu.GetPropValue(kNoLongerUsedPropTag) != null) { _englishTuIdsNoLongerUsed.Add(tu.Id); } } } catch (Exception e) { #if DEBUG throw e; #else //REVIEW: Better explain the conditions where we get an error on a file but don't care about it? // If error happened reading some localization file other than the one we care // about right now, just ignore it. if (file == OwningManager.GetTmxPathForLanguage(LocalizationManager.UILanguageId, false)) { error = e; } #endif } } if (error != null) { throw error; } }