简体   繁体   中英

Android Plurals for float values

I would like to use plurals for my Android project. However, the values I provide can be float values.

So for instance, when setting 1.5 stars, I want this to understand, it's not 1 star but 1.5 star s .

<plurals name="stars">
  <item quantity="one">%d star</item>
  <item quantity="other">%d stars</item>
</plurals>

However, the Android system seems to use integer values (%d) only.

The method looks like this:

String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs)

where quantity is defined as Int .

Is there any solution for this?

First I have to say: very interesting question.

Secondly this is the solution: Use %s instead of %d for your placeholder.

<plurals name="stars">
    <item quantity="one">%s star</item>
    <item quantity="other">%s stars</item>
</plurals>

Then you have to use Math.ceil() on your quantity. So 1.1f for example would become 2 and therefore your text would turn from star to star s .

I have written this solution in Kotlin and Java.


For Kotlin I wrote an extension function which looks like this:

    fun Resources.getQuantityString(@PluralsRes pluralResId: Int, pluralValue: Float) =
       this.getQuantityString(R.plurals.stars, Math.ceil(pluralValue.toDouble()).toInt(), pluralValue)

which would be called like this: tvText.text = resources.getQuantityString(R.plurals.stars, 1.1f)


For Java my solution looks like this:

import android.content.res.Resources;
import android.support.annotation.PluralsRes;

public class PluralResource {
    public static String get(Resources resources, @PluralsRes int pluralResId, float pluralValue) {
        return resources.getQuantityString(pluralResId, (int) Math.ceil(pluralValue), pluralValue);
    }
}

which is called like this:

tvText.text = PluralResource.get(resources, R.plurals.stars, 1.1f)

Simply do this:

getQuantityString(R.plurals.stars, quantity > 1f ? 2 : 1, quantity):

And replace the %d in your strings with %f.

Don't use plurals for fractional numbers. Just stick with basic string resources and use a placeholder:

<string name="fractional_stars">%1$s stars</string>

getString(R.string.fractional_stars, 0.5F.toString())

or

<string name="fractional_stars">% stars</string>

getString(R.string.half_a_star).replace("%", 0.5F.toString())

After doing further research, it appears there is no good solution for this .

As also seen in the other answers, they always require a lot of "manual processing" to it requiring no different workflow than creating separate string resources.

The general suggestion seems to be rounding / processing the float values manually (eg checking whether the float value matches 1.0) and then using apropriate Int values for the plurals call.

But aside from not really using plurals then this comes with the problem of other languages (eg I have no clue if 1.5 stars would also be plural in another language as it is in English) and thus these rounding options may not apply universally.

So the answer is: there seems to be no perfect solution (meaning solved "automatically" by the Android system).

What I actually do therefore is to simply pick exceptions and use different Strings there. So the (pseudo code) way of doing currently looks like

// optionally wrap different languages around
// if language == English

   when (amountStars) {
    is 1.0 -> getString(R.string.stars_singular, 1) 
    ... ->
    else -> getString(R.string.stars_plural, amountStars)
   }
// if language == Chinese ...

where additional cases have to be "hard coded". So for example you have to decide whether 0 means

" 0 star s " (plural string) or

" no star " (singular string)

But there seems no real benefit of using plurals over separate string resources with common placeholders. On the other hand this (at last for me) gives more flexibility for formatting options. For example, one may create a text like "1 star and a half" where it becomes singular again (even though numerically we would write 1.5 stars).

getQuantityString takes a quantity of type Int and Object... formatArgs If you round the quantity to Int you would make sure that any value in 1.0 -> 1.99 is a single item and other than that is a plural

 resources.getQuantityString(
            R.plurals.products_left_in_stock_message_plural,
            leftInStock.toInt(), leftInStock.toString()
        )

So you only round it the quantity but pass the actually value as an argument

<plurals name="products_left_in_stock_message_plural">
    <item quantity="one">Only one item is available from this product</item>
    <item quantity="other">There are only %s item is available from this product</item>
</plurals>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM