在 SwiftUI 中使用字符串插值进行本地化
Localization with String interpolation in SwiftUI
我正在尝试本地化我的 SwiftUI Watch 应用程序。
我对静态字符串没有任何问题。我在我的文本视图中使用 LocalizedKeyStrings,并在 Localizable.strings 文件中添加我的翻译。
例如:
Text("history")
在Localizable.strings中:
"history" = "Historique";
结果:
"Historique"
但我也想使用插值来定位刺。例如:
Text("startCustom \(format: "%.1f",customDistance)")
在Localizable.strings中,我尝试了不同的语法:
"startCustom %@" = "Course de %@ km";
或
"startCustom %f" = "Course de %f km";
或
"startCustom %.1f" = "Course de %.1f km";
没有任何效果。
我没有找到任何相关文件...
我已经构建了这个字符串扩展来管理我的应用程序的本地化。您可以像 "history".localized
一样简单地使用它来获取本地化的字符串。
要应用替换,请使用方法 "my string %@".localized(withSubstitutions: "my substitution")
您可以切片和切块以使用您想要的扩展程序。
我还维护了 2 个常量 let sameInBothLanguages: [String]
和 let needTranslationsFor: [String]
以记录是否存在不应本地化的字符串,因为它们在两种语言中相同或发送到内容翻译团队。
extension String {
var localized: String {
return localized(from: nil)
}
func localized(withSubstitutions substitutions: String...) -> String {
return String(format: self.localized, arguments: substitutions)
}
func localized(from table: String?) -> String {
let translatedString = getTranslatedString(fromTable: table)
// No sense looking up the string in Release builds
#if !DEBUG
return translatedString
#endif
guard Locale.current.languageCode == "en" else {
return translatedString
}
let otherLanguage = "es"
// We can keep adding to this list temporarily in order to make the app actually run. Every so often we will give this list to the content team and empty it once we get the translations back.
let otherLanguageString = getTranslatedString(fromTable: table, inLanguage: otherLanguage)
if otherLanguageString == self &&
!sameInBothLanguages.contains(self) &&
!needTranslationsFor.contains(self) {
//swiftlint:disable:next no_nslocalizedstring
assertionFailure("No Spanish version of localized string found for '\(self)'. Please go to String+SA.swift and add this string to either the 'needTranslationsFor' or 'sameInBothLanguages' array.")
}
return translatedString
}
private func getTranslatedString(fromTable table: String?, inLanguage language: String) -> String {
if let path = Bundle.main.path(forResource: language, ofType: "lproj"),
let otherLanguageBundle = Bundle(path: path) {
let otherLanguageString = getTranslatedString(fromTable: table, andBundle: otherLanguageBundle)
return otherLanguageString
}
return self
}
private func getTranslatedString(fromTable table: String?, andBundle bundle: Bundle = Bundle.main) -> String {
let translatedString = bundle.localizedString(forKey: self, value: self, table: table)
return translatedString
}
}
以下简单的工作(使用 Xcode 11.4 测试)
Text(String(format: NSLocalizedString("startCustom %.1f", comment: ""),
self.customDistance))
和Localizable.string有
"startCustom %.1f" = "Course de %.1f km";
显然,LocalizedStringKey
将根据内插值的类型自动生成本地化密钥。例如,如果您有以下 Text
s
Text("title key")
Text("name key \("Club")")
Text("count key \(8)")
Text("price key \(6.25)")
您的 Localizable.strings 文件应如下所示
"title key" = "Sandwiches";
"name key %@" = "Name: %@";
"count key %lld" = "%lld sandwiches";
// You can change the format specifier in the value, but not in the key.
"price key %lf" = "Price: %.2lf";
如果要支持 32 位系统(iPhone 5 或更早版本),请小心。在32位系统中,Int
是Int32
,"int32 key \(Int32(8))"
的key是"int32 key %d"
。您始终可以将整数转换为 Int64
,就像 "count key \(Int64(8))"
中那样,以在不同系统之间强制执行一致的密钥。
备注1:适合想知道它是如何工作的人。当您在 Text
中使用字符串字面量或内插字符串(例如 "count key \(8)"
)时,编译器会将字符串视为 LocalizedStringKey
,因为 Text
具有初始值设定项 [=29] =]
init(_ key: LocalizedStringKey, tableName: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil),
和LocalizedStringKey
符合ExpressibleByStringLiteral
和ExpressibleByStringInterpolation
,因此可以从字符串文字或字符串插值中隐式初始化。
备注2:如果您不确定密钥是什么,您可以通过在调试器中po一个LocalizedStringKey来自己获得答案,如下所示:
po LocalizedStringKey("count key \(8)")
我的方式
可本地化的文件
"myNameIs %@" = "My name is %@.";
SwiftUI 文件
struct TestLocalize: View {
var name = "Hien Nguyen"
var body: some View {
Text("myNameIs \(name)")
}
}
结果
我叫 Hien Nguyen
我正在尝试本地化我的 SwiftUI Watch 应用程序。 我对静态字符串没有任何问题。我在我的文本视图中使用 LocalizedKeyStrings,并在 Localizable.strings 文件中添加我的翻译。 例如:
Text("history")
在Localizable.strings中:
"history" = "Historique";
结果: "Historique"
但我也想使用插值来定位刺。例如:
Text("startCustom \(format: "%.1f",customDistance)")
在Localizable.strings中,我尝试了不同的语法:
"startCustom %@" = "Course de %@ km";
或
"startCustom %f" = "Course de %f km";
或
"startCustom %.1f" = "Course de %.1f km";
没有任何效果。 我没有找到任何相关文件...
我已经构建了这个字符串扩展来管理我的应用程序的本地化。您可以像 "history".localized
一样简单地使用它来获取本地化的字符串。
要应用替换,请使用方法 "my string %@".localized(withSubstitutions: "my substitution")
您可以切片和切块以使用您想要的扩展程序。
我还维护了 2 个常量 let sameInBothLanguages: [String]
和 let needTranslationsFor: [String]
以记录是否存在不应本地化的字符串,因为它们在两种语言中相同或发送到内容翻译团队。
extension String {
var localized: String {
return localized(from: nil)
}
func localized(withSubstitutions substitutions: String...) -> String {
return String(format: self.localized, arguments: substitutions)
}
func localized(from table: String?) -> String {
let translatedString = getTranslatedString(fromTable: table)
// No sense looking up the string in Release builds
#if !DEBUG
return translatedString
#endif
guard Locale.current.languageCode == "en" else {
return translatedString
}
let otherLanguage = "es"
// We can keep adding to this list temporarily in order to make the app actually run. Every so often we will give this list to the content team and empty it once we get the translations back.
let otherLanguageString = getTranslatedString(fromTable: table, inLanguage: otherLanguage)
if otherLanguageString == self &&
!sameInBothLanguages.contains(self) &&
!needTranslationsFor.contains(self) {
//swiftlint:disable:next no_nslocalizedstring
assertionFailure("No Spanish version of localized string found for '\(self)'. Please go to String+SA.swift and add this string to either the 'needTranslationsFor' or 'sameInBothLanguages' array.")
}
return translatedString
}
private func getTranslatedString(fromTable table: String?, inLanguage language: String) -> String {
if let path = Bundle.main.path(forResource: language, ofType: "lproj"),
let otherLanguageBundle = Bundle(path: path) {
let otherLanguageString = getTranslatedString(fromTable: table, andBundle: otherLanguageBundle)
return otherLanguageString
}
return self
}
private func getTranslatedString(fromTable table: String?, andBundle bundle: Bundle = Bundle.main) -> String {
let translatedString = bundle.localizedString(forKey: self, value: self, table: table)
return translatedString
}
}
以下简单的工作(使用 Xcode 11.4 测试)
Text(String(format: NSLocalizedString("startCustom %.1f", comment: ""),
self.customDistance))
和Localizable.string有
"startCustom %.1f" = "Course de %.1f km";
显然,LocalizedStringKey
将根据内插值的类型自动生成本地化密钥。例如,如果您有以下 Text
s
Text("title key")
Text("name key \("Club")")
Text("count key \(8)")
Text("price key \(6.25)")
您的 Localizable.strings 文件应如下所示
"title key" = "Sandwiches";
"name key %@" = "Name: %@";
"count key %lld" = "%lld sandwiches";
// You can change the format specifier in the value, but not in the key.
"price key %lf" = "Price: %.2lf";
如果要支持 32 位系统(iPhone 5 或更早版本),请小心。在32位系统中,Int
是Int32
,"int32 key \(Int32(8))"
的key是"int32 key %d"
。您始终可以将整数转换为 Int64
,就像 "count key \(Int64(8))"
中那样,以在不同系统之间强制执行一致的密钥。
备注1:适合想知道它是如何工作的人。当您在 Text
中使用字符串字面量或内插字符串(例如 "count key \(8)"
)时,编译器会将字符串视为 LocalizedStringKey
,因为 Text
具有初始值设定项 [=29] =]
init(_ key: LocalizedStringKey, tableName: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil),
和LocalizedStringKey
符合ExpressibleByStringLiteral
和ExpressibleByStringInterpolation
,因此可以从字符串文字或字符串插值中隐式初始化。
备注2:如果您不确定密钥是什么,您可以通过在调试器中po一个LocalizedStringKey来自己获得答案,如下所示:
po LocalizedStringKey("count key \(8)")
我的方式
可本地化的文件
"myNameIs %@" = "My name is %@.";
SwiftUI 文件
struct TestLocalize: View {
var name = "Hien Nguyen"
var body: some View {
Text("myNameIs \(name)")
}
}
结果
我叫 Hien Nguyen