diff --git a/ApplicationLibrary/Views/Setting/MacAppView.swift b/ApplicationLibrary/Views/Setting/MacAppView.swift index fa999c7..997472f 100644 --- a/ApplicationLibrary/Views/Setting/MacAppView.swift +++ b/ApplicationLibrary/Views/Setting/MacAppView.swift @@ -7,16 +7,21 @@ import SwiftUI #endif public struct AppView: View { + private static let supportedLanguages: [(code: String?, name: String)] = [ + (nil, String(localized: "System Default")), + ("en", "English"), + ("zh-Hans", "简体中文"), + ] + @State private var isLoading = true + @State private var selectedLanguage: String? - @State private var startAtLogin = false - @Environment(\.showMenuBarExtra) private var showMenuBarExtra - @Environment(\.menuBarExtraSpeedMode) private var menuBarExtraSpeedMode - @State private var menuBarExtraInBackground = false #if os(macOS) - + @State private var startAtLogin = false + @Environment(\.showMenuBarExtra) private var showMenuBarExtra + @Environment(\.menuBarExtraSpeedMode) private var menuBarExtraSpeedMode + @State private var menuBarExtraInBackground = false @State private var rootHelperRegistrationStatus: SMAppService.Status = .notRegistered - #endif @State private var alert: AlertState? @@ -32,6 +37,15 @@ public struct AppView: View { } } else { FormView { + Picker("Language", selection: $selectedLanguage) { + ForEach(Self.supportedLanguages, id: \.code) { language in + Text(language.name).tag(language.code) + } + } + .onChangeCompat(of: selectedLanguage) { newValue in + updateLanguage(newValue) + } + #if os(macOS) FormToggle("Start At Login", "Launch the application when the system is logged in. If enabled at the same time as `Show in Menu Bar` and `Keep Menu Bar in Background`, the application interface will not be opened automatically.", $startAtLogin) { newValue in updateLoginItems(newValue) @@ -145,6 +159,7 @@ public struct AppView: View { } private func loadSettings() async { + selectedLanguage = Self.currentLanguage() #if os(macOS) startAtLogin = SMAppService.mainApp.status == .enabled menuBarExtraInBackground = await SharedPreferences.menuBarExtraInBackground.get() @@ -155,6 +170,32 @@ public struct AppView: View { isLoading = false } + private static func currentLanguage() -> String? { + guard let languages = UserDefaults.standard.array(forKey: "AppleLanguages") as? [String], + let first = languages.first + else { + return nil + } + for language in supportedLanguages { + if let code = language.code, first.hasPrefix(code) { + return code + } + } + return nil + } + + private func updateLanguage(_ language: String?) { + if let language { + UserDefaults.standard.set([language], forKey: "AppleLanguages") + } else { + UserDefaults.standard.removeObject(forKey: "AppleLanguages") + } + alert = AlertState( + title: String(localized: "Restart Required"), + message: String(localized: "Language will be changed after restarting the app.") + ) + } + #if os(macOS) private func updateLoginItems(_ startAtLogin: Bool) { diff --git a/ApplicationLibrary/Views/Setting/SettingView.swift b/ApplicationLibrary/Views/Setting/SettingView.swift index 663a8a2..27fd3b2 100644 --- a/ApplicationLibrary/Views/Setting/SettingView.swift +++ b/ApplicationLibrary/Views/Setting/SettingView.swift @@ -27,11 +27,7 @@ public struct SettingView: View { self } - #if os(macOS) - case app - #endif - - case core, packetTunnel, onDemandRules, profileOverride, sponsors + case app, core, packetTunnel, onDemandRules, profileOverride, sponsors #if os(macOS) var page: SettingsPage { @@ -58,10 +54,8 @@ public struct SettingView: View { var title: String { switch self { - #if os(macOS) - case .app: - return String(localized: "App") - #endif + case .app: + return String(localized: "App") case .core: return String(localized: "Core") case .packetTunnel: @@ -77,10 +71,8 @@ public struct SettingView: View { private var iconImage: String { switch self { - #if os(macOS) - case .app: - return "app.badge.fill" - #endif + case .app: + return "app.badge.fill" case .core: return "shippingbox.fill" case .packetTunnel: @@ -98,10 +90,8 @@ public struct SettingView: View { var contentView: some View { Group { switch self { - #if os(macOS) - case .app: - AppView() - #endif + case .app: + AppView() case .core: CoreView() case .packetTunnel: @@ -165,10 +155,7 @@ public struct SettingView: View { public var body: some View { FormView { Section { - #if os(macOS) - Tabs.app.navigationLink - #endif - ForEach([Tabs.core, Tabs.packetTunnel, Tabs.onDemandRules, Tabs.profileOverride]) { it in + ForEach([Tabs.app, Tabs.core, Tabs.packetTunnel, Tabs.onDemandRules, Tabs.profileOverride]) { it in it.navigationLink } } diff --git a/Localizable.xcstrings b/Localizable.xcstrings index f2521af..a92ce07 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -1531,6 +1531,26 @@ } } }, + "Language" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "语言" + } + } + } + }, + "Language will be changed after restarting the app." : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "重启应用后语言将生效。" + } + } + } + }, "Last Updated" : { "localizations" : { "zh-Hans" : { @@ -1830,9 +1850,6 @@ } } } - }, - "Not installed" : { - }, "Ok" : { "localizations" : { @@ -2141,6 +2158,16 @@ } } }, + "Restart Required" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "需要重启" + } + } + } + }, "Resume" : { "comment" : "Resume log auto-scroll", "localizations" : { @@ -2517,6 +2544,16 @@ } } }, + "System Default" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "跟随系统" + } + } + } + }, "System Extension" : { "localizations" : { "zh-Hans" : {