现在很多App都有提供多种语言,能够让用户选择自己最熟悉的语言不仅会极大提高用户体验,而且还会提升App的质量。本文将会完整的介绍怎么为一个App添加多种语言支持,并且无需重启系统就可以实现应用内切换语言版本,但是不会详细介绍每一种本地化表达式的规则。
添加语言
在Project Navigation中,点击PROJECT,选择Info可以在Localizations中进行语言的添加。
点击+,选择需要添加的语言。这里就是告诉iOS我们的App会支持哪些语言。
创建本地化文本映射字符串文件
在iOS中,系统是通过查找键值对的方式实现本地化的,这个映射文件就是字符串文件,类型是.strings
,格式如下:
// en
apple = "Apple";
// zh
apple = "苹果";
右键项目目录或command+N创建文件,选择Strings File,命名为Localizable. strings。
选中目录中Localizable. strings,点击右边的按钮Localize…,选择生成Localizable. strings对应的语言文件。由于我这里已经生成好了,不方便截图,直接看最后的生成结果:
然后在对应的文件中创建需要本地化的文本键值对了,注意每一行都要;
结束,最后一行也是。
如果en环境的key和value是一样的,那么en的字符串文件内容可以为空。其实到了这一步,我们的App已经完成了本地化支持了,是跟随系统的语言自动适配。
实现应用内切换
如果我们的App有一个设置页面,里面有一个切换语言的功能,我们希望通过切换语言来实时的改变UI上的文本语言,那现在还远远不够。
@EnvironmentObject
@EnvironmentObject
是SwiftUI的一个属性包装器,用于在整个视图层中传递一个遵循ObservableObject
协议的对象来共享数据,在这个对象中,如果一个属性用@Published
来修饰,当这个属性值发生变化时,所有依赖这个属性的视图都会收到通知并重新渲染。
我们可以通过这个配合修改视图的Locale
环境变量来达到应用内动态的切换语言的目的。先定义一个AppState类,用来保存App的状态:
final class AppState: ObservableObject {
@Published var localeIdentifier: String = "en-US"
init() {
// 获取系统的Locale
var id = Locale.current.identifier;
if let identifier = UserDefaults.standard.string(forKey: "locale_identifier") {
// 使用用户上次的设置。如果用户有选择语言,就要保存起来
id = identifier
}
if id.hasPrefix("zh") {
_localeIdentifier = Published(initialValue: "zh-CN")
}
}
}
初始化App时创建AppState
,并使用.environmentObject()
保存起来,以便其他View通过@EnvironmentObject
获取使用:
@main
struct DingsApp: App {
@StateObject private var appState = AppState()
var body: some Scene {
WindowGroup {
ContentView()
// ContentView()已经加了Locale环境,这个视图就本地化了
.environment(.locale, Locale(identifier: appState.localeIdentifier))
.environmentObject(appState)
}
}
}
如果我们需要对MainView本地化,就需要增加属性AppState,并使用它的localeIdentifier
属性来设置视图的environment,这样当localeIdentifier
改变了,视图的locale也会变化,视图就会使用新的locale来渲染:
struct MainView: View {
@EnvironmentObject private var appState: AppState
var body: some View {
VStack {
...
}
.environment(.locale, Locale(identifier: appState.localeIdentifier))
}
}
切换语言功能
当用户选择不同的语言时,修改AppState的localeIdentifier
属性值,并保存到用户数据中:
Button {
select(for: "zh-Hans")
}
private func select(for language: String) {
let localeIdentifier = (language == "zh-Hans") ? "zh-CN" : "en-US"
appState.localeIdentifier = localeIdentifier
UserDefaults.standard.set(localeIdentifier, forKey: "locale_identifier")
}
其他
有一些地方比较特殊,比如通过函数取得的字符串key,数组里面取的字符串key等就无法本地化,例如:
private let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"]
// 无法本地化
Text(months[index])
这个时候我们可以通过拓展String,增加一个方法,来手动调用获取不同环境下的值。
extension String {
func localizedString(identifier: String = "en-US") -> String {
let language = identifier == "zh-CN" ? "zh-Hans" : "en"
if let path = Bundle.main.path(forResource: language, ofType: "lproj") {
if let bundle = Bundle(path: path) {
return bundle.localizedString(forKey: self, value: self, table: nil)
}
}
return self
}
}
然后在调用String的localizedString
方法就可以了。
Text(months[index].localizedString(identifier: appState.localeIdentifier))
另外,还有Date的格式化也不是使用视图的environment,而是使用Text的format,所以对于Date可以这样处理:
Date().formatted(Date.FormatStyle(date: .abbreviated, time: .omitted, locale: Locale(identifier: appState.localeIdentifier)))
好了,就这些了,现在我们的App应该可以很好的实现应用内切换语言了。谢谢支持,原创不易,转载请注明来源。