using System.ComponentModel; using System.Globalization; using System.Windows.Data; using CursorLang.Core.Services; namespace CursorLang.Core.Tests.Services; public sealed class LocalizationServiceTests { [Fact] public void The_interface_starts_out_in_English() { Assert.Equal("en", new LocalizationService().CurrentLanguage); } [Fact] public void A_string_comes_from_the_resources_of_the_chosen_language() { var localization = new LocalizationService(); string english = localization["SettingsTitle"]; localization.CurrentLanguage = "ru"; string russian = localization["SettingsTitle"]; Assert.False(string.IsNullOrWhiteSpace(english)); Assert.False(string.IsNullOrWhiteSpace(russian)); Assert.NotEqual(english, russian); } // A missing key shows in the interface but does not bring the app down [Fact] public void An_unknown_key_comes_back_as_is() { var localization = new LocalizationService(); Assert.Equal("NoSuchKey", localization["NoSuchKey"]); } [Fact] public void A_language_change_is_announced_to_subscribers() { var localization = new LocalizationService(); List changed = []; localization.PropertyChanged += (_, e) => changed.Add(e.PropertyName); localization.CurrentLanguage = "ru"; Assert.Contains(nameof(LocalizationService.CurrentLanguage), changed); // The indexer is announced separately: that is how the whole text refreshes Assert.Contains(Binding.IndexerName, changed); } [Fact] public void The_same_language_is_not_announced_again() { var localization = new LocalizationService { CurrentLanguage = "ru" }; List changed = []; localization.PropertyChanged += (_, e) => changed.Add(e.PropertyName); localization.CurrentLanguage = "ru"; Assert.Empty(changed); } [Theory] [InlineData("")] [InlineData(" ")] [InlineData(null)] public void An_empty_language_changes_nothing(string? value) { var localization = new LocalizationService(); List changed = []; localization.PropertyChanged += (_, e) => changed.Add(e.PropertyName); localization.CurrentLanguage = value!; Assert.Equal("en", localization.CurrentLanguage); Assert.Empty(changed); } [Fact] public void A_language_with_a_country_falls_back_to_the_language_code() { var localization = new LocalizationService { CurrentLanguage = "ru-RU" }; Assert.Equal("ru", localization.CurrentLanguage); } // Changing the app language has to change the language of the thread as // well: other texts, down to system messages, depend on it [Fact] public void A_language_change_changes_the_language_of_the_thread() { CultureInfo previous = CultureInfo.CurrentUICulture; try { var localization = new LocalizationService { CurrentLanguage = "ru" }; Assert.Equal("ru", CultureInfo.CurrentUICulture.TwoLetterISOLanguageName); Assert.Equal("ru", localization.CurrentLanguage); } finally { CultureInfo.CurrentUICulture = previous; } } [Fact] public void English_and_Russian_are_offered_for_choosing() { IReadOnlyList languages = new LocalizationService().AvailableLanguages; Assert.Equal(2, languages.Count); Assert.Contains(languages, language => language.Code == "en"); Assert.Contains(languages, language => language.Code == "ru"); } // A language is named in itself: that way it is recognised even by someone // who does not know the current interface language [Fact] public void The_languages_are_named_in_themselves() { IReadOnlyList languages = new LocalizationService().AvailableLanguages; Assert.Equal("English", languages.Single(language => language.Code == "en").DisplayName); Assert.Equal("Русский", languages.Single(language => language.Code == "ru").DisplayName); } // Accessibility tools take the name of a list item from ToString [Fact] public void A_language_presents_itself_by_its_name() { Assert.Equal("Русский", new LanguageOption("ru", "Русский").ToString()); } [Fact] public void Languages_with_the_same_code_and_name_are_equal() { Assert.Equal(new LanguageOption("ru", "Русский"), new LanguageOption("ru", "Русский")); Assert.NotEqual(new LanguageOption("ru", "Русский"), new LanguageOption("en", "English")); } [Fact] public void The_service_reports_changes_as_INotifyPropertyChanged() { Assert.IsAssignableFrom(new LocalizationService()); } }