Android中“zero”的复数处理

106
如果我的 strings.xml 文件中有以下复数资源:
   <plurals name="item_shop">
        <item quantity="zero">No item</item>
        <item quantity="one">One item</item>
        <item quantity="other">%d items</item>
   </plurals>   

我正在使用以下代码向用户显示结果:

textView.setText(getQuantityString(R.plurals.item_shop, quantity, quantity));

在数量为1或更多时,它表现良好,但如果数量为0,则会显示"0项"。 文档中似乎只支持阿拉伯语的“零”值,我是否遗漏了什么?


23
这个问题在这里被报告了:http://code.google.com/p/android/issues/detail?id=8287。目前还没有修复:/ - OcuS
6
我不认为这是一个bug: 需要注意的是,选择是基于语法必要性进行的。在英语中,零的字符串将被忽略,即使数量为0,因为0在语法上与2或任何其他数字(除了1)没有区别(“zero books”,“one book”,“two books”等)。 - ByteMe
3
遗憾的是,并非所有语言都与英语相同。 - Armand
4
我认为这是一个bug,因为除了语法之外,99%的情况下我需要使用的是缺失的功能。所以,我相信这是Google可能永远不会修复的一个漏洞。因此,需要一个解决方法。你知道,API旨在帮助消费者完成任务,而不是仅仅代表现实世界中的抽象概念。如果零被支持的话,两个群体(那些需要语法正确性和那些不需要的人)都可以不用使用其他东西就能完成任务。 - Martin Marconcini
7个回答

92

Android的国际化资源方式相当有限。我使用标准的java.text.MessageFormat获得了更好的成功。

基本上,你只需要像这样使用标准字符串资源:

<resources>
    <string name="item_shop">{0,choice,0#No items|1#One item|1&lt;{0} items}</string>
</resources>

那么,从代码中你所要做的就是以下内容:

String fmt = getResources().getText(R.string.item_shop).toString();
textView.setText(MessageFormat.format(fmt, amount));
您可以在MessageFormat的javadoc中阅读更多有关格式字符串的信息。

8
很好的解决方法。我可以想象现在那些翻译我的应用程序的人将会把我的文件弄成什么模样... :) - OcuS
1
Java的MessageFormat类不支持此功能。我的建议是将最后一位数字的值作为额外参数传递,以便您可以对其进行选择。这在俄语中至少是必需的,可能在其他类似的语言中也是如此。 - Elias Mårtenson
10
哇,那很难看。这样的逻辑应该放在代码里,而不是翻译中。复数只应该用于帮助处理数字上的语言差异。现在,你已经成功地将逻辑引入了翻译中,并将翻译转化为了逻辑,这并不应该是建议“few”的结果。 - DennisK
4
这确实不是一个好的解决方法。只需添加一个“no_items”的字符串和一个if语句。如果(items.count == 0),则使用该字符串,否则使用复数形式。然后,让翻译人员在strings.xml文件中解决这些字符串的问题... - Martin Marconcini
2
@MartínMarconcini 您是正确的...超过3年后 :-) - GreyBeardedGeek
显示剩余6条评论

54

来自 http://developer.android.com/guide/topics/resources/string-resource.html#Plurals:

请注意,选择是基于语法必要性进行的。在英语中,零的字符串将被忽略,即使数量为0,因为0在语法上与2或任何其他数字都没有区别,除了1(“zero books”,“one book”,“two books”等)。同时也不要被误导,例如,“two”似乎只适用于数量2:一个语言可能要求将2、12、102(等等)都视为彼此相同但与其他数量不同。请依赖您的翻译家了解他们的语言实际上坚持哪些区别。

总之,'zero'仅适用于某些语言('two'、'few'等也是如此),因为其他语言没有特殊的联动形式,所以'zero'字段被认为是不必要的。


7
即使数量为0,在英语中表示零的字符串会被忽略,因为0在语法上与2没有区别。那么,“我没有水果”和“我有一个苹果和一个橙子”呢? - Jeffrey Blattman
5
有很多不同的方式来表达0和2。但是这些资源的背后意图是用它们来描述数字。所以可以这样表述: 我没有苹果。 我有1个苹果。 我有2个苹果。 - ByteMe
7
太糟糕了。他们不应该试图做出特定语言的假设。我认识的每个使用复数形式的人都被搞糊涂了。 - Jeffrey Blattman
制定特定语言的假设是这个系统的全部意义。可能有一种语言,其中数量0的语法形式也用于10、100等。如果您在英语中滥用“零”类别来表示特殊字符串,则该字符串无法翻译成这种语言。 - miracle2k
2
@miracle2k: 消息不会按类别翻译,因此您在一种语言中拥有的任何类别或消息都不会影响另一种语言。因此,如果您在零计数的英语中有一个“0”级消息,并且另一种语言使用“0”类来计算10、100,则无关紧要,因为您不必翻译“0”级消息。另一方面:如果您为英语中的0计数创建单独的消息资源,则会为翻译者创建工作流问题,因为该情况已由其语言中的原始“0”级消息覆盖。 - Basel Shishani
2
启用英语中的“零”类别作为方便功能确实是可行的,但这并不“干净”,只会鼓励不良行为。您可能会想到放置文本以鼓励用户将项目添加到“零”字符串中-然后可能无法翻译。严格来说,OP的示例(“没有项目”)已经展示了这个问题,因为翻译人员可能也想用单词来表达0情况。如果您认为“%s项”是“%s项”的单数版本,“没有项目”则是完全不同的语言形式,则这不是工作流问题。 - miracle2k

