复数 NSLocalizedString 中的字符串参数
String parameter in pluralized NSLocalizedString
我想用适当的复数形式翻译字符串 %@ there are up to %i sun hours
。
%@
包含一天,%i
太阳时数。
这是我的 Localizable.stringsdict
文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>%@ there are up to %i sun hours</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@hours@</string>
<key>hours</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>i</string>
<key>zero</key>
<string>%@ there are no sun hours</string>
<key>one</key>
<string>There is one sun hour %@ </string>
<key>other</key>
<string>%@ there are up to %i sun hours</string>
</dict>
</dict>
</dict>
</plist>
我是这样称呼它的:
[NSString stringWithFormat:NSLocalizedString(@"%@ there are up to %i sun hours", nil), dayString, sunHours];
无论我将什么 NSInteger
作为第二个参数传入,翻译将始终使用 "other" 模板。我将它缩小到 NSStringLocalizedFormatKey
是错误的,当我使用像 %@ %#@hours@
这样的键时它正在工作。
不过,我希望字符串参数成为本地化的一部分。有办法吗?
根据我所做的实验,您需要更改参数的顺序。好像只有第一个可以作为替换规则的控制值。
这本词典
<key>%i hours %@</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@hours@</string>
<key>hours</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>i</string>
<key>zero</key>
<string>%2$@ there are no sun hours</string>
<key>one</key>
<string>There is one sun hour %2$@ </string>
<key>other</key>
<string>%2$@ there are up to %1$d sun hours</string>
</dict>
</dict>
结合
[NSString stringWithFormat:NSLocalizedString(@"%i hours %@", nil), sunHours, dayString];
产生了我预期的结果。请注意,我已将参数索引添加到替换字符串中,以便正确放置值。
看起来文档对此功能的描述过于雄心勃勃。例如,给出的示例 in the document titled "OS X 10.9 Release Notes"(尽管 link 用于 iOS)意味着您应该能够打开第二个参数:
We're allowing the recursive formatting by applying the entire
argument list to each substituted format specifier.
@"%d in %d files are selected" = @"%2$#@d_in_d_files_are_selected@"
The configuration dictionary can contain
"d_in_d_files_are_selected" = {
"NSStringFormatSpecTypeKey" = "NSStringPluralRuleType"; // plural type
"NSStringFormatValueTypeKey" = "d"; // int argument
"zero" = "There is no file";
"one" = "There is a file, and %1$#@it_is_selected@";
"other" = "%1$d in %2$d files are selected";
};
但是根据那里的指南构建字典并没有给出规定的结果。 (请注意紧接着给出的示例 XML 与该词典不匹配。)
可能是我误读了某些内容(或者可能是错误),但我无法弄清楚到底发生了什么。现在,我将其保留为 "changing the argument order will fix your problem".
从 macOS SDK 10.12/Xcode 8.3 开始,这仍然是一个问题,NSStringLocalizedFormatKey
忽略参数编号并使用第一个参数来确定 d_in_d_files_are_selected
的复数。但。复数规则格式中的嵌套编号引用确实有效,例如"one" = "There is one file, and %2$#@it_is_selected@";
将正确使用 select 复数规则的第二个参数。这意味着如果您使用单个 other
规则创建格式代理,则无需重新排列格式字符串即可实现您想要的行为:
<key>%@ there are up to %i sun hours</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@proxy@</string>
<key>proxy</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>i</string>
<key>other</key>
<string>%2$#@hours@</string>
</dict>
<key>hours</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>i</string>
<key>zero</key>
<string>%1$@ there are no sun hours</string>
<key>one</key>
<string>There is one sun hour %1$@ </string>
<key>other</key>
<string>%1$@ there are up to %2$i sun hours</string>
</dict>
</dict>
请注意,要使其正常工作,您必须在最终格式字符串 ("%1$@ there are up to %2$i sun hours"
) 中明确指定参数编号。
在解决了我需要基于第二个 argument/variable 进行复数化的问题之后,我还找到了解决您问题的方法,而无需更改参数的顺序或使用代理规则链。感谢其他发帖者的回答和解决方案,让我进行了实验并更好地理解了 .stringsdict
。
我认为我的情况与您的情况接近,但更容易一些,并且可以帮助您更好地理解如何应用字符串格式和规则,所以让我从我的示例开始展示。
我的案例
我需要构造一个字符串,如 1 of 1 item selected
或 1 of 5 items selected
:
let countWithSelectionFormat = NSLocalizedString("%ld of %ld item(s) selected", comment: "Number of items with selection")
let countString = String.localizedStringWithFormat(countWithSelectionFormat, selectedCount, totalCount)
字符串格式%ld of %ld item(s) selected
在这里只是一个占位符,一个别名。它在 Localizable.strings
中被忽略,因为它首先在 Localizable.stringsdict
中找到。它仅用作Localizable.stringsdict
中规则的键,但实际上并不用于构造字符串:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>%ld of %ld item(s) selected</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%ld of %#@totalItems@ selected</string>
<key>totalItems</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>one</key>
<string>%ld item</string>
<key>other</key>
<string>%ld items</string>
</dict>
</dict>
</dict>
</plist>
实际的字符串格式在NSStringLocalizedFormatKey
中列出,是%ld of %#@totalItems@ selected
。这里第一个参数 selectedCount
是通过使用 %ld
传递的(没有使其成为变量并为其列出规则)。第二个参数 totalCount
用 %#@totalItems@
变量及其规则解析 return 1 item
或例如 5 items
,从而构造正确的字符串。
如果您需要更改某些语言输出中参数的顺序,您可以使用 %2$#@totalItems@, %1$ld selected
作为 NSStringLocalizedFormatKey
。
如果需要,您还可以引入第二个变量(和规则):%2$#@totalItems@, %1$#@selectedItems@
.
你的情况
我的 Swift 代码与 Objective-C 中的基本相同:
let stringFormat = NSLocalizedString("On %@ there are up to %ld sun hours", comment: "")
let string = String.localizedStringWithFormat(stringFormat, dayString, sunHours)
Localizable.stringsdict
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>On %@ there are up to %ld sun hours</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@day@%#@hours@</string>
<key>day</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>other</key>
<string></string>
</dict>
<key>hours</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>zero</key>
<string>On %1$@ there are no sun hours</string>
<key>one</key>
<string>There is one sun hour on %1$@</string>
<key>other</key>
<string>On %1$@ there are up to %2$ld sun hours</string>
</dict>
</dict>
</dict>
</plist>
在 NSStringLocalizedFormatKey
中我使用了 2 个变量:%#@day@%#@hours@
.
day
变量和规则用于'consume'第一个参数dayString
,我们这里不需要任何输出(固定在句首)。我在这里使用了格式值类型 d
(如 %d
中的整数),因为我们不需要并且我们无法解析字符串来选择适用的复数规则。我们只使用必需的 other
规则,其中 return 是一个空字符串。出于同样的原因,字符串格式中的 day
和 hours
变量之间没有 space。
hours
变量捕获第二个参数 sunHours
并且它的规则负责构造实际的输出字符串(在这种情况下是整个句子)。因为我必须从第二个变量的规则中引用两个参数,所以我使用 %1$@
和 %2$ld
分别引用 dayString
和 sunHours
参数。因此,您还可以按任意组合和顺序使用变量。
这给出了预期的结果:
String.localizedStringWithFormat(stringFormat, dayString, 0) // On Monday there are no sun hours
String.localizedStringWithFormat(stringFormat, dayString, 1) // There is one sun hour on Monday
String.localizedStringWithFormat(stringFormat, dayString, 5) // On Monday there are up to 5 sun hours
参考文献:
- Internationalization and Localization Guide – Handling Noun Plurals and Units of Measurement
- String Programming Guide – String Format Specifiers
两个示例均使用 Swift 5、Xcode 10.2.1、macOS 10.14.5、目标 macOS 10.12 进行了测试。
我想用适当的复数形式翻译字符串 %@ there are up to %i sun hours
。
%@
包含一天,%i
太阳时数。
这是我的 Localizable.stringsdict
文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>%@ there are up to %i sun hours</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@hours@</string>
<key>hours</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>i</string>
<key>zero</key>
<string>%@ there are no sun hours</string>
<key>one</key>
<string>There is one sun hour %@ </string>
<key>other</key>
<string>%@ there are up to %i sun hours</string>
</dict>
</dict>
</dict>
</plist>
我是这样称呼它的:
[NSString stringWithFormat:NSLocalizedString(@"%@ there are up to %i sun hours", nil), dayString, sunHours];
无论我将什么 NSInteger
作为第二个参数传入,翻译将始终使用 "other" 模板。我将它缩小到 NSStringLocalizedFormatKey
是错误的,当我使用像 %@ %#@hours@
这样的键时它正在工作。
不过,我希望字符串参数成为本地化的一部分。有办法吗?
根据我所做的实验,您需要更改参数的顺序。好像只有第一个可以作为替换规则的控制值。
这本词典
<key>%i hours %@</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@hours@</string>
<key>hours</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>i</string>
<key>zero</key>
<string>%2$@ there are no sun hours</string>
<key>one</key>
<string>There is one sun hour %2$@ </string>
<key>other</key>
<string>%2$@ there are up to %1$d sun hours</string>
</dict>
</dict>
结合
[NSString stringWithFormat:NSLocalizedString(@"%i hours %@", nil), sunHours, dayString];
产生了我预期的结果。请注意,我已将参数索引添加到替换字符串中,以便正确放置值。
看起来文档对此功能的描述过于雄心勃勃。例如,给出的示例 in the document titled "OS X 10.9 Release Notes"(尽管 link 用于 iOS)意味着您应该能够打开第二个参数:
We're allowing the recursive formatting by applying the entire argument list to each substituted format specifier.
@"%d in %d files are selected" = @"%2$#@d_in_d_files_are_selected@"
The configuration dictionary can contain
"d_in_d_files_are_selected" = { "NSStringFormatSpecTypeKey" = "NSStringPluralRuleType"; // plural type "NSStringFormatValueTypeKey" = "d"; // int argument "zero" = "There is no file"; "one" = "There is a file, and %1$#@it_is_selected@"; "other" = "%1$d in %2$d files are selected"; };
但是根据那里的指南构建字典并没有给出规定的结果。 (请注意紧接着给出的示例 XML 与该词典不匹配。)
可能是我误读了某些内容(或者可能是错误),但我无法弄清楚到底发生了什么。现在,我将其保留为 "changing the argument order will fix your problem".
从 macOS SDK 10.12/Xcode 8.3 开始,这仍然是一个问题,NSStringLocalizedFormatKey
忽略参数编号并使用第一个参数来确定 d_in_d_files_are_selected
的复数。但。复数规则格式中的嵌套编号引用确实有效,例如"one" = "There is one file, and %2$#@it_is_selected@";
将正确使用 select 复数规则的第二个参数。这意味着如果您使用单个 other
规则创建格式代理,则无需重新排列格式字符串即可实现您想要的行为:
<key>%@ there are up to %i sun hours</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@proxy@</string>
<key>proxy</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>i</string>
<key>other</key>
<string>%2$#@hours@</string>
</dict>
<key>hours</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>i</string>
<key>zero</key>
<string>%1$@ there are no sun hours</string>
<key>one</key>
<string>There is one sun hour %1$@ </string>
<key>other</key>
<string>%1$@ there are up to %2$i sun hours</string>
</dict>
</dict>
请注意,要使其正常工作,您必须在最终格式字符串 ("%1$@ there are up to %2$i sun hours"
) 中明确指定参数编号。
在解决了我需要基于第二个 argument/variable 进行复数化的问题之后,我还找到了解决您问题的方法,而无需更改参数的顺序或使用代理规则链。感谢其他发帖者的回答和解决方案,让我进行了实验并更好地理解了 .stringsdict
。
我认为我的情况与您的情况接近,但更容易一些,并且可以帮助您更好地理解如何应用字符串格式和规则,所以让我从我的示例开始展示。
我的案例
我需要构造一个字符串,如 1 of 1 item selected
或 1 of 5 items selected
:
let countWithSelectionFormat = NSLocalizedString("%ld of %ld item(s) selected", comment: "Number of items with selection")
let countString = String.localizedStringWithFormat(countWithSelectionFormat, selectedCount, totalCount)
字符串格式%ld of %ld item(s) selected
在这里只是一个占位符,一个别名。它在 Localizable.strings
中被忽略,因为它首先在 Localizable.stringsdict
中找到。它仅用作Localizable.stringsdict
中规则的键,但实际上并不用于构造字符串:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>%ld of %ld item(s) selected</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%ld of %#@totalItems@ selected</string>
<key>totalItems</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>one</key>
<string>%ld item</string>
<key>other</key>
<string>%ld items</string>
</dict>
</dict>
</dict>
</plist>
实际的字符串格式在NSStringLocalizedFormatKey
中列出,是%ld of %#@totalItems@ selected
。这里第一个参数 selectedCount
是通过使用 %ld
传递的(没有使其成为变量并为其列出规则)。第二个参数 totalCount
用 %#@totalItems@
变量及其规则解析 return 1 item
或例如 5 items
,从而构造正确的字符串。
如果您需要更改某些语言输出中参数的顺序,您可以使用 %2$#@totalItems@, %1$ld selected
作为 NSStringLocalizedFormatKey
。
如果需要,您还可以引入第二个变量(和规则):%2$#@totalItems@, %1$#@selectedItems@
.
你的情况
我的 Swift 代码与 Objective-C 中的基本相同:
let stringFormat = NSLocalizedString("On %@ there are up to %ld sun hours", comment: "")
let string = String.localizedStringWithFormat(stringFormat, dayString, sunHours)
Localizable.stringsdict
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>On %@ there are up to %ld sun hours</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@day@%#@hours@</string>
<key>day</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>other</key>
<string></string>
</dict>
<key>hours</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>zero</key>
<string>On %1$@ there are no sun hours</string>
<key>one</key>
<string>There is one sun hour on %1$@</string>
<key>other</key>
<string>On %1$@ there are up to %2$ld sun hours</string>
</dict>
</dict>
</dict>
</plist>
在 NSStringLocalizedFormatKey
中我使用了 2 个变量:%#@day@%#@hours@
.
day
变量和规则用于'consume'第一个参数dayString
,我们这里不需要任何输出(固定在句首)。我在这里使用了格式值类型 d
(如 %d
中的整数),因为我们不需要并且我们无法解析字符串来选择适用的复数规则。我们只使用必需的 other
规则,其中 return 是一个空字符串。出于同样的原因,字符串格式中的 day
和 hours
变量之间没有 space。
hours
变量捕获第二个参数 sunHours
并且它的规则负责构造实际的输出字符串(在这种情况下是整个句子)。因为我必须从第二个变量的规则中引用两个参数,所以我使用 %1$@
和 %2$ld
分别引用 dayString
和 sunHours
参数。因此,您还可以按任意组合和顺序使用变量。
这给出了预期的结果:
String.localizedStringWithFormat(stringFormat, dayString, 0) // On Monday there are no sun hours
String.localizedStringWithFormat(stringFormat, dayString, 1) // There is one sun hour on Monday
String.localizedStringWithFormat(stringFormat, dayString, 5) // On Monday there are up to 5 sun hours
参考文献:
- Internationalization and Localization Guide – Handling Noun Plurals and Units of Measurement
- String Programming Guide – String Format Specifiers
两个示例均使用 Swift 5、Xcode 10.2.1、macOS 10.14.5、目标 macOS 10.12 进行了测试。