我如何使用Swift在程序中进行区域设置更改?
在Swift中,有一种方法可以实时改变本地化语言,你可以给String添加一个扩展函数:
extension String {
func localized(lang: String) -> String {
let path = NSBundle.mainBundle().pathForResource(lang, ofType: "lproj")
let bundle = NSBundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}
}
Swift 4版本:
extension String {
func localized(_ lang: String) -> String {
let path = Bundle.main.path(forResource: lang, ofType: "lproj")
let bundle = Bundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}
}
假设你已经按照正常的方式设置了Localizable.strings文件,并分别放在lang_id.lproj(例如en.lproj, de.lproj等)文件夹中,你可以在任何需要的地方使用这个函数:
var val = "MY_LOCALIZED_STRING".localized("de")
恭喜,这是我找到的迄今为止最好的解决方案。我刚刚进行了一些修改,以便能够动态地改变语言。我将它作为一个答案发布。
但是如何在改变语言后重新加载Storyboard呢?目前,当我改变语言时,使用.localized()的字符串可以正常显示,但是Storyboard没有反映出选择的语言。
嗨Hardik,在这种情况下,你的Storyboard标签需要从代码中设置,并且每次语言改变时都要运行该代码。
这个答案并没有回答问题,问题是关于本地语言的Storyboard,它只是一个将字符串翻译的方法。
如何使用Swift以编程方式更改区域设置
在我的情况下,如果用户更改了语言设置,我必须在运行时更新两个东西。
1. Localizable.strings
2. Storyboard本地化
我使Pang代码更加Swifty。
首先,创建一个名为BundleExtension.swift的文件,并在其中添加以下代码:
import UIKit
private var bundleKey: UInt8 = 0
final class BundleExtension: Bundle {
override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {
return (objc_getAssociatedObject(self, &bundleKey) as? Bundle)?.localizedString(forKey: key, value: value, table: tableName) ?? super.localizedString(forKey: key, value: value, table: tableName)
}
}
extension Bundle {
static let once: Void = { object_setClass(Bundle.main, type(of: BundleExtension())) }()
static func set(language: Language) {
Bundle.once
let isLanguageRTL = Locale.characterDirection(forLanguage: language.code) == .rightToLeft
UIView.appearance().semanticContentAttribute = isLanguageRTL == true ? .forceRightToLeft : .forceLeftToRight
UserDefaults.standard.set(isLanguageRTL, forKey: "AppleTe zxtDirection")
UserDefaults.standard.set(isLanguageRTL, forKey: "NSForceRightToLeftWritingDirection")
UserDefaults.standard.set([language.code], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
guard let path = Bundle.main.path(forResource: language.code, ofType: "lproj") else {
log(.error, "Failed to get a bundle path.")
return
}
objc_setAssociatedObject(Bundle.main, &bundleKey, Bundle(path: path), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
然后,创建一个名为Language.swift的文件,并在其中添加以下代码:
import Foundation
enum Language: Equatable {
case english(English)
case chinese(Chinese)
case korean
case japanese
enum English {
case us
case uk
case australian
case canadian
case indian
}
enum Chinese {
case simplified
case traditional
case hongKong
}
}
extension Language {
var code: String {
switch self {
case .english(let english):
switch english {
case .us: return "en"
case .uk: return "en-GB"
case .australian: return "en-AU"
case .canadian: return "en-CA"
case .indian: return "en-IN"
}
case .chinese(let chinese):
switch chinese {
case .simplified: return "zh-Hans"
case .traditional: return "zh-Hant"
case .hongKong: return "zh-HK"
}
case .korean: return "ko"
case .japanese: return "ja"
}
}
var name: String {
switch self {
case .english(let english):
switch english {
case .us: return "English"
case .uk: return "English (UK)"
case .australian: return "English (Australia)"
case .canadian: return "English (Canada)"
case .indian: return "English (India)"
}
case .chinese(let chinese):
switch chinese {
case .simplified: return "简体中文"
case .traditional: return "繁體中文"
case .hongKong: return "繁體中文 (香港)"
}
case .korean: return "한국어"
case .japanese: return "日本語"
}
}
}
extension Language {
init?(languageCode: String?) {
guard let languageCode = languageCode else { return nil }
switch languageCode {
case "en", "en-US": self = .english(.us)
case "en-GB": self = .english(.uk)
case "en-AU": self = .english(.australian)
case "en-CA": self = .english(.canadian)
case "en-IN": self = .english(.indian)
case "zh-Hans": self = .chinese(.simplified)
case "zh-Hant": self = .chinese(.traditional)
case "zh-HK": self = .chinese(.hongKong)
case "ko": self = .korean
case "ja": self = .japanese
default: return nil
}
}
}
要使用这些代码,可以按以下方式调用:
var languages: [Language] = [.korean, .english(.us), .english(.uk), .english(.australian), .english(.canadian), .english(.indian), .chinese(.simplified), .chinese(.traditional), .chinese(.hongKong), .japanese]
Bundle.set(language: languages[indexPath.row].language)
然而,"Locale.current.languageCode"总是返回系统设置的语言。因此,我们必须使用"Locale.preferredLanguages.first"。然而,返回的值看起来像"ko-US"。这是一个问题!因此,我创建了LocaleManager来仅获取语言代码。
创建一个名为LocaleManager.swift的文件,并在其中添加以下代码:
import Foundation
struct LocaleManager {
/// "ko-US" → "ko"
static var languageCode: String? {
guard var splits = Locale.preferredLanguages.first?.split(separator: "-"), let first = splits.first else { return nil }
guard splits.count > 1 else { return String(first) }
splits.removeLast()
return String(splits.joined(separator: "-"))
}
static var language: Language? {
return Language(languageCode: languageCode)
}
}
要使用这些代码,可以按以下方式调用:
guard let languageCode = LocaleManager.languageCode, let title = RemoteConfiguration.shared.logIn?.main?.title?[languageCode] else {
return NSLocalizedString("Welcome!", comment: "")
}
return title
希望这个答案能被标记为正确!
问题:如何用Swift编程方式更改语言环境?
原因:想要通过更新UserDefaults键来更改语言。
解决方法:
1. 创建一个String的extension,其中包含一个localized的计算属性。这个属性根据UserDefaults中的i18n_language值来返回本地化的字符串。
2. 在使用字符串的地方,通过在字符串后添加.localized来获取本地化的字符串。
3. 若要更改语言,只需通过UserDefaults设置i18n_language的值为目标语言即可。
然而,目前在更改语言后,字符串的本地化工作正常,但是Storyboard并未更改。为了解决这个问题,有人提供了一个解决方案,即在名为"Settings"的视图控制器中更改语言,然后关闭该视图控制器,这样所有的标签和按钮就会实时更改语言。此外,还有其他一些方法,例如在需要更改语言的视图控制器中调用一个updateStrings()方法来重置字符串,但这可能不是一个理想的解决方案。
需要注意的是,不应该使用synchronize()方法来同步UserDefaults,因为据苹果文档所述,这个方法已经过时了。