Açıklama Yok

AppLocalization.swift 3.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. //
  2. // AppLocalization.swift
  3. // App for Indeed
  4. //
  5. // English-first localization using Localizable.strings (same pattern as the LinkedIn app).
  6. // Add more locales by creating `<locale>.lproj/Localizable.strings` and extending AppLanguage.
  7. //
  8. import Foundation
  9. enum AppLanguage: CaseIterable {
  10. case english
  11. case arabic
  12. case chineseSimplified
  13. var localeIdentifier: String {
  14. switch self {
  15. case .english:
  16. return "en"
  17. case .arabic:
  18. return "ar"
  19. case .chineseSimplified:
  20. return "zh-Hans"
  21. }
  22. }
  23. static var systemLanguage: AppLanguage {
  24. let preferred = Locale.preferredLanguages.first ?? "en"
  25. if preferred.lowercased().hasPrefix("zh") {
  26. return .chineseSimplified
  27. }
  28. for language in AppLanguage.allCases where preferred.hasPrefix(language.localeIdentifier) {
  29. return language
  30. }
  31. return .english
  32. }
  33. /// Settings language picker labels — fixed English names, not localized.
  34. var localizedDisplayName: String {
  35. switch self {
  36. case .english:
  37. return "English"
  38. case .arabic:
  39. return "Arabic"
  40. case .chineseSimplified:
  41. return "Chinese (Simplified)"
  42. }
  43. }
  44. }
  45. func appLocalized(_ key: String, language: AppLanguage) -> String {
  46. guard let path = Bundle.main.path(forResource: language.localeIdentifier, ofType: "lproj"),
  47. let bundle = Bundle(path: path) else {
  48. return key
  49. }
  50. return bundle.localizedString(forKey: key, value: key, table: nil)
  51. }
  52. func currentAppLanguage() -> AppLanguage {
  53. let code = UserDefaults.standard.string(forKey: "com.appforindeed.preferredLanguage") ?? "en"
  54. return AppLanguage.allCases.first(where: { $0.localeIdentifier == code }) ?? .english
  55. }
  56. /// Resolves copy for the user’s currently selected language.
  57. func L(_ key: String) -> String {
  58. appLocalized(key, language: currentAppLanguage())
  59. }
  60. /// Localized CV template title; `name` is always the English localization key.
  61. func localizedTemplateName(_ nameKey: String) -> String {
  62. L(nameKey)
  63. }
  64. @MainActor
  65. final class AppLanguageManager {
  66. static let shared = AppLanguageManager()
  67. static let didChangeNotification = Notification.Name("AppLanguageManager.didChange")
  68. private enum UserDefaultsKey {
  69. static let preferredLanguage = "com.appforindeed.preferredLanguage"
  70. }
  71. var current: AppLanguage {
  72. currentAppLanguage()
  73. }
  74. func applyStoredPreferenceOnLaunch() {
  75. // UI copy uses `appLocalized`; do not drive layout from `AppleLanguages` (Arabic forces RTL and breaks CV templates).
  76. UserDefaults.standard.removeObject(forKey: "AppleLanguages")
  77. if UserDefaults.standard.string(forKey: UserDefaultsKey.preferredLanguage) == nil {
  78. setLanguage(AppLanguage.systemLanguage, notify: false)
  79. }
  80. }
  81. func setLanguage(_ language: AppLanguage, notify: Bool = true) {
  82. let code = language.localeIdentifier
  83. UserDefaults.standard.set(code, forKey: UserDefaultsKey.preferredLanguage)
  84. UserDefaults.standard.removeObject(forKey: "AppleLanguages")
  85. if notify {
  86. NotificationCenter.default.post(name: Self.didChangeNotification, object: self)
  87. }
  88. }
  89. func setLanguage(code: String, notify: Bool = true) {
  90. guard let language = AppLanguage.allCases.first(where: { $0.localeIdentifier == code }) else { return }
  91. setLanguage(language, notify: notify)
  92. }
  93. }