diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index 79e173b8..337fc584 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -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)", diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index b4775496..b1f93fc9 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -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] = [] diff --git a/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift b/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift index b15f5c6d..511528cd 100644 --- a/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift @@ -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 } } diff --git a/Xcodes/XcodesApp.swift b/Xcodes/XcodesApp.swift index a7835dd6..20dd850e 100644 --- a/Xcodes/XcodesApp.swift +++ b/Xcodes/XcodesApp.swift @@ -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) })