一些小型的工具类应用还是比较推荐支持多语言的,毕竟语言是使用软件的最大门槛。如果没有掌握语言,使用这个应用仅靠着图形界面来说除非是常用应用,否则根本不会使用。特别对于年纪较大的人来说更不友好。如何使用SwiftUI的情况下还能够尽兴本地化呢?

起始项目

我这里创建了一个项目,进行了一个基本布局。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct ContentView: View {
@State var name = "小明"
@State var appleCount = 2
@State var orangeCount = 1

var body: some View {
VStack {
Text("你好世界")
Text("\(name)!我们都有什么?")
Text("我们有 \(appleCount) 苹果")
Text("并且有 \(orangeCount) 个橘子")
}
}
}

初始布局

我们的目标是将页面进行本地化,支持英文。

项目多语言

我们需要告诉程序我们支持什么语言。

添加项目语言

新建语言文件

我们创建一个语言文件

新建语言文件

创建Strings

命名为Localizable.strings

重命名并勾选文件的适用范围

分配语言

我们需要给语言文件分配一个语言

本地化语言选项

然后勾选所有语言

选择语言

之后我们就可以在左侧的项目文件管理中看到两个语言文件了

语言文件

编辑语言文件

语言文件编写规则很简单。

1
"你好世界" = "Hello,world";

预览多语言

我们可以添加预览结构的语言修饰符

1
.environment(\.locale, .init(identifier: "en"))

语言已经改变

除了这种方式,我们还可以在虚拟机测试时更改语言

编辑虚拟机

更改语言

插入变量的文本如何转换

我们先解决小明的问题。这段文本我们插入了一个name的变量

1
2
3
@State var name = "小明"
//...
Text("\(name)!我们都有什么?")

用直接替换的传统方式显然行不通,因为我们不可能将所有的情况都写进多语言文件里。

所以我们需要在语言文件中用字符串格式说明符来插入变量。因为小明是一个字符串,我们可以使用%@来代替。例如这段文字我们可以写成:

1
2
"小明" = "XiaoMing";
"%@!我们都有什么?" = "%@! What do we have?";

第二行文字已经改变

我们可以发现小明并没有发生改变,这是因为变量的值我们需要进行一些改进。根据如何在 SwiftUI 中将 LocalizedStringKey 更改为 String我们需要添加一段扩展,具体原理可以参考原文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
extension LocalizedStringKey {
var stringKey: String {
let description = "\(self)"

let components = description.components(separatedBy: "key: \"")
.map { $0.components(separatedBy: "\",") }

return components[1][0]
}
}

extension String {
static func localizedString(for key: String,
locale: Locale = .current) -> String {

let language = locale.languageCode
let path = Bundle.main.path(forResource: language, ofType: "lproj")!
let bundle = Bundle(path: path)!
let localizedString = NSLocalizedString(key, bundle: bundle, comment: "")

return localizedString
}
}

extension LocalizedStringKey {
func stringValue(locale: Locale = .current) -> String {
return .localizedString(for: self.stringKey, locale: locale)
}
}

原文给到的用例如下

1
2
3
4
5
6
7
8
let localizedKey = LocalizedStringKey("KEY_NAME_HERE")

print(localizedKey.stringKey)
//prints `KEY_NAME_HERE`

print(localizedKey.stringValue())
// prints Localized value of `KEY_NAME_HERE`
// DOESNT print `KEY_NAME_HERE`

这本篇文章中,我们可以改变一下小明的变量。

小明变成了英文

单数与复数的变化

在中文一般不会出现复数变化,但是英文中就比较常见了(还有一些其他的语言)。这个时候我们当然可以用数量判断来进行,但是这样做不仅麻烦而且还有代码量影响阅读。我们可以通过添加一个Stringsdict文件来进行调整。

新建一个Stringsdict文件,我们可以将它命名为Plurals.stringsdict

Stringsdict文件

在本文的项目中我们只针对英文有单复数变化,所以在语言中我们选择英文。

绑定英文语言

如果不使用复数形式,原文中是我们有 \(appleCount) 苹果,appleCount是Int类型,我们可以知道使用的是%lld来进行表示,即%lld 苹果。

1
"我们有 %lld 苹果" = "We have %lld apples"

但是涉及到复数问题我们就不能在这里填写了,就需要在Plurals.stringsdict表里添加这种复数情况

Localized String Key更改为形式我们有 %lld 苹果

Localized Format Key的值为我们需要变复数的词

VARIABLE更改为我们需要变复数的词

NSStringFormatValueTypeKey在本文中为lld

在下方的数值中我们只保留oneother

对应的值填写对应的形式

Stringsdict文件内容

之后我们需要更改字符串的tableName值,在原来的UI代码中:

1
Text("我们有 \(appleCount) 苹果")

添加tableName属性

1
Text("我们有 \(appleCount) 苹果",tableName: "Plurals")

Plurals为我们的文件名

苹果是两个的情况

苹果是1个的情况

参考文献

Localizing Strings That Contain Plurals

SwiftUI Tutorial on Localization

String Format Specifiers

LocalizedStringKey