This commit is contained in:
Morteza Gharedaghi 2025-09-17 18:01:04 -06:00 committed by GitHub
commit 3a0feb445c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 64 additions and 10 deletions

View file

@ -807,6 +807,7 @@
nl,
pl,
ar,
fa,
);
mainGroup = CAD2E7952449574E00113D76;
packageReferences = (
@ -1173,7 +1174,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;
@ -1223,10 +1224,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;
@ -1372,12 +1373,12 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
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 = 32;
DEVELOPMENT_ASSET_PATHS = "\"Xcodes/Preview Content\"";
DEVELOPMENT_TEAM = ZU6GR6B2FY;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = Xcodes/Resources/Info.plist;
@ -1401,12 +1402,12 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
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 = 32;
DEVELOPMENT_ASSET_PATHS = "\"Xcodes/Preview Content\"";
DEVELOPMENT_TEAM = ZU6GR6B2FY;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = Xcodes/Resources/Info.plist;
@ -1430,7 +1431,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

@ -74,7 +74,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?
@ -139,6 +139,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)
})