34

在我当前的应用程序中,我有显示一条消息的代码,例如“5 个项目已处理”。为了保持短语语法正确,即是否应该是“5 项”或“5 项”,我使用以下代码:

int numItems = 5;
NSString *myString = [[NSString alloc] initWithFormat:@"%d Item%@ Processed", numItems, (numItems == 1 ? @"" : @"s")];

这目前工作正常。但我正在本地化我的应用程序,并希望确保文本在我将应用程序翻译成的所有语言中的语法正确。我可以做这样的事情:

int numItems = 5;
NSString *myString = (numItems == 1 ? 
NSLocalizedStringWithTable(@"%d Item Processed", @"myApp", @"singular version") :
NSLocalizedStringWithTable(@"%d Items Processed", @"myApp", @"plural version"));

然而,并非所有语言对复数的操作都有相同的规则!例如,在俄语中(请原谅我在这里的非常具体的例子),以最后一个数字 1 结尾的数字(即 21、31,但不是11)修饰的名词采用主格,以 2-4 结尾的数字采用单数属格, 和 5+ 采用复数属格。这将需要更严肃的逻辑来处理如何以语法正确的方式对特定名词进行复数,并且这种逻辑与英语逻辑不匹配。因此,理论上,我的 Objective-C 代码中不能有语法逻辑,而应该在字符串文件中有语法逻辑。有没有办法做到这一点?人们如何为他们的应用程序翻译动态文本以保持语法正确?

4

4 回答 4

62

从 iOS 7 开始,Foundation 框架原生支持复数。 以下是如何使用它的快速教程:

创建一个名为Localizable.stringsdict的 plist 文件

英文本地化:

<?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>%d tasks waiting for action</key>
        <dict>
            <key>NSStringLocalizedFormatKey</key>
            <string>%#@tasks@ waiting for action</string>
            <key>tasks</key>
            <dict>
                <key>NSStringFormatSpecTypeKey</key>
                <string>NSStringPluralRuleType</string>
                <key>NSStringFormatValueTypeKey</key>
                <string>d</string>
                <key>one</key>
                <string>A task is</string>
                <key>two</key>
                <string>Two tasks are</string>
                <key>other</key>
                <string>%d tasks are</string>
            </dict>
        </dict>
    </dict>
</plist>

波兰语本地化:

<?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>%d tasks waiting for action</key>
        <dict>
            <key>NSStringLocalizedFormatKey</key>
            <string>Masz %#@zadanie@ do zrobienia</string>
            <key>zadanie</key>
            <dict>
                <key>NSStringFormatSpecTypeKey</key>
                <string>NSStringPluralRuleType</string>
                <key>NSStringFormatValueTypeKey</key>
                <string>d</string>
                <key>one</key>
                <string>jedno zadanie</string>
                <key>few</key>
                <string>%d zadania</string>
                <key>other</key>
                <string>%d zadań</string>
            </dict>
        </dict>
    </dict>
</plist>

最后在您的实现文件中,您可以像这样调用字典:

cell.tasksInfoLabel.text = [NSString localizedStringWithFormat:NSLocalizedString(@"%d tasks waiting for action", @"%d tasks waiting for action"), (long)taskCount];

编辑:感谢Zaphod指出这一点->:您还需要在 .stringsdict 旁边创建 Localizable.strings 文件以进行复数工作(即使它是空的)。

于 2013-12-01T02:51:16.520 回答
12

我的团队开发了一个开源库来处理这种情况,在 github 上查看我们的iOS i18n 复数库。

基本前提是根据CLDR 复数规则将复数字符串的键扩展为包含其复数形式,并且字符串的查找不使用典型的 NSLocalizedString。

发布的示例的英文文件如下所示:

"%d Items Processed##{one}"   = "1 Item Processed";    
"%d Items Processed##{other}" = "%d Items Processed";

然后将使用 SLPluralizedString 函数完成查找

SLPluralizedString(@”%d Items Processed”, numItems, @”Number of items processed”);

在运行时,对于英语,将根据 numItems 的值返回字符串“1 Item Processed”或“%d Items Processed”。

俄罗斯文件将如下所示:

"%d Items Processed##{one}"   = "%d элемент обработан";
"%d Items Processed##{few}"   = "%d элемента обработано";
"%d Items Processed##{many}"  = "%d элементов обработано";
"%d Items Processed##{other}" = "%d элемента обработано";

然后,您无需更改为俄语或任何其他语言查找“已处理项目”的代码,该库将根据该特定语言的 CLDR 复数规则返回正确的字符串。

请随时分享您对图书馆的想法、建议、改进等。

于 2013-03-27T17:10:47.317 回答
2

在英语中,只有两种复数形式,例如“1 file”和“5 files”。在俄语中,有 3 种复数形式(101 файл、2 файла、11 файлов),除非您计算非整数。实际上,一种语言实际上最多可以有 6 个复数形式(例如阿拉伯语有 6 个)。似乎有 3 种方法可以解决这个问题,只要选择对你来说足够好但不太复杂的方法:

  1. 尝试使用复数中性消息,例如“已处理的项目数:%d”而不是“已处理的%d 个项目 | 已处理的 %d 个项目”。

  2. 支持每种复数形式的本地化,最多 6 个。

    "%d Gold Coins##{PluralForm0}" -> "%d золотая монета" // e.g. 1 gold coin
    "%d Gold Coins##{PluralForm1}" -> "%d золотые монеты" // e.g. 2 gold coins
    "%d Gold Coins##{PluralForm2}" -> "%d золотых монет"  // e.g. 5 gold coins
    …
    "%d Gold Coins##{PluralForm5}" -> "%d How did we get here if this is not Arabic???"
    

    知道 %d 的值和目标语言后,您的应用程序将不得不在运行时检测复数形式的数字,即实现类似

    unsigned int "NumberToPluralFormNumber(unsigned int number, const std::string& langCode);
    

    方法。如果您只支持 2-5 种语言并且消息中的数字始终是非负整数,那么在没有任何 3d 方库的情况下实现它实际上非常简单,您可以为每个语言复制/粘贴与 C 兼容的单行来自http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html的语言。请注意,它仅对非负整数有效,因此复数形式的数量可能与 unicode.org 所说的不同。

  3. 3d 派对库。
于 2013-10-30T16:53:16.143 回答
0

我会考虑显示相同信息的替代方法,可能类似于:

@"Items processed: %d"

如果您有兴趣, MDC有一个长而全面的复数规则列表,但我真的认为不值得为实施所有这些规则付出努力。您可能需要考虑的另一件事是,是否有任何语言会在复数后出现数字,因为它会丢弃您的格式字符串(我想不出任何语言会在我脑海中浮现,但也许是一个地方请谨慎处理任何涉及数字的更复杂的本地化字符串)。

于 2011-02-25T21:51:41.407 回答