Set default language based on Windows language.
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
public interface IConfigurationService
|
public interface IConfigurationService
|
||||||
{
|
{
|
||||||
|
bool Contains(string key);
|
||||||
|
|
||||||
void Set(string key, object value);
|
void Set(string key, object value);
|
||||||
T Get<T>(string key, T defaultValue = default);
|
T Get<T>(string key, T defaultValue = default);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using FluentAssertions;
|
||||||
|
using Wino.Core.Domain.Enums;
|
||||||
|
using Wino.Services;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Wino.Core.Tests.Services;
|
||||||
|
|
||||||
|
public class TranslationServiceTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("pl-PL", AppLanguage.Polish)]
|
||||||
|
[InlineData("de-AT", AppLanguage.Deutsch)]
|
||||||
|
[InlineData("pt-PT", AppLanguage.PortugeseBrazil)]
|
||||||
|
[InlineData("zh-TW", AppLanguage.Chinese)]
|
||||||
|
[InlineData("tr_TR", AppLanguage.Turkish)]
|
||||||
|
[InlineData("nl-NL", AppLanguage.English)]
|
||||||
|
public void ResolveSupportedLanguage_ReturnsExpectedLanguage(string languageTag, AppLanguage expectedLanguage)
|
||||||
|
{
|
||||||
|
var result = TranslationService.ResolveSupportedLanguage([languageTag]);
|
||||||
|
|
||||||
|
result.Should().Be(expectedLanguage);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,9 @@ namespace Wino.Mail.WinUI.Services;
|
|||||||
|
|
||||||
public class ConfigurationService : IConfigurationService
|
public class ConfigurationService : IConfigurationService
|
||||||
{
|
{
|
||||||
|
public bool Contains(string key)
|
||||||
|
=> ApplicationData.Current.LocalSettings.Values.ContainsKey(key);
|
||||||
|
|
||||||
public T Get<T>(string key, T defaultValue = default!)
|
public T Get<T>(string key, T defaultValue = default!)
|
||||||
=> GetInternal(key, ApplicationData.Current.LocalSettings.Values, defaultValue);
|
=> GetInternal(key, ApplicationData.Current.LocalSettings.Values, defaultValue);
|
||||||
|
|
||||||
|
|||||||
@@ -147,9 +147,30 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
|
|||||||
|
|
||||||
public void Receive(SettingsRootNavigationRequested message)
|
public void Receive(SettingsRootNavigationRequested message)
|
||||||
{
|
{
|
||||||
var currentRootPage = SettingsNavigationInfoProvider.GetRootPage(PageHistory.LastOrDefault()?.Request.PageType ?? WinoPage.SettingOptionsPage);
|
var activePage = PageHistory.LastOrDefault()?.Request.PageType ?? WinoPage.SettingOptionsPage;
|
||||||
if (message.PageType != WinoPage.SettingOptionsPage && currentRootPage == message.PageType)
|
var currentRootPage = SettingsNavigationInfoProvider.GetRootPage(activePage);
|
||||||
|
|
||||||
|
if (message.PageType == currentRootPage)
|
||||||
|
{
|
||||||
|
if (activePage == currentRootPage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var currentRootIndex = PageHistory
|
||||||
|
.Select((item, index) => new { item.Request.PageType, Index = index })
|
||||||
|
.FirstOrDefault(item => item.PageType == currentRootPage)?.Index ?? -1;
|
||||||
|
|
||||||
|
if (TryNavigateToBreadcrumbIndex(currentRootIndex))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.PageType == WinoPage.SettingOptionsPage)
|
||||||
|
{
|
||||||
|
if (activePage == WinoPage.SettingOptionsPage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
NavigateToSettingsHome();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
NavigateToRootPage(message.PageType);
|
NavigateToRootPage(message.PageType);
|
||||||
}
|
}
|
||||||
@@ -196,11 +217,7 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
|
|||||||
|
|
||||||
private void NavigateToRootPage(WinoPage targetPage)
|
private void NavigateToRootPage(WinoPage targetPage)
|
||||||
{
|
{
|
||||||
PageHistory.Clear();
|
NavigateToSettingsHome();
|
||||||
SettingsFrame.BackStack.Clear();
|
|
||||||
SettingsFrame.ForwardStack.Clear();
|
|
||||||
|
|
||||||
NavigateBreadcrumb(new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage));
|
|
||||||
|
|
||||||
if (targetPage != WinoPage.SettingOptionsPage)
|
if (targetPage != WinoPage.SettingOptionsPage)
|
||||||
{
|
{
|
||||||
@@ -213,6 +230,43 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
|
|||||||
UpdateWindowTitle();
|
UpdateWindowTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void NavigateToSettingsHome()
|
||||||
|
{
|
||||||
|
if (PageHistory.Count == 0 || SettingsFrame.Content == null)
|
||||||
|
{
|
||||||
|
ResetToSettingsHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PageHistory.Count == 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryNavigateToBreadcrumbIndex(0))
|
||||||
|
{
|
||||||
|
ResetToSettingsHome();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryNavigateToBreadcrumbIndex(int targetIndex)
|
||||||
|
{
|
||||||
|
if (!BreadcrumbNavigationHelper.NavigateTo(SettingsFrame, PageHistory, targetIndex))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
UpdateBackNavigationState();
|
||||||
|
_ = RefreshCurrentPageStateAsync();
|
||||||
|
UpdateWindowTitle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetToSettingsHome()
|
||||||
|
{
|
||||||
|
PageHistory.Clear();
|
||||||
|
SettingsFrame.BackStack.Clear();
|
||||||
|
SettingsFrame.ForwardStack.Clear();
|
||||||
|
|
||||||
|
NavigateBreadcrumb(new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage));
|
||||||
|
}
|
||||||
|
|
||||||
public void ResetForModeSwitch()
|
public void ResetForModeSwitch()
|
||||||
{
|
{
|
||||||
while (PageHistory.Count > 1 && SettingsFrame.CanGoBack)
|
while (PageHistory.Count > 1 && SettingsFrame.CanGoBack)
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Wino.Core.Tests")]
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@@ -19,17 +20,19 @@ public class TranslationService : ITranslationService
|
|||||||
|
|
||||||
private ILogger _logger = Log.ForContext<TranslationService>();
|
private ILogger _logger = Log.ForContext<TranslationService>();
|
||||||
private readonly IPreferencesService _preferencesService;
|
private readonly IPreferencesService _preferencesService;
|
||||||
|
private readonly IConfigurationService _configurationService;
|
||||||
private bool isInitialized = false;
|
private bool isInitialized = false;
|
||||||
|
|
||||||
public AppLanguageModel CurrentLanguageModel { get; private set; }
|
public AppLanguageModel CurrentLanguageModel { get; private set; }
|
||||||
|
|
||||||
public TranslationService(IPreferencesService preferencesService)
|
public TranslationService(IPreferencesService preferencesService, IConfigurationService configurationService)
|
||||||
{
|
{
|
||||||
_preferencesService = preferencesService;
|
_preferencesService = preferencesService;
|
||||||
|
_configurationService = configurationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize default language with ignoring current language check.
|
// Initialize default language with ignoring current language check.
|
||||||
public Task InitializeAsync() => InitializeLanguageAsync(_preferencesService.CurrentLanguage, ignoreCurrentLanguageCheck: true);
|
public Task InitializeAsync() => InitializeLanguageAsync(GetInitialLanguage(), ignoreCurrentLanguageCheck: true);
|
||||||
|
|
||||||
public async Task InitializeLanguageAsync(AppLanguage language, bool ignoreCurrentLanguageCheck = false)
|
public async Task InitializeLanguageAsync(AppLanguage language, bool ignoreCurrentLanguageCheck = false)
|
||||||
{
|
{
|
||||||
@@ -65,6 +68,66 @@ public class TranslationService : ITranslationService
|
|||||||
WeakReferenceMessenger.Default.Send(new LanguageChanged());
|
WeakReferenceMessenger.Default.Send(new LanguageChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AppLanguage GetInitialLanguage()
|
||||||
|
{
|
||||||
|
if (_configurationService.Contains(nameof(IPreferencesService.CurrentLanguage)))
|
||||||
|
return _preferencesService.CurrentLanguage;
|
||||||
|
|
||||||
|
var windowsDisplayLanguage = CultureInfo.CurrentUICulture?.Name;
|
||||||
|
var initialLanguage = ResolveSupportedLanguage(
|
||||||
|
[
|
||||||
|
windowsDisplayLanguage ?? string.Empty,
|
||||||
|
CultureInfo.CurrentUICulture?.TwoLetterISOLanguageName ?? string.Empty
|
||||||
|
]);
|
||||||
|
|
||||||
|
_logger.Information("No saved app language preference found. Using Windows display language {LanguageTag} -> {Language}.",
|
||||||
|
string.IsNullOrWhiteSpace(windowsDisplayLanguage) ? "<unknown>" : windowsDisplayLanguage,
|
||||||
|
initialLanguage);
|
||||||
|
|
||||||
|
return initialLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static AppLanguage ResolveSupportedLanguage(IEnumerable<string> languageTags)
|
||||||
|
{
|
||||||
|
foreach (var languageTag in languageTags)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(languageTag))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var normalizedLanguageTag = languageTag.Replace('_', '-').Trim();
|
||||||
|
var languageCode = normalizedLanguageTag.Split('-')[0];
|
||||||
|
|
||||||
|
if (TryResolveSupportedLanguage(languageCode, out var supportedLanguage))
|
||||||
|
return supportedLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultAppLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryResolveSupportedLanguage(string languageCode, out AppLanguage supportedLanguage)
|
||||||
|
{
|
||||||
|
supportedLanguage = languageCode.ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"cs" => AppLanguage.Czech,
|
||||||
|
"de" => AppLanguage.Deutsch,
|
||||||
|
"el" => AppLanguage.Greek,
|
||||||
|
"en" => AppLanguage.English,
|
||||||
|
"es" => AppLanguage.Spanish,
|
||||||
|
"fr" => AppLanguage.French,
|
||||||
|
"id" => AppLanguage.Indonesian,
|
||||||
|
"it" => AppLanguage.Italian,
|
||||||
|
"pl" => AppLanguage.Polish,
|
||||||
|
"pt" => AppLanguage.PortugeseBrazil,
|
||||||
|
"ro" => AppLanguage.Romanian,
|
||||||
|
"ru" => AppLanguage.Russian,
|
||||||
|
"tr" => AppLanguage.Turkish,
|
||||||
|
"zh" => AppLanguage.Chinese,
|
||||||
|
_ => AppLanguage.None
|
||||||
|
};
|
||||||
|
|
||||||
|
return supportedLanguage != AppLanguage.None;
|
||||||
|
}
|
||||||
|
|
||||||
public List<AppLanguageModel> GetAvailableLanguages()
|
public List<AppLanguageModel> GetAvailableLanguages()
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user