Android множественное число для значений с плавающей точкой

Я хотел бы использовать множественное число для моего проекта Android. Однако указанные мной значения могут быть значениями с плавающей точкой.

Так, например, при установке 1,5 звезды, я хочу, чтобы это понимало, это не 1 звезда, а 1,5 звезды.

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

Однако система Android, похоже, использует только целочисленные значения (%d).

Метод выглядит так:

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

где количество определяется как Int.

Есть ли решение для этого?

6 ответов

После дальнейших исследований выясняется, что для этого нет хорошего решения.

Как также видно из других ответов, они всегда требуют много "ручной обработки", не требующей другого рабочего процесса, чем создание отдельных строковых ресурсов.

Похоже, что общим предложением является округление / обработка значений с плавающей запятой вручную (например, проверка соответствия значения с плавающей запятой 1,0), а затем использование соответствующих значений Int для вызова множественного числа.

Но, кроме того, что на самом деле не используется множественное число, это связано с проблемой других языков (например, я понятия не имею, если 1,5 звезды также будут множественным числом на другом языке, как на английском), и, следовательно, эти варианты округления могут не применяться универсально.

Таким образом, ответ таков: кажется, что не существует идеального решения (то есть "автоматически" решается системой Android).

Поэтому я на самом деле просто выбираю исключения и использую там разные строки. Таким образом, способ (псевдокод) в настоящее время выглядит так

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

где дополнительные случаи должны быть "жестко закодированы". Так, например, вы должны решить, означает ли 0

"0 звездочекs" (множественное число) или

"без звезды" (единственная строка)

Но, похоже, нет реального преимущества использования множественного числа по сравнению с отдельными строковыми ресурсами с общими заполнителями. С другой стороны, это (наконец-то для меня) дает больше возможностей для форматирования. Например, можно создать текст типа "1 звезда с половиной", где он снова станет единичным (хотя численно мы будем писать 1,5 звезды).

Сначала я должен сказать: очень интересный вопрос.

Во-вторых, это решение: используйте%s вместо%d в качестве заполнителя.

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

Тогда вы должны использовать Math.ceil() на ваше количество. Так 1.1f например станет 2 и поэтому ваш текст будет превращаться из звезды в звездус.

Я написал это решение на Kotlin и Java.


Для Kotlin я написал функцию расширения, которая выглядит следующим образом:

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

который будет называться так:tvText.text = resources.getQuantityString(R.plurals.stars, 1.1f)


Для Java мое решение выглядит так:

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

который называется так:

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

Не используйте множественное число для дробных чисел. Просто придерживайтесь основных строковых ресурсов и используйте заполнитель:

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

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

или же

      <string name="fractional_stars">% stars</string>
      
getString(R.string.half_a_star).replace("%", 0.5F.toString())

Просто сделайте это:

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

И замените%d в ваших строках на%f.

getQuantityString принимает количество типа Int и Object... formatArgs Если вы округляете количество до Int, вы должны убедиться, что любое значение в 1.0 -> 1.99 является одним элементом, а кроме этого - во множественном числе.

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

Таким образом, вы только округляете количество, но передаете фактическое значение в качестве аргумента.

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

вы можете сделать это:

      data class PluralInfo(
    @StringRes val talkBackOne: Int,    //singular plural form
    @StringRes val talkBackTwo: Int,    //dual plural form.
    @StringRes val talkBackFew: Int,    //paucal plural form (russian 2-4)
    @StringRes val talkBackMany: Int,   //russian 5-19, arabic 11-99...
    @StringRes val talkBackZero: Int,   //zero plural form
    @StringRes val talkBackOther: Int,  //default plural form (float values here)
) {
    fun getQuantityString(context: Context, quantity: Double): String {
        val locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            context.resources.configuration.locales[0];
        } else {
            //noinspection deprecation
            context.resources.configuration.locale;
        }

        val rules = PluralRules.forLocale(locale, PluralRules.PluralType.CARDINAL)
        val resId = when (rules.select(quantity)) {
            PluralRules.KEYWORD_ZERO -> talkBackMany
            PluralRules.KEYWORD_ONE -> talkBackOne
            PluralRules.KEYWORD_TWO -> talkBackFew
            PluralRules.KEYWORD_FEW -> talkBackFew
            PluralRules.KEYWORD_MANY -> talkBackMany
            PluralRules.KEYWORD_OTHER -> talkBackOther
            else -> talkBackOther
        }
        return context.getString(resId)
    }
}
Другие вопросы по тегам