add: ability to change app localization in app

This commit is contained in:
Morteza Gharedaghi 2024-10-28 16:25:53 +03:30
parent dd9a348298
commit 6a4c0bd4b7
4 changed files with 64 additions and 10 deletions

View file

@ -796,6 +796,7 @@
nl,
pl,
ar,
fa,
);
mainGroup = CAD2E7952449574E00113D76;
packageReferences = (
@ -1142,7 +1143,7 @@
CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY;
CODE_SIGN_STYLE = Automatic;
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
DEVELOPMENT_TEAM = ZU6GR6B2FY;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "$(SRCROOT)/$(TARGET_NAME)/Info.plist";
MARKETING_VERSION = 2.0.0;
@ -1192,10 +1193,10 @@
buildSettings = {
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
DEVELOPMENT_TEAM = ZU6GR6B2FY;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "$(SRCROOT)/$(TARGET_NAME)/Info.plist";
MARKETING_VERSION = 2.0.0;
@ -1340,12 +1341,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY;
CODE_SIGN_ENTITLEMENTS = Xcodes/Resources/Xcodes.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 28;
DEVELOPMENT_ASSET_PATHS = "\"Xcodes/Preview Content\"";
DEVELOPMENT_TEAM = ZU6GR6B2FY;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = Xcodes/Resources/Info.plist;
@ -1368,12 +1369,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT = ZU6GR6B2FY;
CODE_SIGN_ENTITLEMENTS = Xcodes/Resources/Xcodes.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 28;
DEVELOPMENT_ASSET_PATHS = "\"Xcodes/Preview Content\"";
DEVELOPMENT_TEAM = ZU6GR6B2FY;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = Xcodes/Resources/Info.plist;
@ -1397,7 +1398,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = ZU6GR6B2FY;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = XcodesTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",

View file

@ -71,7 +71,7 @@ class AppState: ObservableObject {
/// Whether the user is being prepared for the helper installation alert with an explanation.
/// This closure will be performed after the user chooses whether or not to proceed.
@Published var isPreparingUserForActionRequiringHelper: ((Bool) -> Void)?
// MARK: - Errors
@Published var error: Error?
@ -136,6 +136,14 @@ class AppState: ObservableObject {
}
}
@Published var appLanguage: String? {
didSet {
Current.defaults.set(appLanguage, forKey: "appLanguage")
}
}
// MARK: - Runtimes
@Published var downloadableRuntimes: [DownloadableRuntime] = []

View file

@ -3,7 +3,8 @@ import SwiftUI
struct GeneralPreferencePane: View {
@EnvironmentObject var appState: AppState
@State var languages: [String: String] = [:]
@State var currentLanguage: String? = ""
var body: some View {
VStack(alignment: .leading) {
GroupBox(label: Text("AppleID")) {
@ -19,6 +20,23 @@ struct GeneralPreferencePane: View {
GroupBox(label: Text("Notifications")) {
NotificationsView().environmentObject(appState)
}
.groupBoxStyle(PreferencesGroupBoxStyle())
Divider()
GroupBox(label: Text("Language")) {
Picker("", selection: $currentLanguage) {
ForEach(languages.values.sorted(), id: \.self) { language in
Text(language)
.tag(language)
}
}
.onChange(of: currentLanguage!) { newLanguage in
if let langKey = languages.first(where: { $0.value == newLanguage })?.key {
changeAppLanguage(to: langKey)
}
}
}
.groupBoxStyle(PreferencesGroupBoxStyle())
Divider()
@ -27,6 +45,26 @@ struct GeneralPreferencePane: View {
}
.groupBoxStyle(PreferencesGroupBoxStyle())
}
.onAppear {
languages = getLocalizedLanguages()
currentLanguage = languages[Current.defaults.string(forKey: "appLanguage") ?? "en"]
}
}
func getLocalizedLanguages() -> [String : String] {
let langIds = Bundle.main.localizations
var languages = [String:String]()
for langId in langIds {
let loc = Locale(identifier: langId)
if let name = loc.localizedString(forLanguageCode: langId) {
languages[langId] = name
}
}
return languages
}
func changeAppLanguage(to languageCode: String) {
self.appState.appLanguage = languageCode
}
}

View file

@ -15,6 +15,8 @@ struct XcodesApp: App {
MainWindow()
.environmentObject(appState)
.environmentObject(updater)
.environment(\.locale, Locale(identifier: Current.defaults.string(forKey: "appLanguage") ?? "en"))
// .environment(\.layoutDirection, Current.defaults.string(forKey: "appLanguage") ?? "en" == "fa" ? .rightToLeft : .leftToRight)
// This is intentionally used on a View, and not on a WindowGroup,
// so that it's triggered when an individual window's phase changes instead of all window phases.
// When used on a View it's also invoked on launch, which doesn't occur with a WindowGroup.
@ -73,6 +75,8 @@ struct XcodesApp: App {
PreferencesView()
.environmentObject(appState)
.environmentObject(updater)
.environment(\.locale, Locale(identifier: Current.defaults.string(forKey: "appLanguage") ?? "en"))
.environment(\.layoutDirection, Current.defaults.string(forKey: "appLanguage") ?? "en" == "fa" ? .rightToLeft : .leftToRight)
.alert(item: $appState.presentedPreferenceAlert, content: { presentedAlert in
alert(for: presentedAlert)
})
@ -81,6 +85,9 @@ struct XcodesApp: App {
Window("Platforms", id: "platforms") {
PlatformsListView()
.environmentObject(appState)
.environmentObject(updater)
.environment(\.locale, Locale(identifier: Current.defaults.string(forKey: "appLanguage") ?? "en"))
.environment(\.layoutDirection, Current.defaults.string(forKey: "appLanguage") ?? "en" == "fa" ? .rightToLeft : .leftToRight)
.alert(item: $appState.presentedPreferenceAlert, content: { presentedAlert in
alert(for: presentedAlert)
})