使用Objective-C在iPhone上本地化动态复数名词消息(例如“5个已处理项目”)

时间:2011-02-25 21:33:39

标签: iphone ios localization internationalization localizable.strings

在我当前的应用中,我有代码显示一条消息,例如“处理了5件物品。”为了保持短语在语法上正确,即它是否应该是“5 Item”或“5 Items”,我使用以下代码:

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代码中使用语法逻辑,而应该在strings文件中使用语法逻辑。有没有办法做到这一点? 人们如何为其应用翻译动态文字,以使其在语法上保持正确?

4 个答案:

答案 0 :(得分:61)

从iOS 7开始,Foundation框架已native support for pluralization. 这是一个如何使用它的快速教程:

创建名为 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指出这一点 - &gt;:您还需要在.stringsdict旁边创建Localizable.strings文件以使复数化(即使它是空的)。

答案 1 :(得分:12)

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

基本前提是根据CLDR plural rules扩展多个字符串的键以包含其复数形式,并且字符串的查找不使用典型的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”);

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

俄语文件将如下所示:

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

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

请随时与您分享您对图书馆,建议,改进等方面的看法。

答案 2 :(得分:2)

在英语中,只有2种复数形式,例如“1个文件”和“5个文件”。在俄语中,有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和目标语言的价值,你的app必须在运行时检测复数形式编号,即实现类似

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

    方法。如果你只支持2-5种语言并且消息中的数字总是非负的int,实际上很简单,无需任何3d派对lib,你可以复制/粘贴C兼容的单行来自http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html的语言。请注意,它仅适用于非负整数,因此复数形式的数量可能与unicode.org所说的不同。

  3. 3d party libs。

答案 3 :(得分:1)

我会考虑使用其他方法来显示相同​​的信息,可能是这样的:

@"Items processed: %d"
如果您感兴趣,

MDC有一个长而全面的复数规则列表,但我认为实施所有这些规则并不值得。你可能需要考虑的另一件事是,如果任何语言都会在复数后出现这个数字,因为它会抛弃你的格式字符串(我想不出任何语言可能会超出我的头脑,但也许是一个地方在涉及数字的任何更复杂的本地化字符串中要小心。)