SwiftPM本地化方案探索

示例代码🔗https://github.com/gewill/BlogCodes/tree/main/Localizable%20in%20SwiftPM

在处理SwiftPM中本地化时,尝试了几种方案。先说结论Rswift preferredLanguage方案最佳。

方案一:Local

在SwiftUI中使用local可行,但是在SwiftPM会被宿主应用中覆写。不过也是小问题,只要命名规范,按照模块页面功能前缀来的话,一般也不会出现key重复的问题。

这里也是用到了Rswift自动生成的key,避免复制粘贴字符串类型的key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// SwiftUI view			
Section {
Text("Change locale").font(.title)
Text("Will be overwrite by host app!").foregroundColor(.pink)
Button(action: {
viewModel.locale = Locale(identifier: Language.en.rawValue)
}, label: {
Text("Change locale english")
})
Button(action: {
viewModel.locale = Locale(identifier: Language.zh_Hans.rawValue)
}, label: {
Text("Change locale chinese simplified")
})
Text(LocalizedStringKey(R.string.localizable.hello_world.key.description))
} header: {
Text("Change locale")
}
.environment(\.locale, viewModel.locale)

方案二:Rswift preferredLanguage

目前是比较完善的方案。配合 AppLocale 可以全局切换语言。

利用Rswift可处理key和bundle的问题,还优化了SwiftUI.Text的使用体验,直接使用init即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class AppLocale {
let preferredLanguage = CurrentValueSubject<Language, Never>(.en)
var preferredString: _R.string {
R.string(preferredLanguages: [preferredLanguage.value.rawValue])
}

static var shared = AppLocale()
private init() {}
}

// SwiftUI view
Section {
Text("Preferred Languages \(viewModel.preferredLanguage.displayTitle)")
Picker("Preferred Languages", selection: $viewModel.preferredLanguage) {
ForEach(Language.allCases) {
Text($0.displayTitle)
}
}
.pickerStyle(.segmented)
Text(AppLocale.shared.preferredString.localizable.hello_world)
} header: {
Text("Change R.string Preferred Languages")
}

最轻量级集成方式在ViewModel订阅AppLocale.shared.preferredLanguage,更新self.objectWillChange.send(),即可响应语言切换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class HomeViewModel: ObservableObject {
init() {
AppLocale.shared.preferredLanguage
.removeDuplicates()
.sink(receiveValue: { _ in
guard let self else { return }
self.objectWillChange.send()
})
.store(in: &cancelables)
}
}

struc HomeView: View {
@StateObject var viewModel = HomeViewModel()

var body: some View {
Text(AppLocale.shared.preferredString.localizable.hello_world)
}
}

方案三:liamnichols / xcstrings-tool

可用,但是仅支持 iOS16+。有个小坑SwiftPM集成时,官方教程的有错误,正确的git地址为:

1
2
3
4
// 1. Add the xcstrings-tool Package dependency
.package(url: "https://github.com/liamnichols/xcstrings-tool.git", from: "0.1.0")
// 2. Or use the repo is essentially a mirror of the main repository however the xcstrings-tool command line interface is a binary dependency that significantly simplifies your build graph and improves compile times.
.package(url: "https://github.com/liamnichols/xcstrings-tool-plugin.git", from: "0.1.0")

具体参考官方的示例:https://github.com/liamnichols/xcstrings-tool-demo