From 5c3e291ed712b160b248282a26d41fbfeb7cdd42 Mon Sep 17 00:00:00 2001 From: pdjdev Date: Fri, 14 Aug 2026 14:11:56 +0900 Subject: [PATCH 1/4] feat: add localization support and language settings - Introduced a Localizer class to manage string resources for English and Korean. - Updated various UI components to use localized strings instead of hardcoded text. - Added language setting in application settings to allow users to choose between system default, Korean, and English. - Embedded English resource strings directly in the main assembly to avoid generating a satellite assembly. - Modified the MainWindow, TrayControl, and other components to reflect localized text for better user experience. - Created new resource files for English and Korean strings. --- Battify/App.xaml.cs | 4 +- Battify/AssemblyInfo.cs | 3 + Battify/BatteryInfoWindow.xaml | 149 +++++++++++-- Battify/BatteryInfoWindow.xaml.cs | 75 ++++--- Battify/Battify.csproj | 5 + Battify/Localizer.cs | 63 ++++++ Battify/MainWindow.xaml.cs | 17 +- Battify/MsixStartupSetter.cs | 12 +- Battify/Settings.Designer.cs | 16 +- Battify/Settings.settings | 5 +- Battify/StartupSetter.cs | 2 +- Battify/Strings.en.resx | 341 ++++++++++++++++++++++++++++++ Battify/Strings.resx | 80 +++++++ Battify/TrayControl.cs | 37 ++-- Battify/TrayGuideWindow.xaml | 13 +- 15 files changed, 735 insertions(+), 87 deletions(-) create mode 100644 Battify/Localizer.cs create mode 100644 Battify/Strings.en.resx create mode 100644 Battify/Strings.resx diff --git a/Battify/App.xaml.cs b/Battify/App.xaml.cs index c780c0d..1fe8d92 100644 --- a/Battify/App.xaml.cs +++ b/Battify/App.xaml.cs @@ -14,6 +14,8 @@ public partial class App : System.Windows.Application protected override void OnStartup(StartupEventArgs e) { + Localizer.ApplyConfiguredCulture(); + // 뮤텍스 생성 시도 _mutex = new Mutex(true, MutexName, out bool createdNew); @@ -22,7 +24,7 @@ protected override void OnStartup(StartupEventArgs e) // 이미 실행 중인 인스턴스가 있음 /* System.Windows.MessageBox.Show( - "Battify가 이미 실행 중입니다.\n시스템 트레이를 확인해주세요.", + Localizer.Get("App.AlreadyRunning"), "Battify", MessageBoxButton.OK, MessageBoxImage.Information); diff --git a/Battify/AssemblyInfo.cs b/Battify/AssemblyInfo.cs index b0ec827..8564045 100644 --- a/Battify/AssemblyInfo.cs +++ b/Battify/AssemblyInfo.cs @@ -1,4 +1,7 @@ using System.Windows; +using System.Resources; + +[assembly: NeutralResourcesLanguage("ko-KR")] [assembly: ThemeInfo( ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located diff --git a/Battify/BatteryInfoWindow.xaml b/Battify/BatteryInfoWindow.xaml index 68afbae..73c7dbf 100644 --- a/Battify/BatteryInfoWindow.xaml +++ b/Battify/BatteryInfoWindow.xaml @@ -1,7 +1,8 @@ + + + + + @@ -143,7 +252,7 @@ - \ No newline at end of file + diff --git a/Battify/BatteryInfoWindow.xaml.cs b/Battify/BatteryInfoWindow.xaml.cs index 2afcada..b49d095 100644 --- a/Battify/BatteryInfoWindow.xaml.cs +++ b/Battify/BatteryInfoWindow.xaml.cs @@ -34,6 +34,15 @@ public BatteryInfoWindow() // 창이 로드된 후 DWM 속성 설정 this.Loaded += BatteryInfoWindow_Loaded; + + foreach (var item in LanguageSelector.Items.OfType()) + { + if ((string)item.Tag == Settings.Default.language) + { + LanguageSelector.SelectedItem = item; + break; + } + } } private async void BatteryInfoWindow_Loaded(object sender, RoutedEventArgs e) @@ -47,12 +56,12 @@ private async void BatteryInfoWindow_Loaded(object sender, RoutedEventArgs e) this.Top = workArea.Bottom - this.Height; // 버전 정보 갱신해서 표시 - AppVersionLabel.Content = "배티파이, v" + assmblyVersion; + AppVersionLabel.Content = Localizer.Format("Battery.AppVersion", assmblyVersion); // StatusTextBox를 비활성화하고 로딩 메시지 표시 StatusTextBox.IsEnabled = false; - StatusTextBox.Text = "로드 중..."; + StatusTextBox.Text = Localizer.Get("Common.Loading"); // 시작프로그램 설정 확인 및 디버깅 정보 출력 SetStartupChk.IsChecked = await MsixStartupSetter.IsStartupEnabledAsync(); @@ -99,7 +108,7 @@ private async void UpdateButton_Click(object sender, RoutedEventArgs e) { // 업데이트 버튼도 비동기로 처리 StatusTextBox.IsEnabled = false; - StatusTextBox.Text = "로드 중..."; + StatusTextBox.Text = Localizer.Get("Common.Loading"); await LoadBatteryInfoAsync(); } @@ -113,36 +122,36 @@ private void UpdateText() // voltage string voltage = BatteryInfoGetter.Get("Voltage"); - resultString += "전압: " + voltage + " mV" + Environment.NewLine; + resultString += Localizer.Format("Battery.Voltage", voltage) + Environment.NewLine; // DesignVoltage string designVoltage = BatteryInfoGetter.Get("DesignVoltage"); - resultString += "지정 전압: " + designVoltage + " mV" + Environment.NewLine; + resultString += Localizer.Format("Battery.DesignVoltage", designVoltage) + Environment.NewLine; // ChargeRate string chargeRate = BatteryInfoGetter.Get("ChargeRate"); - resultString += "충전율: " + chargeRate + " mW" + Environment.NewLine; + resultString += Localizer.Format("Battery.ChargeRate", chargeRate) + Environment.NewLine; // DischargeRate string dischargeRate = BatteryInfoGetter.Get("DischargeRate"); - resultString += "방전율: " + dischargeRate + " mW" + Environment.NewLine; + resultString += Localizer.Format("Battery.DischargeRate", dischargeRate) + Environment.NewLine; // DesignCapacity string designCapacity = BatteryInfoGetter.Get("DesignCapacity"); - resultString += "지정 용량: " + designCapacity + " mWh" + Environment.NewLine; + resultString += Localizer.Format("Battery.DesignCapacity", designCapacity) + Environment.NewLine; // MaxCapacity uint maxCapacity = BatteryInfoGetter.MaxCapacity(); - resultString += "완충 용량: " + maxCapacity + " mWh" + Environment.NewLine; + resultString += Localizer.Format("Battery.MaxCapacity", maxCapacity) + Environment.NewLine; // RemainingCapacity string remainingCapacity = BatteryInfoGetter.Get("RemainingCapacity"); uint remainingCapacityUint = BatteryInfoGetter.RemainingCapacity(); - resultString += "남은 용량 (레거시): " + remainingCapacity + "(" + remainingCapacityUint.ToString() + ") mWh" + Environment.NewLine; + resultString += Localizer.Format("Battery.RemainingCapacity", remainingCapacity, remainingCapacityUint) + Environment.NewLine; // Name string name = BatteryInfoGetter.Get("Name"); - resultString += "모델명: " + name + Environment.NewLine; + resultString += Localizer.Format("Battery.ModelName", name) + Environment.NewLine; // EstimatedChargeRemaining string estimatedChargeRemaining = BatteryInfoGetter.Get("EstimatedChargeRemaining"); @@ -151,19 +160,14 @@ private void UpdateText() { int hours = estimatedChargeRemainingInt / 3600; int minutes = estimatedChargeRemainingInt % 3600 / 60; - estimatedChargeRemaining = ""; - - if (hours > 0) - { - estimatedChargeRemaining = hours + "시간 "; - } - - estimatedChargeRemaining += minutes + "분"; + estimatedChargeRemaining = hours > 0 + ? Localizer.Format("Battery.TimeHoursMinutes", hours, minutes) + : Localizer.Format("Battery.TimeMinutes", minutes); } - resultString += "충전 예상 시간: " + estimatedChargeRemaining + Environment.NewLine; + resultString += Localizer.Format("Battery.EstimatedChargeTime", estimatedChargeRemaining) + Environment.NewLine; - resultString += "레거시 예상 시간: " + BatteryInfoGetter.EstimatedTime().ToString() + Environment.NewLine; + resultString += Localizer.Format("Battery.LegacyEstimatedTime", BatteryInfoGetter.EstimatedTime()) + Environment.NewLine; // 계산 @@ -172,25 +176,25 @@ private void UpdateText() { // 충전 퍼센트 계산 double percentage = (double)remainingCapacityInt / maxCapacity * 100; - resultString += "충전 퍼센트: " + percentage.ToString("0.00") + "%" + Environment.NewLine; + resultString += Localizer.Format("Battery.ChargePercentage", percentage) + Environment.NewLine; // 지정 용량이 숫자로 변환 가능한 경우 if (int.TryParse(designCapacity, out int designCapacityInt)) { // 웨어율 계산 double wear = (double)(designCapacityInt - maxCapacity) / designCapacityInt * 100; - resultString += "웨어율: " + wear.ToString("0.00") + "%" + Environment.NewLine; + resultString += Localizer.Format("Battery.WearRate", wear) + Environment.NewLine; } } // PowerOnline string powerOnline = BatteryInfoGetter.Get("PowerOnline"); - resultString += "전원 연결: " + powerOnline; + resultString += Localizer.Format("Battery.PowerOnline", powerOnline); } catch (Exception ex) { - resultString = "배터리 정보 로드 실패: " + ex.Message; + resultString = Localizer.Format("Battery.LoadFailed", ex.Message); } // 이제 프로그램 정보 덧대기 @@ -198,10 +202,10 @@ private void UpdateText() resultString += Environment.NewLine + Environment.NewLine; // 프로그램 정보 - resultString += "Battify v" + assmblyVersion + Environment.NewLine; - resultString += "Made by PBJSoftware (박동준)" + Environment.NewLine; + resultString += Localizer.Format("Battery.AppVersion", assmblyVersion) + Environment.NewLine; + resultString += Localizer.Get("Battery.Author") + Environment.NewLine; resultString += "Source: https://github.com/pdjdev/Battify" + Environment.NewLine; - resultString += "본 프로그램은 MIT License 하에 자유롭게 이용이 가능합니다." + Environment.NewLine; + resultString += Localizer.Get("Battery.License") + Environment.NewLine; // StatusTextBox 출력 StatusTextBox.Text = resultString; @@ -270,7 +274,7 @@ private async Task HandleStartupSettingChange(bool isChecked) { SetStartupCheckbox(!isChecked); var errorMessage = MsixStartupSetter.LastError - ?? "시작 프로그램 설정을 변경하지 못했습니다."; + ?? Localizer.Get("Startup.ChangeFailed"); global::System.Windows.MessageBox.Show(errorMessage, "Battify", MessageBoxButton.OK, MessageBoxImage.Information); return; @@ -284,7 +288,7 @@ private async Task HandleStartupSettingChange(bool isChecked) SetStartupCheckbox(!isChecked); Debug.WriteLine($"SetStartupChk 변경 예외: {ex}"); - global::System.Windows.MessageBox.Show("시작 프로그램 설정 중 오류가 발생했습니다." + Environment.NewLine + ex.Message, + global::System.Windows.MessageBox.Show(Localizer.Format("Startup.ChangeError", ex.Message), "Battify", MessageBoxButton.OK, MessageBoxImage.Error); } } @@ -301,5 +305,16 @@ private void SetStartupCheckbox(bool isChecked) isUpdatingStartupSetting = false; } } + + private void LanguageSelector_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) + { + if (LanguageSelector.SelectedValue is not string language || language == Settings.Default.language) + return; + + Settings.Default.language = language; + Settings.Default.Save(); + global::System.Windows.MessageBox.Show(Localizer.Get("Language.RestartRequired"), + Localizer.Get("Language.RestartTitle"), MessageBoxButton.OK, MessageBoxImage.Information); + } } } diff --git a/Battify/Battify.csproj b/Battify/Battify.csproj index 443901a..4a6617b 100644 --- a/Battify/Battify.csproj +++ b/Battify/Battify.csproj @@ -86,6 +86,11 @@ + + + false + Battify.Strings.en.resources + ResXFileCodeGenerator AppIcon.Designer.cs diff --git a/Battify/Localizer.cs b/Battify/Localizer.cs new file mode 100644 index 0000000..fd1a8f3 --- /dev/null +++ b/Battify/Localizer.cs @@ -0,0 +1,63 @@ +using System.Globalization; +using System.Resources; +using System.Threading; +using System.Windows.Markup; + +namespace Battify +{ + internal static class Localizer + { + private static readonly ResourceManager ResourceManager = new ResourceManager("Battify.Strings", typeof(Localizer).Assembly); + private static readonly ResourceManager EnglishResourceManager = new ResourceManager("Battify.Strings.en", typeof(Localizer).Assembly); + + public static string Get(string key) + { + var resourceManager = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "en" + ? EnglishResourceManager + : ResourceManager; + + // The English resource is embedded in the main assembly, so it is + // intentionally read as the invariant resource set rather than as + // a satellite assembly. + var culture = ReferenceEquals(resourceManager, EnglishResourceManager) + ? CultureInfo.InvariantCulture + : CultureInfo.CurrentUICulture; + + return resourceManager.GetString(key, culture) ?? key; + } + + public static string Format(string key, params object[] arguments) => + string.Format(CultureInfo.CurrentCulture, Get(key), arguments); + + public static void ApplyConfiguredCulture() + { + var language = Settings.Default.language; + if (language == "system") + { + // Korean is the only non-English translation currently + // available; all other Windows UI languages use English. + language = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "ko" + ? "ko" + : "en"; + } + + if (language == "ko" || language == "en") + { + var culture = CultureInfo.GetCultureInfo(language); + CultureInfo.DefaultThreadCurrentCulture = culture; + CultureInfo.DefaultThreadCurrentUICulture = culture; + Thread.CurrentThread.CurrentCulture = culture; + Thread.CurrentThread.CurrentUICulture = culture; + } + } + } + + [MarkupExtensionReturnType(typeof(string))] + public sealed class LocExtension : MarkupExtension + { + public LocExtension(string key) => Key = key; + public string Key { get; } + + public override object ProvideValue(IServiceProvider serviceProvider) => Localizer.Get(Key); + } +} diff --git a/Battify/MainWindow.xaml.cs b/Battify/MainWindow.xaml.cs index 8c34617..46c026b 100644 --- a/Battify/MainWindow.xaml.cs +++ b/Battify/MainWindow.xaml.cs @@ -172,20 +172,13 @@ private void InitBattTimer() // 트레이 아이콘 툴팁 텍스트 설정 if (percentage < 0 || percentage > 100) { - trayControl.trayIcon.Text = "배터리 없음"; + trayControl.trayIcon.Text = Localizer.Get("Tray.NoBattery"); } else { - trayControl.trayIcon.Text = percentage.ToString() + "%"; - - if (plugged) - { - trayControl.trayIcon.Text += ", 충전중"; - } - else - { - trayControl.trayIcon.Text += " 남음"; - } + trayControl.trayIcon.Text = plugged + ? Localizer.Format("Tray.Charging", percentage) + : Localizer.Format("Tray.Remaining", percentage); } }; @@ -463,4 +456,4 @@ public void UpdateIcon(int percentage) } -} \ No newline at end of file +} diff --git a/Battify/MsixStartupSetter.cs b/Battify/MsixStartupSetter.cs index fe090e6..ddf2df6 100644 --- a/Battify/MsixStartupSetter.cs +++ b/Battify/MsixStartupSetter.cs @@ -39,7 +39,7 @@ public static async Task IsStartupEnabledAsync() } catch (Exception ex) { - LastError = $"MSIX 시작 프로그램 상태를 확인하지 못했습니다: {ex.Message}"; + LastError = Localizer.Format("Startup.StatusCheckFailed", ex.Message); return false; } } @@ -57,7 +57,7 @@ public static async Task SetStartupAsync(bool enable) } catch (Exception ex) { - LastError = $"시작 프로그램 레지스트리를 변경하지 못했습니다: {ex.Message}"; + LastError = Localizer.Format("Startup.RegistryChangeFailed", ex.Message); return false; } } @@ -81,19 +81,19 @@ public static async Task SetStartupAsync(bool enable) case StartupTaskState.Disabled: return await startupTask.RequestEnableAsync() == StartupTaskState.Enabled; case StartupTaskState.DisabledByUser: - LastError = "작업 관리자에서 사용자가 시작 프로그램을 해제했습니다. 작업 관리자의 시작 앱 탭에서 Battify를 다시 활성화해주세요."; + LastError = Localizer.Get("Startup.DisabledByUser"); return false; case StartupTaskState.DisabledByPolicy: - LastError = "조직 또는 Windows 정책에 의해 시작 프로그램이 비활성화되어 있습니다."; + LastError = Localizer.Get("Startup.DisabledByPolicy"); return false; default: - LastError = $"알 수 없는 시작 프로그램 상태입니다: {startupTask.State}"; + LastError = Localizer.Format("Startup.UnknownState", startupTask.State); return false; } } catch (Exception ex) { - LastError = $"MSIX 시작 프로그램을 변경하지 못했습니다: {ex.Message}"; + LastError = Localizer.Format("Startup.MsixChangeFailed", ex.Message); return false; } } diff --git a/Battify/Settings.Designer.cs b/Battify/Settings.Designer.cs index c1dfd08..f3219b1 100644 --- a/Battify/Settings.Designer.cs +++ b/Battify/Settings.Designer.cs @@ -14,7 +14,7 @@ namespace Battify { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); public static Settings Default { @@ -22,7 +22,7 @@ public static Settings Default { return defaultInstance; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -34,7 +34,6 @@ public bool mute { this["mute"] = value; } } - [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -82,5 +81,16 @@ public string traytheme { this["traytheme"] = value; } } + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("system")] + public string language { + get { + return ((string)(this["language"])); + } + set { + this["language"] = value; + } + } } } diff --git a/Battify/Settings.settings b/Battify/Settings.settings index f548625..b15e4af 100644 --- a/Battify/Settings.settings +++ b/Battify/Settings.settings @@ -17,5 +17,8 @@ auto + + system + - \ No newline at end of file + diff --git a/Battify/StartupSetter.cs b/Battify/StartupSetter.cs index 76af8e4..6a8f710 100644 --- a/Battify/StartupSetter.cs +++ b/Battify/StartupSetter.cs @@ -11,7 +11,7 @@ internal static class StartupSetter public static bool SetStartup(bool enable) { using var key = Registry.CurrentUser.OpenSubKey(RunKeyPath, writable: true) - ?? throw new InvalidOperationException("시작 프로그램 레지스트리 키를 열 수 없습니다."); + ?? throw new InvalidOperationException(Localizer.Get("Startup.RegistryKeyFailed")); if (enable) key.SetValue(ValueName, QuoteExecutablePath(GetExecutablePath()), RegistryValueKind.String); diff --git a/Battify/Strings.en.resx b/Battify/Strings.en.resx new file mode 100644 index 0000000..f6315d6 --- /dev/null +++ b/Battify/Strings.en.resx @@ -0,0 +1,341 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Battify is already running. +Check the system tray. + + + Battify information + + + Battify installation complete + + + Battify, v{0} + + + Battery & app information + + + Refresh + + + Close + + + Loading... + + + Run at startup + + + Check for updates + + + (Store) + + + Developer site (pbj.kr) + + + Voltage: {0} mV + + + Design voltage: {0} mV + + + Charge rate: {0} mW + + + Discharge rate: {0} mW + + + Design capacity: {0} mWh + + + Full charge capacity: {0} mWh + + + Remaining capacity (legacy): {0} ({1}) mWh + + + Model: {0} + + + {0}h {1}m + + + {0}m + + + Estimated charge time: {0} + + + Legacy estimated time: {0} + + + Charge percentage: {0:0.00}% + + + Wear level: {0:0.00}% + + + Power connected: {0} + + + Failed to load battery information: {0} + + + Made by PBJSoftware (Dongjun Park) + + + This program is freely available under the MIT License. + + + Could not change the startup setting. + + + An error occurred while changing the startup setting. +{0} + + + Could not check MSIX startup status: {0} + + + Could not change the startup registry setting: {0} + + + Startup was disabled by the user in Task Manager. Re-enable Battify in the Startup apps tab. + + + Startup is disabled by organization or Windows policy. + + + Unknown startup state: {0} + + + Could not change MSIX startup setting: {0} + + + Could not open the startup registry key. + + + Setup complete + + + Added to startup apps. + + + Setup failed + + + Could not add to startup apps. + + + Startup not configured + + + Click here to run Battify at startup. + + + Exit Battify + + + Appearance + + + Notifications + + + Info / Settings + + + Enable sound + + + Disable sound + + + Enable popup + + + Disable popup + + + Popup color: Auto + + + Popup color: Light + + + Popup color: Dark + + + Icon color: Auto + + + Icon color: Light + + + Icon color: Dark + + + No battery + + + {0}%, charging + + + {0}% remaining + + + Battify has been installed! + + + If the battery percentage icon is not visible, + + + drag the percentage ( + + + ) icon out to pin it. + + + Got it + + + Language + + + System default + + + 한국어 + + + English + + + The language change will apply after restarting Battify. + + + Language changed + + \ No newline at end of file diff --git a/Battify/Strings.resx b/Battify/Strings.resx new file mode 100644 index 0000000..db132ab --- /dev/null +++ b/Battify/Strings.resx @@ -0,0 +1,80 @@ + + + text/microsoft-resx + 2.0 + System.Resources.ResXResourceReader, System.Windows.Forms + System.Resources.ResXResourceWriter, System.Windows.Forms + Battify가 이미 실행 중입니다. 시스템 트레이를 확인해주세요. + Battify 정보 + Battify 설치 완료 + 배티파이, v{0} + 배터리 & 앱 정보 + 새로 고침 + 닫기 + 로드 중... + 시작프로그램 설정 + 최신 버전 확인 + (스토어) + 제작자 사이트 (pbj.kr) + 전압: {0} mV + 지정 전압: {0} mV + 충전율: {0} mW + 방전율: {0} mW + 지정 용량: {0} mWh + 완충 용량: {0} mWh + 남은 용량 (레거시): {0} ({1}) mWh + 모델명: {0} + {0}시간 {1}분 + {0}분 + 충전 예상 시간: {0} + 레거시 예상 시간: {0} + 충전 퍼센트: {0:0.00}% + 웨어율: {0:0.00}% + 전원 연결: {0} + 배터리 정보 로드 실패: {0} + Made by PBJSoftware (박동준) + 본 프로그램은 MIT License 하에 자유롭게 이용이 가능합니다. + 시작 프로그램 설정을 변경하지 못했습니다. + 시작 프로그램 설정 중 오류가 발생했습니다. {0} + MSIX 시작 프로그램 상태를 확인하지 못했습니다: {0} + 시작 프로그램 레지스트리를 변경하지 못했습니다: {0} + 작업 관리자에서 사용자가 시작 프로그램을 해제했습니다. 작업 관리자의 시작 앱 탭에서 Battify를 다시 활성화해주세요. + 조직 또는 Windows 정책에 의해 시작 프로그램이 비활성화되어 있습니다. + 알 수 없는 시작 프로그램 상태입니다: {0} + MSIX 시작 프로그램을 변경하지 못했습니다: {0} + 시작 프로그램 레지스트리 키를 열 수 없습니다. + 설정 완료 + 시작 프로그램으로 설정되었습니다. + 설정 실패 + 시작 프로그램 설정에 실패했습니다. + 시작 프로그램 설정 안됨 + 여기를 눌러 시작프로그램으로 설정하세요. + Battify 종료 + 모양 + 알림 + 정보 / 설정 + 알림음 켜기 + 알림음 끄기 + 팝업 켜기 + 팝업 끄기 + 팝업 색: 자동 + 팝업 색: 라이트 + 팝업 색: 다크 + 아이콘 색: 자동 + 아이콘 색: 라이트 + 아이콘 색: 다크 + 배터리 없음 + {0}%, 충전중 + {0}% 남음 + Battify가 설치되었습니다! + 배터리 퍼센트 아이콘이 보이지 않는다면, + 퍼센트 숫자( + ) 아이콘을 밖으로 드래그하여 고정해 주세요. + 확인했어요 + 언어 + 시스템 기본값 + 한국어 + English + 언어 변경은 Battify를 다시 시작한 후 적용됩니다. + 언어 변경 + diff --git a/Battify/TrayControl.cs b/Battify/TrayControl.cs index 49b1880..63c8e12 100644 --- a/Battify/TrayControl.cs +++ b/Battify/TrayControl.cs @@ -11,6 +11,7 @@ public TrayControl(MainWindow w) { InitializeComponent(); mainWindow = w; + ApplyLocalizedTexts(); // 커스텀 렌더러 적용 contextMenuStrip1.Renderer = new DarkMenuRenderer(); @@ -71,11 +72,11 @@ private async void TrayIcon_BalloonTipClicked(object? sender, EventArgs e) bool success = await MsixStartupSetter.SetStartupAsync(true); if (success) { - trayIcon.ShowBalloonTip(2000, "설정 완료", "시작 프로그램으로 설정되었습니다.", ToolTipIcon.Info); + trayIcon.ShowBalloonTip(2000, Localizer.Get("Tray.StartupCompleted"), Localizer.Get("Tray.StartupCompletedMessage"), ToolTipIcon.Info); } else { - trayIcon.ShowBalloonTip(2000, "설정 실패", "시작 프로그램 설정에 실패했습니다.", ToolTipIcon.Error); + trayIcon.ShowBalloonTip(2000, Localizer.Get("Tray.StartupFailed"), Localizer.Get("Tray.StartupFailedMessage"), ToolTipIcon.Error); } } finally @@ -160,8 +161,8 @@ private void showBattInfoToolStripMenuItem_Click(object sender, EventArgs e) public void StartupSuggestBalloonShow() { - trayIcon.BalloonTipTitle = "시작 프로그램 설정 안됨"; - trayIcon.BalloonTipText = "여기를 눌러 시작프로그램으로 설정하세요."; + trayIcon.BalloonTipTitle = Localizer.Get("Tray.StartupNotConfigured"); + trayIcon.BalloonTipText = Localizer.Get("Tray.StartupSuggestion"); trayIcon.BalloonTipIcon = ToolTipIcon.Info; trayIcon.ShowBalloonTip(3000); @@ -182,30 +183,38 @@ private void togglePopupToolStripMenuItem_Click(object sender, EventArgs e) private void contextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e) { // 알림음 메뉴 텍스트 업데이트 - muteToolStripMenuItem.Text = Settings.Default.mute ? "알림음 켜기" : "알림음 끄기"; + muteToolStripMenuItem.Text = Settings.Default.mute ? Localizer.Get("Tray.EnableSound") : Localizer.Get("Tray.DisableSound"); // 팝업 메뉴 텍스트 업데이트 - togglePopupToolStripMenuItem.Text = Settings.Default.nopopup ? "팝업 켜기" : "팝업 끄기"; + togglePopupToolStripMenuItem.Text = Settings.Default.nopopup ? Localizer.Get("Tray.EnablePopup") : Localizer.Get("Tray.DisablePopup"); // 팝업 색상 메뉴 텍스트 업데이트 string popupThemeText = Settings.Default.theme switch { - "auto" => "팝업 색: 자동", - "light" => "팝업 색: 라이트", - "dark" => "팝업 색: 다크", - _ => "팝업 색: 자동" + "auto" => Localizer.Get("Tray.PopupThemeAuto"), + "light" => Localizer.Get("Tray.PopupThemeLight"), + "dark" => Localizer.Get("Tray.PopupThemeDark"), + _ => Localizer.Get("Tray.PopupThemeAuto") }; changeThemeToolStripMenuItem.Text = popupThemeText; // 아이콘 색상 메뉴 텍스트 업데이트 string iconThemeText = Settings.Default.traytheme switch { - "auto" => "아이콘 색: 자동", - "white" => "아이콘 색: 라이트", - "black" => "아이콘 색: 다크", - _ => "아이콘 색: 자동" + "auto" => Localizer.Get("Tray.IconThemeAuto"), + "white" => Localizer.Get("Tray.IconThemeLight"), + "black" => Localizer.Get("Tray.IconThemeDark"), + _ => Localizer.Get("Tray.IconThemeAuto") }; changeTrayToolStripMenuItem.Text = iconThemeText; } + + private void ApplyLocalizedTexts() + { + closeAppToolStripMenuItem.Text = Localizer.Get("Tray.Exit"); + appearanceToolStripMenuItem.Text = Localizer.Get("Tray.Appearance"); + notificationToolStripMenuItem.Text = Localizer.Get("Tray.Notifications"); + showBattInfoToolStripMenuItem.Text = Localizer.Get("Tray.InformationSettings"); + } } } diff --git a/Battify/TrayGuideWindow.xaml b/Battify/TrayGuideWindow.xaml index 6455fdf..b86cd42 100644 --- a/Battify/TrayGuideWindow.xaml +++ b/Battify/TrayGuideWindow.xaml @@ -1,7 +1,8 @@ - + - + - + @@ -98,7 +99,7 @@