如何在iPhone应用程序中本地化数字?

10
在我的iPhone应用中,我需要显示对象计数,然后进行本地化处理,因为英语区分单数和复数,所以我使用以下伪代码:
if (objectList.count == 1)
{
   NSLog(@"%@", NSLocalizedString(@"1 object", @"显示一个对象"));
}
else
{
  NSLog(@"%@", NSLocalizedString(@"%d objects", @"显示多个对象"));
}
这对英语来说是有效的,但在许多其他语言中,名词的复数形式不仅仅是简单地添加“s”。正如此页面所解释的那样,有两件事情可能在不同语言之间存在差异:
  • 构建复数形式的方式不同。对于有许多不规则性的语言来说,这是一个问题。例如,德语是一个极端的例子。虽然英语和德语属于同一语族(日耳曼语族),但几乎规律地形成复数名词形式(添加"s")在德语中很难找到。
  • 复数形式的数量不同。对于那些只有罗曼语和日耳曼语言经验的人来说,这有点令人惊讶,因为在这里数字是相同的(有两个)。
我该如何在我的代码中处理这个问题?

这不是仅限于俄语的问题 - 对于其他语言,您需要不止一个复数形式。 - Pavel Minaev
3
请参阅http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html以获取更详细的问题解释(以及一份相当长的具有不规则性的语言清单)。 - Pavel Minaev
谢谢Pavel,我已经相应地编辑了问题。 - Andriy Volkov
5个回答

11
NSLocalizedString将从您的应用程序包中的字符串表中读取。因此,您需要支持的语言列表在编译时已知。与其担心如何为每种可能的语言编写代码,不如只支持您正在支持的语言。
如果您的翻译人员告诉您,为了支持火星语,您需要为偶数和奇数数字分别提供不同的拼写,则可以调整您的代码,例如:
if (objectList.count == 1) {
    NSLocalizedString(@"ObjectCount1", @"display one");
} else if (objectList.count % 2 == 0) {
    NSLocalizedString(@"ObjectCountEven", @"display even");
} else if (objectList.count % 2 == 0) {
    NSLocalizedString(@"ObjectCountOdd", @"display odd");
}

en.lproj/Localizable.strings:

ObjectCount1 = "1 object";
ObjectCountEven = "%d objects";
ObjectCountOdd = "%d objects"; // same as ObjectCountEven

mars.lproj/Localizable.strings:

ObjectCount1 = "1 object-o";
ObjectCountEven = "%d object-e";
ObjectCountOdd = "%d object-o"; // same as ObjectCount1

抱歉如果听起来不是很理想,但人类语言是混乱和不规则的,因此试图为所有语言寻找一个优雅、统一、共同的解决方案是浪费时间的。这样的解决方案是不存在的。

4

我刚刚发布了JJPluralForm,这是适用于Mozilla PluralForm的一种改编。

有了它,您就不必担心在代码中进行本地化时需要编写所有的if-else、switch-case代码,随着本地化数量的增加,这些代码将变得难以维护。

像你提供的例子这样的东西可以这样处理:

[[JJPluralForm sharedManager] pluralStringForNumber:numberOfObjects
                                    withPluralForms:NSLocalizedString(@"N_OBJECTS_PLURAL_STRING", @"")
                                    localizeNumeral:YES];

每个Localizable.strings文件都将N_OBJECTS_PLURAL_STRING本地化为一个分号分隔的复数形式列表。对于英语,那将是"%@ object;%@ objects"
查看项目以获取更多详细信息。

哦,很好,你处理了阿拉伯语的双重情况! - benzado

4
Smartling发布了一个支持遵循CLDR标准的复数形式的开源框架。

https://github.com/Smartling/ios-i18n

为了使用它,您需要在遵循CLDR样式标记的键中添加特殊标记,并调用SLPluralizedString而不是NSLocalizedString。在运行时,库将根据应用当前运行的语言选择正确的字符串来使用。
因此,您的英文.strings文件将包含:
"%@ apples##{one}" = "One apple";
"%@ apples##{other}" = "%@ apples";

俄语.strings文件:

"%@ apples##{one}" =   "%@ яблоко";
"%@ apples##{few}" =   "%@ яблока";
"%@ apples##{many}" =  "%@ яблок";
"%@ apples##{other}" = "%@ яблока";

而调用它的代码可能如下所示:

    NSString *s2 = [NSString stringWithFormat:SLPluralizedString(@"%@ apples", number, nil), number];

注意,此示例演示了该库的一个伟大之处-它允许您使用表达性语言编写原始字符串或翻译,甚至可能不使用格式说明符。
在撰写本文时,我是Smartling的产品经理。

