Add in-app language picker to App Settings

This commit is contained in:
世界
2026-01-05 19:51:15 +08:00
parent ce0d7ead88
commit 98410fe093
3 changed files with 95 additions and 30 deletions
@@ -7,16 +7,21 @@ import SwiftUI
#endif #endif
public struct AppView: View { 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 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) #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 @State private var rootHelperRegistrationStatus: SMAppService.Status = .notRegistered
#endif #endif
@State private var alert: AlertState? @State private var alert: AlertState?
@@ -32,6 +37,15 @@ public struct AppView: View {
} }
} else { } else {
FormView { 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) #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 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) updateLoginItems(newValue)
@@ -145,6 +159,7 @@ public struct AppView: View {
} }
private func loadSettings() async { private func loadSettings() async {
selectedLanguage = Self.currentLanguage()
#if os(macOS) #if os(macOS)
startAtLogin = SMAppService.mainApp.status == .enabled startAtLogin = SMAppService.mainApp.status == .enabled
menuBarExtraInBackground = await SharedPreferences.menuBarExtraInBackground.get() menuBarExtraInBackground = await SharedPreferences.menuBarExtraInBackground.get()
@@ -155,6 +170,32 @@ public struct AppView: View {
isLoading = false 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) #if os(macOS)
private func updateLoginItems(_ startAtLogin: Bool) { private func updateLoginItems(_ startAtLogin: Bool) {
@@ -27,11 +27,7 @@ public struct SettingView: View {
self self
} }
#if os(macOS) case app, core, packetTunnel, onDemandRules, profileOverride, sponsors
case app
#endif
case core, packetTunnel, onDemandRules, profileOverride, sponsors
#if os(macOS) #if os(macOS)
var page: SettingsPage { var page: SettingsPage {
@@ -58,10 +54,8 @@ public struct SettingView: View {
var title: String { var title: String {
switch self { switch self {
#if os(macOS) case .app:
case .app: return String(localized: "App")
return String(localized: "App")
#endif
case .core: case .core:
return String(localized: "Core") return String(localized: "Core")
case .packetTunnel: case .packetTunnel:
@@ -77,10 +71,8 @@ public struct SettingView: View {
private var iconImage: String { private var iconImage: String {
switch self { switch self {
#if os(macOS) case .app:
case .app: return "app.badge.fill"
return "app.badge.fill"
#endif
case .core: case .core:
return "shippingbox.fill" return "shippingbox.fill"
case .packetTunnel: case .packetTunnel:
@@ -98,10 +90,8 @@ public struct SettingView: View {
var contentView: some View { var contentView: some View {
Group { Group {
switch self { switch self {
#if os(macOS) case .app:
case .app: AppView()
AppView()
#endif
case .core: case .core:
CoreView() CoreView()
case .packetTunnel: case .packetTunnel:
@@ -165,10 +155,7 @@ public struct SettingView: View {
public var body: some View { public var body: some View {
FormView { FormView {
Section { Section {
#if os(macOS) ForEach([Tabs.app, Tabs.core, Tabs.packetTunnel, Tabs.onDemandRules, Tabs.profileOverride]) { it in
Tabs.app.navigationLink
#endif
ForEach([Tabs.core, Tabs.packetTunnel, Tabs.onDemandRules, Tabs.profileOverride]) { it in
it.navigationLink it.navigationLink
} }
} }
+40 -3
View File
@@ -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" : { "Last Updated" : {
"localizations" : { "localizations" : {
"zh-Hans" : { "zh-Hans" : {
@@ -1830,9 +1850,6 @@
} }
} }
} }
},
"Not installed" : {
}, },
"Ok" : { "Ok" : {
"localizations" : { "localizations" : {
@@ -2141,6 +2158,16 @@
} }
} }
}, },
"Restart Required" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "需要重启"
}
}
}
},
"Resume" : { "Resume" : {
"comment" : "Resume log auto-scroll", "comment" : "Resume log auto-scroll",
"localizations" : { "localizations" : {
@@ -2517,6 +2544,16 @@
} }
} }
}, },
"System Default" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "跟随系统"
}
}
}
},
"System Extension" : { "System Extension" : {
"localizations" : { "localizations" : {
"zh-Hans" : { "zh-Hans" : {