Android plurals treatment of "zero"

Android

Android Problem Overview


If have the following plural ressource in my 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>	

I'm showing the result to the user using:

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

It's working well with 1 and above, but if quantity is 0 then I see "0 items". Is "zero" value supported only in Arabic language as the documentation seems to indicate? Or am I missing something?

Android Solutions


Solution 1 - Android

The Android resource method of internationalisation is quite limited. I have had much better success using the standard java.text.MessageFormat.

Basically, all you have to do is use the standard string resource like this:

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

Then, from the code all you have to do is the following:

String fmt = getResources().getText(R.string.item_shop).toString();
textView.setText(MessageFormat.format(fmt, amount));

You can read more about the format strings in the javadocs for MessageFormat

Solution 2 - Android

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

Note that the selection is made based on grammatical necessity. A string for zero in English will be ignored even if the quantity is 0, because 0 isn't grammatically different from 2, or any other number except 1 ("zero books", "one book", "two books", and so on). Don't be misled either by the fact that, say, two sounds like it could only apply to the quantity 2: a language may require that 2, 12, 102 (and so on) are all treated like one another but differently to other quantities. Rely on your translator to know what distinctions their language actually insists upon.

In conclusion, 'zero' is only used for certain languages (same goes for 'two' 'few' etc.) because the other languages do not have a special conjugation and therefore the 'zero' field is considered unnecessary

Solution 3 - Android

Here is a workaround I am using to handle this issue without switching to MessageFormat.

First I extract the "zero" string into its own string resource.

<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>

Then I have some convenience methods in my own 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);
    }
}

Now anytime I want to use a specific string for quantity zero I call:

String pluralString = ResourcesUtil.getQuantityStringZero(
     getContext().getResources(),
     R.plural.x_items,
     R.string.x_items_zero,
     quantity
);

I wish there was something better but this at least gets the job done while keeping the string resource XML legible.

Solution 4 - Android

Android is using the CLDR plurals system, and this is just not how it works (so don't expect this to change).

The system is described here:

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

In short, it's important to understand that "one" does not mean the number 1. Instead these keywords are categories, and the specific numbers n that belong to each category are defined by rules in the CLDR database:

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

While there appears to be no language which uses "zero" for anything other than 0, there are languages which assign 0 to "one". There are certainly plenty of cases where "two" contains other numbers than just 2.

If Android where to allow you to do what you intended, your applications could not be properly translated into any number of languages with more complex plural rules.

Solution 5 - Android

I've wrote a Kotlin extension to handle all the scenarios that I can think about.

I have zeroResId as optional, so that sometimes we want to handle the zero by displaying "No Items", rather than "0 Items".

English treats zero grammatically as plural.

> The selection of which string to use is made solely based on > grammatical necessity.

> In English, a string for zero is ignored even > if the quantity is 0, because 0 isn't grammatically different from 2, > or any other number except 1 ("zero books", "one book", "two books", > and so on).

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)
    }
}

Solution 6 - Android

If you are using data binding you can work around this with something like:

<TextView ... 
android:text="@{collection.size() > 0 ? @plurals/plural_str(collection.size(), collection.size()) : @string/zero_str}"/>

Solution 7 - Android

Android's implementation seems correct unlike iOS (see details here).

The correct way to implement this should be the same are in MessageFormat, which means that for non-grammatical categories, you should add explicit rules (using numbers instead of categories). A correct implementation could look like this:

   <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>  

See here you are using the number 0 instead of the plural category zero which does not apply to English.

Using MessageFormat, this would translate to this (you can test here):

{messageNumber, plural, =0 {No item.} =1 {One item.} one {# item.} other {# items.}}

In English the category one is equal to 1 so in the example one should never be used but this is not true for all languages, and when spelling out a number, you are better make sure that you know which plural rule applies for that language.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEricLarchView Question on Stackoverflow
Solution 1 - AndroidElias MårtensonView Answer on Stackoverflow
Solution 2 - AndroidByteMeView Answer on Stackoverflow
Solution 3 - AndroidJustin FiedlerView Answer on Stackoverflow
Solution 4 - Androidmiracle2kView Answer on Stackoverflow
Solution 5 - AndroidMorgan KohView Answer on Stackoverflow
Solution 6 - AndroidMarkView Answer on Stackoverflow
Solution 7 - AndroidNicolas BouvretteView Answer on Stackoverflow