2

顺便提一下,在 iOS 7 和 Mac OS X 10.9 中,本地化中内置了对复数的支持。请参见 WWDC 2013 视频 "Making Your App World-Ready",从 16:10 开始介绍。复数是在一个 .stringsdict 的 plist 文件中进行本地化的,然后你只需要执行以下操作:

NSLog(@"%@", [NSString localizedStringWithFormat:
          NSLocalizedString(@"%d objects", @"display multiple objects"), n]);

0

对于这个问题,还有一个更强大的解决方案。请看来自TranslationExchange.com的Tr8n库。

https://github.com/tr8n/tr8n_objc_clientsdk

该库使用TranslationExchange的TML(翻译标记语言),使国际化过程非常简单。首先,您甚至不需要再处理Strings XML文件了... - Tr8n SDK将会实时为您创建和管理String文件 - 您永远不用再看它们了。

您的特定示例将简单的是:

Tr8nLocalizedStringWithTokens(@"{count || object}", @{@"count": objectList.count}) 

Tr8n库会自动为您选择任何语言中的正确复数形式。哈?是的,这很神奇。上面令牌示例的完整形式实际上是:

{count:number || one: object, other: objects}

这意味着“count”标记是一种数值类型,使用关键字“one”和“other”映射到英语复数形式...但Tr8n足够聪明,不需要您输入所有内容。它还足够聪明,将参数序列映射到适当的规则值。当然,它知道通过命名约定,“count”与数字规则相关联。因此,它变得非常简单:
{count || object}

顺便提一下,既然你提到了俄语,那么上面的内容的俄语翻译就很简单:
"{count || object}" = "{count || объект, объекта, объектов}"

这个例子太简单了,让我们来看一个更有趣的:

Tr8nLocalizedStringWithTokens(
   @"{user} uploaded {count || photo} to {user | his, her} photo album.", 
   @{@"user": user, @"count": 5}
) 

首先,祝你好运使用标准的iOS i18n库(或任何其他库)翻译这个句子...开个玩笑-但实际上,除了Tr8n之外,没有其他方法可以做到。
上述TML翻译成俄语就是简单的:
@"{user || загрузил, загрузила} {count || фотографию, фотографии, фотографий} в свой фотоальбом." 

在这里,我们处理性别规则的方式与处理数字规则的方式相同。但是,我们不是使用“one”、“few”、“other”,而是使用“男性”、“女性”、“未知” - 每种语言可能具有不同的性别和数字规则。Tr8n将处理它,因此您无需担心。

好的,让我们把它提升到下一个级别。您已经决定必须将照片的数量加粗显示。小菜一碟。

Tr8nLocalizedAttributedStringWithTokens(
  @"{user} uploaded [bold: {count || photo}] to {user | his, her} photo album.", 
  @{
    @"user": user, 
    @"count": 5, 
    @"bold": @{@"font":@{@"name": @"system", @"size": @12, @"type": @"bold"}}
  }
) 

[加粗:... ] 是一个修饰标记。你是否注意到我们将宏切换到了AttributedString版本?这个宏实际上会使用iOS本地的装饰机制来产生NSAttributedString。你能猜出俄语翻译是什么吗?

@"{user || загрузил, загрузила} [bold: {count || фотографию, фотографии, фотографий}] в свой фотоальбом."

另外,您可以在其他地方预定义所有装饰令牌,这样您就不必每次都定义它们。

让我们再做一个最后的例子... 假设您有一篇以下形式的新闻订阅:

Tr8nLocalizedAttributedStringWithTokens(
  @"{actor} sent {target} [bold: {count || gift}].", 
  @{
    @"actor": user1, 
    @"target": user2, 
    @"count": 5
  }
) 

在英语中看起来并不有趣。但是在俄语或任何支持语言格的语言中,它都会变得有趣。如果{target}的名称恰好是俄语,则实际上需要使用俄语与格。

http://en.wikipedia.org/wiki/Dative_case

如果您不会说俄语,那么您可能不需要了解它。但是您的俄语翻译应该知道。让我们看看俄语翻译:

@"{actor || подарил, подарила} {target::dat} [bold: {count || подарок, подарка, подарков}].", 

Tr8n 足够智能,可以使用其强大的语言规则引擎,并将与 {target} 令牌传递的俄语名称应用于与格语态...

对于一个简单的数字问题来说,这是一个有点长的答案。感谢您阅读到这里。希望它有所帮助。

免责声明:我是 Tr8n 框架和 TML 语言的创建者。如果您有任何问题,请联系我,我很乐意为您解答所有翻译问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接