using System.Globalization; using System.Resources; using CommunityToolkit.Mvvm.ComponentModel; namespace CursorLang.Core.Services; /// /// Takes the strings from the resources and, when the language changes, asks WPF to /// re-read every binding. /// /// /// Both processes use it: the settings window for its whole interface, the agent for /// the three captions of the tray menu. The theme no longer reaches that menu — Windows /// draws it — but the language still does. /// public sealed class LocalizationService : ObservableObject, ILocalizationService { /// /// The name WPF reports when an indexer changes. Spelt out rather than taken from /// Binding.IndexerName: that constant lives in PresentationFramework, and /// Core is read by the agent, which does not load WPF. /// public const string IndexerName = "Item[]"; private static readonly ResourceManager Resources = new("CursorLang.Core.Resources.Strings", typeof(LocalizationService).Assembly); private CultureInfo _culture = CultureInfo.GetCultureInfo("en"); public string this[string key] => Resources.GetString(key, _culture) ?? key; public IReadOnlyList AvailableLanguages { get; } = [ new LanguageOption("en", "English"), new LanguageOption("ru", "Русский"), ]; public string CurrentLanguage { get => _culture.TwoLetterISOLanguageName; set { if (string.IsNullOrWhiteSpace(value) || value == CurrentLanguage) { return; } _culture = CultureInfo.GetCultureInfo(value); CultureInfo.CurrentUICulture = _culture; OnPropertyChanged(nameof(CurrentLanguage)); // We report a change of the indexer: that is how every binding of the // {Binding Localization[Key]} kind updates, that is, all the interface text OnPropertyChanged(IndexerName); } } }