public void CompareInvariantCultures_UsingExcept_NoChanges() { var incomingResource = new LocalizationResource("key") { Translations = new List <LocalizationResourceTranslation> { new LocalizationResourceTranslation { Language = "", Value = "incoming value" } } }; var existingResource = new LocalizationResource("key") { Translations = new List <LocalizationResourceTranslation> { new LocalizationResourceTranslation { Language = "", Value = "existing value" } } }; var sut = new TranslationComparer(true); var differences = incomingResource.Translations.Except(existingResource.Translations, sut).ToList(); Assert.Empty(differences); }
public void CompareUsingInvariant_UsingExcept_NewLanguageDetected() { var incomingResource = new LocalizationResource("key", false); incomingResource.Translations.AddRange( new List <LocalizationResourceTranslation> { new LocalizationResourceTranslation { Language = "", Value = "incoming value" }, new LocalizationResourceTranslation { Language = "en", Value = "incoming EN value" } }); var existingResource = new LocalizationResource("key", false); existingResource.Translations.Add(new LocalizationResourceTranslation { Language = "", Value = "existing value" }); var sut = new TranslationComparer(true); var differences = incomingResource.Translations.Except(existingResource.Translations, sut).ToList(); Assert.NotEmpty(differences); }
public ICollection <DetectedImportChange> DetectChanges(ICollection <LocalizationResource> importingResources, IEnumerable <LocalizationResource> existingResources) { var result = new List <DetectedImportChange>(); // deleted deletes var resourceComparer = new ResourceComparer(); var deletes = existingResources.Except(importingResources, resourceComparer); result.AddRange(deletes.Select(d => new DetectedImportChange(ChangeType.Delete, LocalizationResource.CreateNonExisting(d.ResourceKey), d))); foreach (var incomingResource in importingResources.Except(deletes, resourceComparer)) { // clean up nulls from translations incomingResource.Translations = incomingResource.Translations.Where(t => t != null).ToList(); var existing = existingResources.FirstOrDefault(r => r.ResourceKey == incomingResource.ResourceKey); if (existing != null) { var comparer = new TranslationComparer(true); var existingTranslations = existing.Translations.Where(_ => _ != null).ToList(); var differences = incomingResource.Translations.Except(existingTranslations, comparer).Where(_ => _ != null).ToList(); // some of the translations are different - so marking this resource as potential update if (differences.Any()) { // here we need to check whether incoming resource is overriding existing translation (exists translation in given language) // or we are maybe importing exported language that had no translations // this could happen if you export language with no translations in xliff format // then new exported target language will have translation as empty string // these cases we need to filter out var detectedChangedLanguages = differences.Select(_ => _.Language).Distinct().ToList(); var existingLanguages = existingTranslations.Select(_ => _.Language).Distinct().ToList(); if (!differences.All(r => string.IsNullOrEmpty(r.Value)) || !detectedChangedLanguages.Except(existingLanguages).Any()) { result.Add(new DetectedImportChange(ChangeType.Update, incomingResource, existing) { ChangedLanguages = detectedChangedLanguages }); } } } else { result.Add(new DetectedImportChange(ChangeType.Insert, incomingResource, LocalizationResource.CreateNonExisting(incomingResource.ResourceKey)) { ChangedLanguages = incomingResource.Translations.Select(t => t.Language).ToList() }); } } return(result); }
public void TwoDifferentTranslations_InvariantCulture_NoChangesDetected() { var sut = new TranslationComparer(true); var result = sut.Equals(new LocalizationResourceTranslation { Language = "", Value = "Value 1" }, new LocalizationResourceTranslation { Language = "", Value = "Value 2" }); Assert.True(result); }
public IEnumerable <DetectedImportChange> DetectChanges(IEnumerable <LocalizationResource> importingResources, IEnumerable <LocalizationResource> existingResources) { var result = new List <DetectedImportChange>(); // deleted deletes var resourceComparer = new ResourceComparer(); var deletes = existingResources.Except(importingResources, resourceComparer); result.AddRange(deletes.Select(d => new DetectedImportChange(ChangeType.Delete, LocalizationResource.CreateNonExisting(d.ResourceKey), d))); foreach (var incomingResource in importingResources.Except(deletes, resourceComparer)) { var existing = existingResources.FirstOrDefault(r => r.ResourceKey == incomingResource.ResourceKey); if (existing != null) { var comparer = new TranslationComparer(); var differences = incomingResource.Translations.Except(existing.Translations, comparer) .ToList(); // some of the translations are different - so marking this resource as potential update if (differences.Any()) { result.Add(new DetectedImportChange(ChangeType.Update, incomingResource, existing) { ChangedLanguages = differences.Select(t => t.Language).Distinct().ToList() }); } } else { result.Add(new DetectedImportChange(ChangeType.Insert, incomingResource, LocalizationResource.CreateNonExisting(incomingResource.ResourceKey))); } } return(result); }
/// <summary> /// Converts translations to sql commands that'll insert and/or update translations to the database. /// /// To determine whether an update or translation is needed, translations already in the database are queried and compared against /// the translations passed into this method. /// /// A two finger algorithm is used against the sorted database stream to the sorted translation parameter for a time /// complexity of O(n+m). All statements are wrapped into a transaction. /// </summary> public string syncSqlCmds(SortedSet<Translation> translations) { var categoriesClause = categoriesSqlClause(translations.ToList()); //todo: change algorithm to work with HashSet.Enumerator StringBuilder buff = new StringBuilder(); using (var conn = new MySqlConnection(FlashCardAppDB.instance.connectionString)){ var cmd = new MySqlCommand( $"select italiano,category from translation where category in ({categoriesClause}) order by category,italiano", conn); conn.Open(); MySqlDataReader rdr = cmd.ExecuteReader(); var translateEnumerator = translations.GetEnumerator(); var comparer = new TranslationComparer(); var hasNext = translateEnumerator.MoveNext(); Func <string,string> printNull = x => (x != null) ? $"'{x}'" : "null"; Translation dbt; if (hasNext){ buff.AppendLine("start transaction;"); while (hasNext){ //read-only, forward-only stream dbt = (rdr.Read()) ? new Translation{ italiano = rdr.GetString("italiano"), category = rdr.GetString("category") } : null; if (dbt == null || comparer.Compare(translateEnumerator.Current, dbt) < 0){ //Less than or remaining parsed translations buff.AppendLine("insert into translation (italiano,category,espanol,priority) " + $"values ('{translateEnumerator.Current.italiano}','{translateEnumerator.Current.category}',{printNull(translateEnumerator.Current.espanol)},{translateEnumerator.Current.priority});"); hasNext = translateEnumerator.MoveNext(); }else if (comparer.Compare(translateEnumerator.Current, dbt) == 0){ //Equal buff.AppendLine($"update translation set espanol={printNull(translateEnumerator.Current.espanol)},priority={translateEnumerator.Current.priority} " + $"where italiano='{translateEnumerator.Current.italiano}' and category='{translateEnumerator.Current.category}';"); hasNext = translateEnumerator.MoveNext(); } //Greater than... skip } buff.AppendLine("commit;"); } return buff.ToString(); } }