Swift 库,以一种不同、更好、更简单、更强大的方式,帮助在系统本地化之外本地化应用程序。它使用 JSON 文件而不是字符串文件。
Swifternalization 以更智能的方式帮助本地化应用程序。它是由于解决波兰语内部化问题而创建的,但它具有通用性且与每种语言都很好地协同工作。
它使用 JSON 文件和表达式来避免编写代码处理您在未使用此框架时必须编写的某些情况,从而简化本地化过程。
实用使用示例的描述将使用在本文档后面介绍的内容,因此请继续阅读直至文章结束,然后阅读这里呈现的详细/特性。
假设此应用支持英语和波兰语。自然而然,应用包含两个 Localizable.strings 文件。一个是用于 Base 本地区化的,其中包含 英语 翻译,另一个是 波兰语。
应用显示包含信息的标签,指示最近一次更新后端对象的最后时间,例如:“2分钟前”,“3小时前”,“1分钟前”等。
标签显示数量和一个表示小时/分钟/秒的词(单数或复数形式)以及“之前”的词缀。不同语言在可数数词的复数和基数表达中有不同的处理方式。这里我们需要支持英语和波兰语。
在英语中,针对每个小时/分钟/秒的词有两个情况需要处理
在波兰语中这更加棘手,因为基数数词更加复杂
以下章节将介绍没有Swifternalization框架和有Swifternalization框架的解决方案。每个解决方案都描述了Base(英语)和波兰语的本地化。
以下是一个表格,其中包含了语言复数规则,覆盖了许多语言的基数数词形式 - 许多语言以其特有的方式处理复数。
Localizable.strings (Base)
--------------------------
"one-second" = "1 second ago";
"many-seconds" = "%d seconds ago";
"one-minute" = "1 minute ago";
"many-minutes" = "%d minutes ago";
"one-hour" = "1 hour ago";
"many-hours" = "%d hours ago";
Localizable.strings (Polish)
-------------------------
"one-second" = "1 sekundę temu"
"few-seconds" = "%d sekundy temu"
"many-seconds" = "%d sekund temu""
"one-minute" = "1 minutę temu"
"few-minutes" = "%d minuty temu "
"many-minutes" = "%d minut temu"
"one-hours" = "1 godzinę temu"
"few-hours" = "%d godziny temu"
"many-hours" = "%d godzin temu";
英语中有6种情况需要处理,波兰语中有9种情况。注意,如果没有额外的逻辑处理,我们无法检测出应用应该显示的小时/分钟/秒字符串的哪个版本。不同语言的逻辑不同。我们需要在应用中使用的每种语言中添加一些处理逻辑的代码。如果有超过两种语言怎么办?不用想太多——这可能并不容易。
这种逻辑已经在Swifternalization框架中实现,并且适用于所有语言。
以下是这些可本地化文件的示例
base.json
---------
"time-seconds": {
"one": "%d second ago"
"other": "%d seconds ago"
},
"time-minutes": {
"one": "%d minute ago"
"other": "%d minutes ago"
},
"time-hours": {
"one": "%d hours ago"
"other": "%d hours ago"
}
pl.json
-------
"time-seconds": {
"one": "1 sekundę temu",
"few": "%d sekundy temu",
"many": "%d sekund temu"
},
"time-minutes": {
"one": "1 minutę temu",
"few": "%d minuty temu",
"many": "%d minut temu"
},
"time-hours": {
"one": "1 godzinę temu",
"few": "%d godziny temu",
"many": "%d godzin temu"
}
正如所提到的,逻辑已经集成到框架中,因此如果你想获得某个本地化字符串的值,你必须进行一个简单的调用。
Swifternalization.localizedString("time-seconds", intValue: 10)
或者使用I18n
类型别名 (I-18-letters-n,内部化)
I18n.localizedString("time-seconds", intValue: 10)
由加载的表达式和正确的字符串版本进行验证的键和intValue参数,下面将进行介绍。
Swifternalization消除了使用如以下所示的stringdicts文件的需要来支持本地化字符串中的复数化。相反,你可以简单地定义覆盖此类情况的表示法。
<plist version="1.0">
<dict>
<key>%d file(s) remaining</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@files@</string>
<key>files</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d file remaining</string>
<key>other</key>
<string>%d files remaining</string>
</dict>
</dict>
</dict>
</plist>
不再需要stringsdict文件!
苹果iOS 9提供了根据屏幕宽度选择合适的本地化字符串变体的新方法。它使用带有NSStringVariableWidthRuleType键的stringsdict文件。
Swifternalization消除了使用此类文件的需要,并且不需要使用此新键即可使用该功能。
Swifternalization自iOS 8.0以来就支持长度变化功能,因为该框架有自己的实现。
要使用长度变化功能,您的翻译文件应该包含如下条目
base.json
---------
"forgot-password": {
"@200": "Forgot Password? Help.",
"@300": "Forgot Your Password? Use Help.",
"@400": "Do not remember Your Password?" Use Help.""
}
符号“@”后面的数字是屏幕或字符串可适应的最大宽度。例如,如果传递的适应宽度作为参数大于200且小于等于300,则将返回第二个字符串。
要获取第二个本地化字符串,调用方式如下
I18n.localizedString("forgot-password", fittingWidth: 300) // 201 - 300
您可以混合表达式与长度变化。以下示例显示了如何进行操作
base.json
---------
"car": {
"ie:x=1": {
@100: "One car",
@200: "You've got one car"
},
"more": "You've got few cars"
}
有几个表达式类型。每种表达式类型都有自己的解析器和匹配器,但它们在内部工作,所以您不需要担心它们。
有3种表达式类型
它由几个元素组成
示例
"cars": {
"ie:x=1": "1 car",
"ie:x=0": "no cars",
"ie:x>1": "%d cars"
}
这是扩展不等式表达式的版本。它由两个值组成,一个标值和一个两个不等式符号。
示例
"tomatos": {
"iex:2<x<10": "%d tomatos is between 2 and 10"
}
这是最强大的表达式类型。它需要正则表达式;)
示例:(波兰语的警车)
"police-cars": {
"exp:^1$": "1 samochód policyjny",
"exp:(((?!1).[2-4]{1})$)|(^[2-4]$)": "%d samochody policyjne",
"exp:(.*(?=1).[0-9]$)|(^[05-9]$)|(.*(?!1).[0156789])": "%d samochodów policyjnych"
}
功能强大,不是吗?:>
PS. 有内置的波兰语解决方案,所以您可以通过以下方式使用它
"police-cars": {
"one": "1 samochód policyjny",
"few": "%d samochody policyjne",
"many": "%d samochodów policyjnych"
}
这被称为“共享内置表达式”,下面会介绍。
共享表达式是所有本地化文件中可用的表达式。它们在expressions.json文件中根据语言进行声明,您可以在本地化文件中使用这些表达式。
该功能允许开发者遵循DRY原则,并避免因在多个地方重复代码而产生的错误。
通常,您可以这样声明表达式
...
"ie:x>1": "Localized String"
...
如果您希望在同一文件中重复使用相同的表达式,无需在别处重复该表达式。当您决定改进/更改表达式以处理您忘记的其他情况时,这也可能成为一个问题 - 您将不得不在多个位置更改表达式。正因为如此,才有了共享表达式。这些特性允许您在一个地方创建表达式,并在需要放置该表达式的多个地方使用其标识符。
您需要做的就是创建以下结构的expressions.json文件。
{
"base": {
"one": "ie:x>1"
},
"pl": {
// ... other than "one" because "one" is available here too.
}
}
现在在pl.json、en.json等文件中,您需要如下使用它。
...
"one": "Localized String"
...
在您决定创建自己的表达式之前,查看是否已存在同名的内置表达式或是否有类似的但名称不同的表达式。也许您根本不需要这样做,只需使用它即可。
如名称所示,内置表达式是预建入框架中的共享表达式,无需配置即可使用。它们按国家划分,并非所有国家都有自己的内置表达式。目前,例如有基本内置表达式和波兰内置表达式。基本表达式在所有国家都可用,它们非常通用,可以匹配所有国家的复数/基数数词逻辑。
支持的内置共享表达式列表
Base (English fits to this completely)
- one - detects if number is equal 1
- >one - detects if number is greater than 1
- two - detects if number is equal 2
- other - detects if number is not 1, so 0, 2, 3 and so on.
Polish
- few - matches (2-4), (22-24), (32-4), ..., (..>22, ..>23, ..>24)
- many - matches 0, (5-9), (10-21), (25-31), ..., (..0, ..1, ..5-9)
如您所见,由于默认继承自基本类型,波兰没有“one”、“>one”等。
本章向您展示如何开始使用Swifternalization以及如何将其与您的代码集成。
文档涵盖了100%的代码,太好了!有两种类型的文档。第一种仅涵盖公开API,这是为那些只想使用框架而不想深入了解的人准备的。第二种涵盖了所有的API - 公开、内部和私有。
您可以在docs目录中找到公共API和完整文档。
它还托管在我的博客上http://szulctomasz.com。
文档集
它与iOS 8.0和更高版本兼容。
使用CocoaPods
pod 'Swifternalization', '~> 1.3.1'
如果您不使用CocoaPods,只需将Swifternalization/Swifternalization目录中的文件导入到您的项目中即可。
Swifternalization也支持Carthage。
在您获取第一个本地化字符串之前,您必须通过传递本地化JSON文件所在的捆绑包来配置Swifternalization。如果您不调用configure()
,它将默认使用内部NSBundle.mainBundle()
。
它将从配置它所使用的捆绑包中获得语言。因此,如果它没有翻译您的字符串,请确保您已在项目>信息>本地化部分添加了本地化 - 与常规本地化相同的部分。
I18n.configure(bundle) // if files are in another bundle
共享表达式必须放在expressions.json中。文件的语法看起来如下。
{
"base": {
"ten": "ie:x=10",
">20": "ie:x>20",
"custom-pl-few": "exp:(.*(?=1).[0-9]$)|(^[05-9]$)|(.*(?!1).[0156789])"
},
"pl": {
"few": "exp:(((?!1).[2-4]{1})$)|(^[2-4]$)",
"two": "ie:x=2",
"three": "ie:x=3"
}
}
在伪语言中
{
"language-1": {
"shared-expression-key-1": "expression-1",
"shared-expression-key-2": "expression-2"
},
"language-2": {
"shared-expression-key-1": "expression-1"
}
}
可以从文件中获取表达式并将其用于可本地化文件中。不同语言的共享表达式都放在同一文件中,因为每种语言的表达式数量很少。大部分表达式都定义在 基础 变体中,因为如果在 基础 中,那么在每种其他语言中也可以使用。所以,“十”在“pl”中可用,但“三”在“base”中不可用。
可本地化文件包含特定语言的翻译。文件可能看起来如下
{
"welcome-key": "welcome",
"cars": {
"one": "one car",
"ie:x>=2": "%d cars",
"ie:x<=-2": "minus %d cars"
}
}
文件的名称应与国家代码相同。例如,对于英语,它是 en.json,对于波兰语,它是 pl.json,对于基础本地化,它是 base.json 等。
你可以在这种文件中放置一些内容。更复杂的文件如下所示
{
"welcome": "welcome",
"cars": {
"one": {
"@100": "one car",
"@200": "You have one car",
"@400": "You have got one car"
},
"other": "%d cars"
}
}
在伪语言中
{
"key": "value",
"key": {
"expression-1": {
"length-variation-1": "value-1",
"length-variation-2": "value-2",
"length-variation-3": "value-3"
},
"expression-2": "value"
}
}
Swifternalization 允许您使用其一个类方法,该方法公开您用于本地化应用程序所需的所有方法。
这些方法具有许多可选参数,如果您想的话,可以省略它们。有一些常见参数
key
- 本地化字符串的键。fittingWidth
- 您要在其中放置本地化字符串的屏幕或位置的宽度。它是整数。defaultValue
- 如果传递给方法的键没有本地化字符串,将返回的值。如果没有指定,则返回 key
。comment
- 仅由开发人员使用的注释,用以了解翻译的上下文。首次调用方法 localizedString(_:fittingWidth:defaultValue:comment:)
允许您获取简单键的值,而不包含表达式。
示例
I18n.localizedString("welcome")
I18n.localizedString("welcome", fittingWidth: 200)
I18n.localizedString("welcome", defaultValue: "Welcome", comment: "Displayed on app start")
下一个方法 localizedString(_:stringValue:fittingWidth:defaultValue:comment:)
允许您获取与表达式匹配的字符串值的本地化字符串。实际上,字符串值通常包含数字或您想匹配的其他字符串。
I18n.localizedString("cars", stringValue: "5")
// Other cases similar to above example
最后一个方法 localizedString(_:intValue:fittingWidth:defaultValue:comment:)
允许您获取整数值的本地化字符串。此方法调用上述方法,并将整数值转换为字符串,因为所有框架都基于字符串操作。
I18n.localizedString("cars", intValue: 5)
Swifternalization 是开源的,因此如果愿意,任何人都可做出贡献。如果您想为其开发,只需将仓库分支出来,做您的工作并创建拉取请求。如果您有一些想法或问题,请随时创建问题并为它添加适当的标签。
没有为贡献者提供指南,但如果您添加了新功能,您必须为它编写单元测试。
Swifternalization 在 MIT 许可下发布。