19

我使用以下方法来处理这个问题,而无需切换到MessageFormat。

首先,我将“零”字符串提取为自己的字符串资源。

<string name="x_items_zero">No items.</string>
<plurals name="x_items">
    <!-- NOTE: This "zero" value is never accessed but is kept here to show the intended usage of the "zero" string -->
    <item quantity="zero">@string/x_items_zero</item>
    <item quantity="one">One item.</item>
    <item quantity="other">%d items.</item>
</plurals>

那么我在自己的ResourcesUtil中有一些便利方法

public static String getQuantityStringZero(Resources resources, int resId, int zeroResId, int quantity) {
    if (quantity == 0) {
        return resources.getString(zeroResId);
    } else {
        return resources.getQuantityString(resId, quantity, quantity);
    }
}

public static String getQuantityStringZero(Resources resources, int resId, int zeroResId, int quantity, Object... formatArgs) {
    if (quantity == 0) {
        return resources.getString(zeroResId);
    } else {
        return resources.getQuantityString(resId, quantity, formatArgs);
    }
}
现在,每当我想使用特定的零数量字符串时,我会调用:
String pluralString = ResourcesUtil.getQuantityStringZero(
     getContext().getResources(),
     R.plural.x_items,
     R.string.x_items_zero,
     quantity
);

我希望有更好的方法,但最起码这种方法可以完成任务,并且保持字符串资源XML的清晰可读性。


2
使用这个,"<item quantity="zero">" 实际上永远不会被使用吗? - android developer
6
“<item quantity="zero">”将不会被使用。我将它放在那里是因为我认为这有助于澄清零值的预期使用方式。 - Justin Fiedler
想到了。这可能有助于翻译人员理解正在发生的事情。 - android developer
我本来想发布一个类似的解决方案,但没有 <item quantity="zero">@string/x_items_zero</item>。你说服了我为了清晰起见加上它,我觉得这非常优雅! - Antoine

13

Android使用CLDR复数系统,这就是它的工作原理(所以不要期望它会改变)。

该系统在此处描述:

http://cldr.unicode.org/index/cldr-spec/plural-rules

简而言之,重要的是要明白,“one”并不表示数字1。相反,这些关键字是类别,每个类别中属于该类别的特定数字n由CLDR数据库规则定义:

http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html

虽然没有任何语言除了0之外使用“zero”,但有些语言将0分配给“one”。还有许多情况,“two”包含的数字不仅限于2。

如果Android允许您执行所需操作,那么您的应用程序将无法正确翻译成任何具有更复杂复数规则的语言。


6
在多个问题上发布复制粘贴的模板/原文答案时,请注意小心,这往往会被社区标记为“垃圾邮件”。如果您这样做,那么通常意味着这些问题是重复的,因此请标记它们为重复:https://dev59.com/KGoy5IYBdhLWcg3wkOwK - Kev

2
如果您正在使用数据绑定,您可以通过类似以下的方式解决这个问题:
<TextView ... 
android:text="@{collection.size() > 0 ? @plurals/plural_str(collection.size(), collection.size()) : @string/zero_str}"/>

2
我编写了一个 Kotlin 扩展来处理我能想到的所有情况。
我将 `zeroResId` 设置为可选项,这样有时我们想通过显示“没有项目”来处理零,而不是“0 个项目”。
在英语中,零在语法上被视为复数。

仅基于语法必要性来选择要使用的字符串。

在英语中,即使数量为 0,也会忽略零的字符串,因为 0 在语法上与 2 或任何其他数字都没有区别,除了 1("zero books", "one book", "two books" 等)。

https://developer.android.com/guide/topics/resources/string-resource.html#Plurals

fun Context.getQuantityStringZero(
    quantity: Int, 
    pluralResId: Int, 
    zeroResId: Int? = null
): String {
    return if (zeroResId != null && quantity == 0) {
        resources.getString(zeroResId)
    } else {
        resources.getQuantityString(pluralResId, quantity, quantity)
    }
}

1

Android的实现似乎是正确的,不像iOS(详见这里)。

正确的实现方式应该与MessageFormat中相同,这意味着对于非语法类别,您应该添加显式规则(使用数字而不是类别)。一个正确的实现可能看起来像这样:

   <plurals name="item_shop">
        <item quantity="0">No item</item>
        <item quantity="1">One item</item>
        <item quantity="one">%d item</item>
        <item quantity="other">%d items</item>
   </plurals>  

看这里,你使用数字0而不是复数类别zero,这在英语中不适用。

使用MessageFormat,这将翻译为以下内容(您可以在此处进行测试):

{messageNumber,plural,= 0 {没有项目。} = 1 {一个项目。} one {#项。} other {#项。}}

在英语中,类别one等于1,因此在示例中不应使用one,但并非所有语言都是如此,当拼写数字时,最好确保您知道该语言适用哪个复数规则


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