From ccf75bd45f38ccedab1451991f7caf19a822ed89 Mon Sep 17 00:00:00 2001 From: Felix <30559812+felixse@users.noreply.github.com> Date: Sat, 11 May 2019 16:50:20 +0200 Subject: [PATCH] Make language selectable, extend to new ssh feature (#362) --- .../SettingsServiceTests.cs | 4 - .../IApplicationLanguageService.cs | 13 + .../ISshConnectionInfo.cs | 6 +- .../Implementation/DefaultValueProvider.cs | 17 +- .../TrayProcessCommunicationService.cs | 4 + FluentTerminal.App.Services/Terminal.cs | 5 +- FluentTerminal.App.Services/Utilities/I18N.cs | 17 + .../Utilities/StringsHelper.cs | 20 - .../MainViewModel.cs | 6 +- .../Settings/GeneralPageViewModel.cs | 32 +- .../Settings/KeyBindingViewModel.cs | 2 +- .../Settings/KeyBindingsPageViewModel.cs | 4 +- .../Settings/MousePageViewModel.cs | 2 +- .../Settings/TerminalPageViewModel.cs | 2 +- .../Settings/ThemesPageViewModel.cs | 4 +- .../SettingsViewModel.cs | 4 +- .../ShellProfileViewModel.cs | 30 +- .../SshConnectionInfoViewModel.cs | 30 +- .../TerminalViewModel.cs | 6 +- .../ThemeViewModel.cs | 4 +- FluentTerminal.App/App.xaml | 2 +- FluentTerminal.App/App.xaml.cs | 42 +- .../Converters/EnumToDescriptionConverter.cs | 24 -- .../Converters/I18NConverter.cs | 27 ++ .../Converters/LineEndingStyleConverter.cs | 48 --- .../Dialogs/CreateKeyBindingDialog.xaml.cs | 4 +- .../Dialogs/InputDialog.xaml.cs | 4 +- .../Dialogs/ShellProfileSelectionDialog.xaml | 1 - FluentTerminal.App/Dialogs/SshInfoDialog.xaml | 38 +- .../Dialogs/SshInfoDialog.xaml.cs | 37 +- FluentTerminal.App/FluentTerminal.App.csproj | 4 +- .../Services/ApplicationLanguageService.cs | 34 ++ .../Services/SshHelperService.cs | 60 ++- FluentTerminal.App/Strings/en/Resources.resw | 361 +++++++++--------- .../Strings/zh-CN/Resources.resw | 361 +++++++++--------- FluentTerminal.App/Views/MainPage.xaml | 17 +- FluentTerminal.App/Views/SettingsPage.xaml.cs | 4 +- .../Views/SettingsPages/GeneralSettings.xaml | 8 + .../SettingsPages/KeyBindingSettings.xaml.cs | 2 +- .../SettingsPages/ShellProfileSettings.xaml | 14 +- FluentTerminal.App/Views/TabBar.xaml | 14 +- .../Views/XtermTerminalView.xaml.cs | 4 +- FluentTerminal.Models/Enums/Command.cs | 57 +-- .../Enums/LineEndingStyle.cs | 10 +- .../SshConnectionInfoValidationResult.cs | 12 + .../Services/TerminalsManager.cs | 18 +- FluentTerminal.SystemTray/Utilities.cs | 8 + UpdateTranslations.ps1 | 1 - 48 files changed, 755 insertions(+), 673 deletions(-) create mode 100644 FluentTerminal.App.Services/IApplicationLanguageService.cs create mode 100644 FluentTerminal.App.Services/Utilities/I18N.cs delete mode 100644 FluentTerminal.App.Services/Utilities/StringsHelper.cs delete mode 100644 FluentTerminal.App/Converters/EnumToDescriptionConverter.cs create mode 100644 FluentTerminal.App/Converters/I18NConverter.cs delete mode 100644 FluentTerminal.App/Converters/LineEndingStyleConverter.cs create mode 100644 FluentTerminal.App/Services/ApplicationLanguageService.cs create mode 100644 FluentTerminal.Models/Enums/SshConnectionInfoValidationResult.cs diff --git a/FluentTerminal.App.Services.Test/SettingsServiceTests.cs b/FluentTerminal.App.Services.Test/SettingsServiceTests.cs index 009dbda..278489e 100644 --- a/FluentTerminal.App.Services.Test/SettingsServiceTests.cs +++ b/FluentTerminal.App.Services.Test/SettingsServiceTests.cs @@ -471,10 +471,6 @@ namespace FluentTerminal.App.Services.Test var commands = _fixture.CreateMany(5); var defaultValueProvider = new Mock(); var keyBindingsContainer = new Mock(); - foreach (var command in commands) - { - keyBindingsContainer.Setup(x => x.ReadValueFromJson>(EnumHelper.GetEnumDescription(command), null)).Returns(new Collection { }); - } var applicationDataContainers = new ApplicationDataContainers { KeyBindings = keyBindingsContainer.Object, diff --git a/FluentTerminal.App.Services/IApplicationLanguageService.cs b/FluentTerminal.App.Services/IApplicationLanguageService.cs new file mode 100644 index 0000000..e3e5aec --- /dev/null +++ b/FluentTerminal.App.Services/IApplicationLanguageService.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace FluentTerminal.App.Services +{ + public interface IApplicationLanguageService + { + IEnumerable Languages { get; } + + void SetLanguage(string language); + + string GetCurrentLanguage(); + } +} diff --git a/FluentTerminal.App.Services/ISshConnectionInfo.cs b/FluentTerminal.App.Services/ISshConnectionInfo.cs index 7edd078..6a03a88 100644 --- a/FluentTerminal.App.Services/ISshConnectionInfo.cs +++ b/FluentTerminal.App.Services/ISshConnectionInfo.cs @@ -1,4 +1,6 @@ -namespace FluentTerminal.App.Services +using FluentTerminal.Models.Enums; + +namespace FluentTerminal.App.Services { public interface ISshConnectionInfo { @@ -16,6 +18,6 @@ ushort MoshPortTo { get; set; } - string Validate(bool allowNoUser = false); + SshConnectionInfoValidationResult Validate(bool allowNoUser = false); } } diff --git a/FluentTerminal.App.Services/Implementation/DefaultValueProvider.cs b/FluentTerminal.App.Services/Implementation/DefaultValueProvider.cs index af79c1e..01e643f 100644 --- a/FluentTerminal.App.Services/Implementation/DefaultValueProvider.cs +++ b/FluentTerminal.App.Services/Implementation/DefaultValueProvider.cs @@ -1,4 +1,5 @@ -using FluentTerminal.Models; +using FluentTerminal.App.Services.Utilities; +using FluentTerminal.Models; using FluentTerminal.Models.Enums; using System; using System.Collections.Generic; @@ -284,7 +285,7 @@ namespace FluentTerminal.App.Services.Implementation new TabTheme { Id = 0, - Name = "None", + Name = I18N.Translate("TabTheme.None"), BackgroundOpacity = double.NaN, BackgroundPointerOverOpacity = double.NaN, BackgroundPressedOpacity = double.NaN, @@ -295,37 +296,37 @@ namespace FluentTerminal.App.Services.Implementation new TabTheme { Id = 1, - Name = "Red", + Name = I18N.Translate("TabTheme.Red"), Color = "#E81123" }, new TabTheme { Id = 2, - Name = "Green", + Name = I18N.Translate("TabTheme.Green"), Color = "#10893E" }, new TabTheme { Id = 3, - Name = "Blue", + Name = I18N.Translate("TabTheme.Blue"), Color = "#0078D7" }, new TabTheme { Id = 4, - Name = "Purple", + Name = I18N.Translate("TabTheme.Purple"), Color = "#881798" }, new TabTheme { Id = 5, - Name = "Orange", + Name = I18N.Translate("TabTheme.Orange"), Color = "#FF8C00" }, new TabTheme { Id = 6, - Name = "Teal", + Name = I18N.Translate("TabTheme.Teal"), Color = "#00B7C3" } }; diff --git a/FluentTerminal.App.Services/Implementation/TrayProcessCommunicationService.cs b/FluentTerminal.App.Services/Implementation/TrayProcessCommunicationService.cs index 69c3ee2..f8866d8 100644 --- a/FluentTerminal.App.Services/Implementation/TrayProcessCommunicationService.cs +++ b/FluentTerminal.App.Services/Implementation/TrayProcessCommunicationService.cs @@ -46,8 +46,10 @@ namespace FluentTerminal.App.Services.Implementation public async Task GetUserName() { if (!string.IsNullOrEmpty(_userName)) + { // Returning the username from cache return _userName; + } GetUserNameResponse response; @@ -81,7 +83,9 @@ namespace FluentTerminal.App.Services.Implementation JsonConvert.DeserializeObject(responseMessage[MessageKeys.Content]); if (!response.Success) + { throw new Exception(string.IsNullOrEmpty(response.Error) ? "Failed to save the file." : response.Error); + } } public async Task CreateTerminal(int id, TerminalSize size, ShellProfile shellProfile, SessionType sessionType) diff --git a/FluentTerminal.App.Services/Terminal.cs b/FluentTerminal.App.Services/Terminal.cs index bdd798e..71657c3 100644 --- a/FluentTerminal.App.Services/Terminal.cs +++ b/FluentTerminal.App.Services/Terminal.cs @@ -22,7 +22,10 @@ namespace FluentTerminal.App.Services private void OnTerminalExited(object sender, TerminalExitStatus status) { - if (status.TerminalId != Id) return; + if (status.TerminalId != Id) + { + return; + } _exited = true; Exited?.Invoke(this, status.ExitCode); diff --git a/FluentTerminal.App.Services/Utilities/I18N.cs b/FluentTerminal.App.Services/Utilities/I18N.cs new file mode 100644 index 0000000..8546f63 --- /dev/null +++ b/FluentTerminal.App.Services/Utilities/I18N.cs @@ -0,0 +1,17 @@ +using Windows.ApplicationModel.Resources; + +namespace FluentTerminal.App.Services.Utilities +{ + /// + /// Get strings in different languages. + /// Author: Zuozishi + /// + public static class I18N + { + public static string Translate(string resource) + { + var resourceLoader = ResourceLoader.GetForCurrentView(); + return resourceLoader.GetString(resource.Replace('.', '/')); + } + } +} diff --git a/FluentTerminal.App.Services/Utilities/StringsHelper.cs b/FluentTerminal.App.Services/Utilities/StringsHelper.cs deleted file mode 100644 index c9b45aa..0000000 --- a/FluentTerminal.App.Services/Utilities/StringsHelper.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Windows.ApplicationModel.Resources; - -namespace FluentTerminal.App.Services.Utilities -{ - /// - /// Get strings in different languages. - /// Author: Zuozishi - /// - public static class StringsHelper - { - public static string GetString(string resource) - { - var resourceLoader = ResourceLoader.GetForCurrentView(); - return resourceLoader.GetString(resource); - } - } -} diff --git a/FluentTerminal.App.ViewModels/MainViewModel.cs b/FluentTerminal.App.ViewModels/MainViewModel.cs index e290727..ce6de8e 100644 --- a/FluentTerminal.App.ViewModels/MainViewModel.cs +++ b/FluentTerminal.App.ViewModels/MainViewModel.cs @@ -236,10 +236,14 @@ namespace FluentTerminal.App.ViewModels { // User selected "Cancel" if (Terminals.Count == 0) + { await ApplicationView.TryClose(); + } } else + { await ApplicationView.RunOnDispatcherThread(() => AddTerminal(profile)); + } } public void AddTerminal() @@ -346,7 +350,7 @@ namespace FluentTerminal.App.ViewModels { if (_applicationSettings.ConfirmClosingWindows) { - var result = await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmCloseWindow"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(false); + var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmCloseWindow"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(false); if (result == DialogButton.OK) { diff --git a/FluentTerminal.App.ViewModels/Settings/GeneralPageViewModel.cs b/FluentTerminal.App.ViewModels/Settings/GeneralPageViewModel.cs index 01b7020..42b75f3 100644 --- a/FluentTerminal.App.ViewModels/Settings/GeneralPageViewModel.cs +++ b/FluentTerminal.App.ViewModels/Settings/GeneralPageViewModel.cs @@ -1,9 +1,11 @@ using FluentTerminal.App.Services; using FluentTerminal.App.Services.Utilities; +using FluentTerminal.App.ViewModels.Infrastructure; using FluentTerminal.Models; using FluentTerminal.Models.Enums; using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; +using System.Collections.Generic; using System.Threading.Tasks; namespace FluentTerminal.App.ViewModels.Settings @@ -22,19 +24,41 @@ namespace FluentTerminal.App.ViewModels.Settings private bool _startupTaskEnabled; private bool _shouldRestartForTrayMessage; private string _startupTaskErrorMessage; + private bool _needsToRestart; + private readonly IApplicationLanguageService _applicationLanguageService; - public GeneralPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, IStartupTaskService startupTaskService) + public GeneralPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, + IStartupTaskService startupTaskService, IApplicationLanguageService applicationLanguageService) { _settingsService = settingsService; _dialogService = dialogService; _defaultValueProvider = defaultValueProvider; _startupTaskService = startupTaskService; + _applicationLanguageService = applicationLanguageService; _applicationSettings = _settingsService.GetApplicationSettings(); RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false)); } + public IEnumerable Languages => _applicationLanguageService.Languages; + + public bool NeedsToRestart + { + get => _needsToRestart; + set => Set(ref _needsToRestart, value); + } + + public string SelectedLanguage + { + get => _applicationLanguageService.GetCurrentLanguage(); + set + { + _applicationLanguageService.SetLanguage(value); + NeedsToRestart = true; + } + } + public async Task OnNavigatedTo() { var startupTaskStatus = await _startupTaskService.GetStatus(); @@ -282,7 +306,7 @@ namespace FluentTerminal.App.ViewModels.Settings private async Task RestoreDefaults() { - var result = await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmRestoreGeneralSettings"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); + var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmRestoreGeneralSettings"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); if (result == DialogButton.OK) { @@ -318,13 +342,13 @@ namespace FluentTerminal.App.ViewModels.Settings case StartupTaskStatus.DisabledByUser: StartupTaskEnabled = false; - StartupTaskErrorMessage = StringsHelper.GetString("DisabledByUser"); + StartupTaskErrorMessage = I18N.Translate("DisabledByUser"); CanEnableStartupTask = false; break; case StartupTaskStatus.DisabledByPolicy: StartupTaskEnabled = false; - StartupTaskErrorMessage = StringsHelper.GetString("DisabledByPolicy"); + StartupTaskErrorMessage = I18N.Translate("DisabledByPolicy"); CanEnableStartupTask = false; break; } diff --git a/FluentTerminal.App.ViewModels/Settings/KeyBindingViewModel.cs b/FluentTerminal.App.ViewModels/Settings/KeyBindingViewModel.cs index 7c4ad92..13cadad 100644 --- a/FluentTerminal.App.ViewModels/Settings/KeyBindingViewModel.cs +++ b/FluentTerminal.App.ViewModels/Settings/KeyBindingViewModel.cs @@ -119,7 +119,7 @@ namespace FluentTerminal.App.ViewModels.Settings private async Task Delete() { - var result = await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmDeleteKeybindings"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); + var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmDeleteKeybindings"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); if (result == DialogButton.OK) { diff --git a/FluentTerminal.App.ViewModels/Settings/KeyBindingsPageViewModel.cs b/FluentTerminal.App.ViewModels/Settings/KeyBindingsPageViewModel.cs index ee6c847..ec06f73 100644 --- a/FluentTerminal.App.ViewModels/Settings/KeyBindingsPageViewModel.cs +++ b/FluentTerminal.App.ViewModels/Settings/KeyBindingsPageViewModel.cs @@ -61,7 +61,7 @@ namespace FluentTerminal.App.ViewModels.Settings foreach (var value in Enum.GetValues(typeof(Command))) { var command = (Command)value; - var viewModel = new KeyBindingsViewModel(command.ToString(), _dialogService, StringsHelper.GetString(EnumHelper.GetEnumDescription(command)), true); + var viewModel = new KeyBindingsViewModel(command.ToString(), _dialogService, I18N.Translate($"{nameof(Command)}.{command}"), true); foreach (var keyBinding in _keyBindings[command.ToString()]) { viewModel.Add(keyBinding); @@ -79,7 +79,7 @@ namespace FluentTerminal.App.ViewModels.Settings private async Task RestoreDefaults() { - var result = await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmRestoreKeybindings"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); + var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmRestoreKeybindings"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); if (result == DialogButton.OK) { diff --git a/FluentTerminal.App.ViewModels/Settings/MousePageViewModel.cs b/FluentTerminal.App.ViewModels/Settings/MousePageViewModel.cs index 5337ffe..6b33e37 100644 --- a/FluentTerminal.App.ViewModels/Settings/MousePageViewModel.cs +++ b/FluentTerminal.App.ViewModels/Settings/MousePageViewModel.cs @@ -122,7 +122,7 @@ namespace FluentTerminal.App.ViewModels.Settings private async Task RestoreDefaults() { - var result = await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmRestoreMouseSettings"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); + var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmRestoreMouseSettings"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); if (result == DialogButton.OK) { diff --git a/FluentTerminal.App.ViewModels/Settings/TerminalPageViewModel.cs b/FluentTerminal.App.ViewModels/Settings/TerminalPageViewModel.cs index 8f56c18..7fefb28 100644 --- a/FluentTerminal.App.ViewModels/Settings/TerminalPageViewModel.cs +++ b/FluentTerminal.App.ViewModels/Settings/TerminalPageViewModel.cs @@ -225,7 +225,7 @@ namespace FluentTerminal.App.ViewModels.Settings private async Task RestoreDefaults() { - var result = await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmRestoreTerminalOptions"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); + var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmRestoreTerminalOptions"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); if (result == DialogButton.OK) { diff --git a/FluentTerminal.App.ViewModels/Settings/ThemesPageViewModel.cs b/FluentTerminal.App.ViewModels/Settings/ThemesPageViewModel.cs index c9fe127..85cc872 100644 --- a/FluentTerminal.App.ViewModels/Settings/ThemesPageViewModel.cs +++ b/FluentTerminal.App.ViewModels/Settings/ThemesPageViewModel.cs @@ -119,7 +119,7 @@ namespace FluentTerminal.App.ViewModels.Settings if (parser == null) { - await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("ImportThemeFailed"), StringsHelper.GetString("NoSuitableParserFound"), DialogButton.OK).ConfigureAwait(false); + await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("ImportThemeFailed"), I18N.Translate("NoSuitableParserFound"), DialogButton.OK).ConfigureAwait(false); return; } @@ -131,7 +131,7 @@ namespace FluentTerminal.App.ViewModels.Settings } catch (Exception exception) { - await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("ImportThemeFailed"), exception.Message, DialogButton.OK).ConfigureAwait(false); + await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("ImportThemeFailed"), exception.Message, DialogButton.OK).ConfigureAwait(false); } } } diff --git a/FluentTerminal.App.ViewModels/SettingsViewModel.cs b/FluentTerminal.App.ViewModels/SettingsViewModel.cs index d6dcf71..9352566 100644 --- a/FluentTerminal.App.ViewModels/SettingsViewModel.cs +++ b/FluentTerminal.App.ViewModels/SettingsViewModel.cs @@ -9,11 +9,11 @@ namespace FluentTerminal.App.ViewModels { public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider, IDialogService dialogService, ITrayProcessCommunicationService trayProcessCommunicationService, IThemeParserFactory themeParserFactory, ISystemFontService systemFontService, - IFileSystemService fileSystemService, IStartupTaskService startupTaskService, IUpdateService updateService, IApplicationView applicationView) + IFileSystemService fileSystemService, IStartupTaskService startupTaskService, IUpdateService updateService, IApplicationView applicationView, IApplicationLanguageService applicationLanguageService) { About = new AboutPageViewModel(settingsService, updateService, applicationView); KeyBindings = new KeyBindingsPageViewModel(settingsService, dialogService, defaultValueProvider, trayProcessCommunicationService); - General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider, startupTaskService); + General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider, startupTaskService, applicationLanguageService); Shell = new ProfilesPageViewModel(settingsService, dialogService, defaultValueProvider, fileSystemService, applicationView); Terminal = new TerminalPageViewModel(settingsService, dialogService, defaultValueProvider, systemFontService); Themes = new ThemesPageViewModel(settingsService, dialogService, defaultValueProvider, themeParserFactory, fileSystemService); diff --git a/FluentTerminal.App.ViewModels/ShellProfileViewModel.cs b/FluentTerminal.App.ViewModels/ShellProfileViewModel.cs index 710101f..5475ea3 100644 --- a/FluentTerminal.App.ViewModels/ShellProfileViewModel.cs +++ b/FluentTerminal.App.ViewModels/ShellProfileViewModel.cs @@ -206,25 +206,41 @@ namespace FluentTerminal.App.ViewModels public bool DoNotModifyIsSelected { get => LineEndingStyle == LineEndingStyle.DoNotModify; - set { if (value) LineEndingStyle = LineEndingStyle.DoNotModify; } + set { if (value) + { + LineEndingStyle = LineEndingStyle.DoNotModify; + } + } } public bool ToCRLFIsSelected { get => LineEndingStyle == LineEndingStyle.ToCRLF; - set { if (value) LineEndingStyle = LineEndingStyle.ToCRLF; } + set { if (value) + { + LineEndingStyle = LineEndingStyle.ToCRLF; + } + } } public bool ToLFIsSelected { get => LineEndingStyle == LineEndingStyle.ToLF; - set { if (value) LineEndingStyle = LineEndingStyle.ToLF; } + set { if (value) + { + LineEndingStyle = LineEndingStyle.ToLF; + } + } } public bool ToCRIsSelected { get => LineEndingStyle == LineEndingStyle.ToCR; - set { if (value) LineEndingStyle = LineEndingStyle.ToCR; } + set { if (value) + { + LineEndingStyle = LineEndingStyle.ToCR; + } + } } public void SaveChanges() @@ -251,7 +267,7 @@ namespace FluentTerminal.App.ViewModels throw new InvalidOperationException(); } - var result = await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmRestoreProfile"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); + var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmRestoreProfile"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); if (result == DialogButton.OK) { @@ -306,7 +322,7 @@ namespace FluentTerminal.App.ViewModels if (!_fallbackProfile.Equals(changedProfile)) { - var result = await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmDiscardChanges"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); + var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmDiscardChanges"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); if (result == DialogButton.OK) { @@ -343,7 +359,7 @@ namespace FluentTerminal.App.ViewModels private async Task Delete() { - var result = await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmDeleteTheme"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); + var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmDeleteTheme"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); if (result == DialogButton.OK) { diff --git a/FluentTerminal.App.ViewModels/SshConnectionInfoViewModel.cs b/FluentTerminal.App.ViewModels/SshConnectionInfoViewModel.cs index 98f1373..d20b965 100644 --- a/FluentTerminal.App.ViewModels/SshConnectionInfoViewModel.cs +++ b/FluentTerminal.App.ViewModels/SshConnectionInfoViewModel.cs @@ -78,27 +78,39 @@ namespace FluentTerminal.App.ViewModels public ObservableCollection SshOptions { get; } = new ObservableCollection(); - public string Validate(bool allowNoUser = false) + public SshConnectionInfoValidationResult Validate(bool allowNoUser = false) { if (!allowNoUser && string.IsNullOrEmpty(_username)) - return "Username cannot be empty."; + { + return SshConnectionInfoValidationResult.UsernameEmpty; + } if (string.IsNullOrEmpty(_host)) - return "Host cannot be empty."; + { + return SshConnectionInfoValidationResult.HostEmpty; + } if (_sshPort < 1) - return "SSH port has to be greater than zero."; + { + return SshConnectionInfoValidationResult.SshPortZeroOrNegative; + } if (!_useMosh) - return null; + { + return SshConnectionInfoValidationResult.Valid; + } if (_moshPortFrom < 1) - return "Mosh port cannot be zero."; + { + return SshConnectionInfoValidationResult.MoshPortZeroOrNegative; + } if (_moshPortFrom > _moshPortTo) - return "Mosh port range is invalid."; + { + return SshConnectionInfoValidationResult.MoshPortRangeInvalid; + } - return null; + return SshConnectionInfoValidationResult.Valid; } public SshConnectionInfoViewModel Clone() @@ -111,7 +123,9 @@ namespace FluentTerminal.App.ViewModels }; foreach (SshOptionViewModel option in SshOptions) + { clone.SshOptions.Add(option); + } return clone; } diff --git a/FluentTerminal.App.ViewModels/TerminalViewModel.cs b/FluentTerminal.App.ViewModels/TerminalViewModel.cs index 2982d66..e5c29c7 100644 --- a/FluentTerminal.App.ViewModels/TerminalViewModel.cs +++ b/FluentTerminal.App.ViewModels/TerminalViewModel.cs @@ -226,7 +226,7 @@ namespace FluentTerminal.App.ViewModels public async Task EditTitle() { - var result = await DialogService.ShowInputDialogAsync(StringsHelper.GetString("EditTitleString")); + var result = await DialogService.ShowInputDialogAsync(I18N.Translate("EditTitleString")); if (result != null) { if (string.IsNullOrWhiteSpace(result)) @@ -324,7 +324,7 @@ namespace FluentTerminal.App.ViewModels ClipboardService.SetText(selection); if(_terminalOptions.ShowTextCopied) { - Overlay.Show(StringsHelper.GetString("TextCopied")); + Overlay.Show(I18N.Translate("TextCopied")); } break; } @@ -388,7 +388,7 @@ namespace FluentTerminal.App.ViewModels { if (ApplicationSettings.ConfirmClosingTabs) { - var result = await DialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmCloseTab"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); + var result = await DialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmCloseTab"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); if (result == DialogButton.Cancel) { diff --git a/FluentTerminal.App.ViewModels/ThemeViewModel.cs b/FluentTerminal.App.ViewModels/ThemeViewModel.cs index 547524a..d8054ed 100644 --- a/FluentTerminal.App.ViewModels/ThemeViewModel.cs +++ b/FluentTerminal.App.ViewModels/ThemeViewModel.cs @@ -344,7 +344,7 @@ namespace FluentTerminal.App.ViewModels if (!_fallbackTheme.Equals(changedTheme)) { - var result = await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmDiscardChanges"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); + var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmDiscardChanges"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); if (result == DialogButton.OK) { @@ -392,7 +392,7 @@ namespace FluentTerminal.App.ViewModels private async Task Delete() { - var result = await _dialogService.ShowMessageDialogAsnyc(StringsHelper.GetString("PleaseConfirm"), StringsHelper.GetString("ConfirmDeleteTheme"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); + var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmDeleteTheme"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true); if (result == DialogButton.OK) { diff --git a/FluentTerminal.App/App.xaml b/FluentTerminal.App/App.xaml index 45e8bf1..661d758 100644 --- a/FluentTerminal.App/App.xaml +++ b/FluentTerminal.App/App.xaml @@ -37,8 +37,8 @@ - + \ No newline at end of file diff --git a/FluentTerminal.App/App.xaml.cs b/FluentTerminal.App/App.xaml.cs index 9c02599..df750ff 100644 --- a/FluentTerminal.App/App.xaml.cs +++ b/FluentTerminal.App/App.xaml.cs @@ -33,6 +33,7 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using FluentTerminal.App.Utilities; using IContainer = Autofac.IContainer; +using Windows.Globalization; namespace FluentTerminal.App { @@ -95,6 +96,7 @@ namespace FluentTerminal.App builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); builder.RegisterInstance(applicationDataContainers); _container = builder.Build(); @@ -165,7 +167,9 @@ namespace FluentTerminal.App ShellProfile profile = await _sshHelperService.GetSshShellProfileAsync(protocolActivated.Uri); if (profile != null) + { await CreateTerminal(profile, _applicationSettings.NewTerminalLocation); + } } return; @@ -246,22 +250,10 @@ namespace FluentTerminal.App { if (!_alreadyLaunched) { - var logDirectory = await ApplicationData.Current.LocalCacheFolder.CreateFolderAsync("Logs", CreationCollisionOption.OpenIfExists); - var logFile = Path.Combine(logDirectory.Path, "fluentterminal.app.log"); - var configFile = await logDirectory.CreateFileAsync("config.json", CreationCollisionOption.OpenIfExists); - var configContent = await FileIO.ReadTextAsync(configFile); + await InitializeLogger(); + Task.Run(async () => await JumpListHelper.Update(_settingsService.GetShellProfiles())); - if (string.IsNullOrWhiteSpace(configContent)) - { - configContent = JsonConvert.SerializeObject(new Logger.Configuration()); - await FileIO.WriteTextAsync(configFile, configContent); - } - - var config = JsonConvert.DeserializeObject(configContent) ?? new Logger.Configuration(); - - Logger.Instance.Initialize(logFile, config); - var viewModel = _container.Resolve(); if (args.Arguments.StartsWith(JumpListHelper.ShellProfileFlag)) { @@ -286,6 +278,25 @@ namespace FluentTerminal.App } } + private static async Task InitializeLogger() + { + var logDirectory = await ApplicationData.Current.LocalCacheFolder.CreateFolderAsync("Logs", CreationCollisionOption.OpenIfExists); + var logFile = Path.Combine(logDirectory.Path, "fluentterminal.app.log"); + var configFile = await logDirectory.CreateFileAsync("config.json", CreationCollisionOption.OpenIfExists); + var configContent = await FileIO.ReadTextAsync(configFile); + + + if (string.IsNullOrWhiteSpace(configContent)) + { + configContent = JsonConvert.SerializeObject(new Logger.Configuration()); + await FileIO.WriteTextAsync(configFile, configContent); + } + + var config = JsonConvert.DeserializeObject(configContent) ?? new Logger.Configuration(); + + Logger.Instance.Initialize(logFile, config); + } + protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) { base.OnBackgroundActivated(args); @@ -403,7 +414,10 @@ namespace FluentTerminal.App viewModel.ShowAboutRequested -= OnShowAboutRequested; viewModel.ActivatedMV -= OnMainViewActivated; if (_activeWindowId == viewModel.ApplicationView.Id) + { _activeWindowId = 0; + } + _mainViewModels.Remove(viewModel); } } diff --git a/FluentTerminal.App/Converters/EnumToDescriptionConverter.cs b/FluentTerminal.App/Converters/EnumToDescriptionConverter.cs deleted file mode 100644 index 8b32e16..0000000 --- a/FluentTerminal.App/Converters/EnumToDescriptionConverter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using FluentTerminal.App.Services.Utilities; -using Windows.UI.Xaml.Data; - -namespace FluentTerminal.App.Converters -{ - public class EnumToDescriptionConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, string language) - { - // value is the Enum, parameter is the string. - if (value is Enum enumValue) - { - return EnumHelper.GetEnumDescription(enumValue); - } - return null; - } - - public object ConvertBack(object value, Type targetType, object parameter, string language) - { - throw new NotSupportedException(); - } - } -} diff --git a/FluentTerminal.App/Converters/I18NConverter.cs b/FluentTerminal.App/Converters/I18NConverter.cs new file mode 100644 index 0000000..c4e6b58 --- /dev/null +++ b/FluentTerminal.App/Converters/I18NConverter.cs @@ -0,0 +1,27 @@ +using FluentTerminal.App.Services.Utilities; +using System; +using Windows.UI.Xaml.Data; + +namespace FluentTerminal.App.Converters +{ + public class I18NConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is Enum enumValue && parameter is string enumType) + { + return I18N.Translate($"{enumType}.{enumValue}"); + } + else if (parameter is string resource) + { + return I18N.Translate(resource); + } + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/FluentTerminal.App/Converters/LineEndingStyleConverter.cs b/FluentTerminal.App/Converters/LineEndingStyleConverter.cs deleted file mode 100644 index 7e7f264..0000000 --- a/FluentTerminal.App/Converters/LineEndingStyleConverter.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Reflection; -using Windows.UI.Xaml.Data; -using FluentTerminal.Models.Enums; - -namespace FluentTerminal.App.Converters -{ - public class LineEndingStyleConverter : IValueConverter - { - #region Static - - public static ObservableCollection LineEndingStyles { get; } = - new ObservableCollection((LineEndingStyle[]) Enum.GetValues(typeof(LineEndingStyle))); - - private static readonly Lazy> LineEndingNames = - new Lazy>(() => - LineEndingStyles.ToDictionary(les => les, les => GetEnumDescription(les))); - - private static string GetEnumDescription(Enum enumObj) - { - if (enumObj == null) - return null; - - return enumObj.GetType().GetField(enumObj.ToString()).GetCustomAttribute() - ?.Description ?? enumObj.ToString(); - } - - #endregion Static - - public object Convert(object value, Type targetType, object parameter, string language) - { - if (!(value is LineEndingStyle lineEnding)) - throw new ArgumentException($"Input value has to be of type {nameof(LineEndingStyle)}", nameof(value)); - - if (targetType != typeof(string)) - throw new ArgumentException("Target type has to be string.", nameof(targetType)); - - return LineEndingNames.Value[lineEnding]; - } - - public object ConvertBack(object value, Type targetType, object parameter, string language) => - throw new NotSupportedException(); - } -} \ No newline at end of file diff --git a/FluentTerminal.App/Dialogs/CreateKeyBindingDialog.xaml.cs b/FluentTerminal.App/Dialogs/CreateKeyBindingDialog.xaml.cs index db1ecfd..c6e1184 100644 --- a/FluentTerminal.App/Dialogs/CreateKeyBindingDialog.xaml.cs +++ b/FluentTerminal.App/Dialogs/CreateKeyBindingDialog.xaml.cs @@ -57,8 +57,8 @@ namespace FluentTerminal.App.Dialogs PreviewKeyDown += RegisterKeyBindingDialog_PreviewKeyDown; Reset(); - this.PrimaryButtonText = StringsHelper.GetString("OK"); - this.SecondaryButtonText = StringsHelper.GetString("Cancel"); + this.PrimaryButtonText = I18N.Translate("OK"); + this.SecondaryButtonText = I18N.Translate("Cancel"); } public RelayCommand ResetCommand { get; } diff --git a/FluentTerminal.App/Dialogs/InputDialog.xaml.cs b/FluentTerminal.App/Dialogs/InputDialog.xaml.cs index 535d3d2..52187ee 100644 --- a/FluentTerminal.App/Dialogs/InputDialog.xaml.cs +++ b/FluentTerminal.App/Dialogs/InputDialog.xaml.cs @@ -17,8 +17,8 @@ namespace FluentTerminal.App.Dialogs public InputDialog() { this.InitializeComponent(); - this.PrimaryButtonText = StringsHelper.GetString("OK"); - this.CloseButtonText = StringsHelper.GetString("Cancel"); + this.PrimaryButtonText = I18N.Translate("OK"); + this.CloseButtonText = I18N.Translate("Cancel"); } public async Task GetInput() diff --git a/FluentTerminal.App/Dialogs/ShellProfileSelectionDialog.xaml b/FluentTerminal.App/Dialogs/ShellProfileSelectionDialog.xaml index 250b114..21208a2 100644 --- a/FluentTerminal.App/Dialogs/ShellProfileSelectionDialog.xaml +++ b/FluentTerminal.App/Dialogs/ShellProfileSelectionDialog.xaml @@ -7,7 +7,6 @@ x:Uid="SelectProfileUI" Title="Select Profile" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" - CloseButtonText="Cancel" PrimaryButtonText="Start" mc:Ignorable="d"> - - - - - @@ -36,13 +30,13 @@ - -