Обработка слова "ноль" в множественном числе Android

83

Если в моем 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 элементов». Поддерживается ли «нулевое» значение только на арабском языке, как, кажется, указано в документации? Или я что-то упускаю?

EricLarch
источник
21
Об этой проблеме сообщается здесь code.google.com/p/android/issues/detail?id=8287 . Еще не исправлено: /
OcuS
5
Не думаю, что это ошибка: обратите внимание, что выбор сделан исходя из грамматической необходимости. Строка для нуля на английском языке будет проигнорирована, даже если количество равно 0, потому что 0 грамматически не отличается от 2 или любого другого числа, кроме 1 («ноль книг», «одна книга», «две книги» и т. Д. on)
ByteMe
1
К сожалению, не все языки идентичны английскому,
Armand
2
Я думаю, что это ошибка в том смысле, что, помимо грамматики, в 99% случаев, когда мне приходилось использовать это, мне требовались недостающие функции. Итак, да, я считаю, что это ошибка, которую Google, вероятно, никогда не исправит. Таким образом, необходимо обходное решение. Видите ли, API существуют для того, чтобы помогать потребителям, добиваться результатов, а не исключительно для представления абстрактных концепций из реального мира. Если бы Zero поддерживался из коробки, обе группы (те, кому нужна грамматическая правильность, и те, кому нет), могли бы обойтись без использования чего-то другого.
Martin Marconcini

Ответы:

90

Метод интернационализации ресурсов 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));

Вы можете узнать больше о строках формата в документации javadoc для MessageFormat.

Элиас Мартенсон
источник
7
Хороший обходной путь. Я могу прямо сейчас представить, какой беспорядок люди, которые переводят мои приложения, будут помещать в мои файлы ... :)
OcuS
1
Как составить строку формата для "нескольких" чисел (числа, оканчивающиеся на 2, 3, 4, но не заканчивающиеся на 12, 13, 14)?
vokilam
3
Вау, это некрасиво. Подобная логика должна идти в коде, а не в переводах. Множественное число должно помогать только с языковыми различиями для чисел. С рекомендацией для «немногих» вы успешно перенесли логику в переводы и переводы в логику.
DennisK 03
2
Это определенно не лучший способ обхода. Просто добавьте строку для no_items и предложение if перед. If (items.count == 0) {используйте строку} else {используйте множественное число} и ЗАТЕМ позвольте переводчикам решить проблему в файле
strings.xml
2
@ MartínMarconcini, вы правы ... более 3 лет спустя :-)
GreyBeardedGeek
47

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

Обратите внимание, что выбор сделан исходя из грамматической необходимости. Строка для нуля на английском языке будет проигнорирована, даже если количество равно 0, потому что 0 грамматически не отличается от 2 или любого другого числа, кроме 1 («ноль книг», «одна книга», «две книги» и т. Д. на). Не заблуждайтесь и тем фактом, что, скажем, два звука, как будто они могут применяться только к величине 2: язык может требовать, чтобы 2, 12, 102 (и так далее) обрабатывались как друг друга, но по-разному. количества. Положитесь на своего переводчика, чтобы знать, на каких различиях на самом деле настаивает их язык.

В заключение, «ноль» используется только для определенных языков (то же самое для «два», «несколько» и т. Д.), Потому что другие языки не имеют специального спряжения, и поэтому поле «ноль» считается ненужным.

ByteMe
источник
4
«Строка для нуля на английском языке будет проигнорирована, даже если количество равно 0, потому что 0 грамматически не отличается от 2» ... как насчет «У меня нет фруктов» или «У меня есть яблоко и апельсин»?
Джеффри Блаттман
4
Есть много разных способов обозначить слово 0 vs 2. Но идея этих ресурсов состоит в том, чтобы использовать их с числами. Примерно так: у меня 0 яблок. У меня 1 яблоко. У меня 2 яблока.
ByteMe 08
4
это очень плохо. им не следовало пытаться делать предположения, зависящие от языка. всех, кого я знаю, что употребляет множественное число, это смущает.
Джеффри Блаттман
2
@ miracle2k: сообщения не переводятся по категориям, поэтому любые категории или сообщения, которые у вас есть на одном языке, не влияют на другой язык. Итак, если у вас есть сообщение класса «ноль» на английском языке только для нулевого счета, а другой язык использует класс «ноль» для счетчиков 10 100, это не имеет значения, потому что вы не переводите сообщение класса «ноль». С другой стороны: если вы создаете отдельный ресурс сообщения для счетчика 0 на английском языке, вы создаете проблему рабочего процесса для переводчиков, потому что этот случай уже охвачен исходным сообщением нулевого класса на их языке.
Basel Shishani
2
Верно, что включение «нулевой» категории в английском языке в качестве удобной функции можно было бы сделать, но это не «чисто», а только поощряет плохое поведение. У вас может возникнуть идея поместить текст, побуждающий пользователя добавлять элементы в «нулевую» строку - тогда, возможно, непереводимый. Строго говоря, пример OP («Нет элементов») уже демонстрирует эту проблему, потому что переводчик может захотеть сформулировать нулевой регистр, используя слова. Это не проблема рабочего процесса, если вы считаете, что «% s item» является единственной версией «% s items», а «No items» - чем-то совершенно другим с лингвистической точки зрения.
miracle2k 02
15

Вот обходной путь, который я использую для решения этой проблемы без переключения на 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-строкового ресурса.

Джастин Фидлер
источник
Неужели при этом "<item amount =" zero ">" никогда не будет использоваться?
разработчик Android
4
Правильный '<item amount = "zero">' использоваться не будет. У меня это есть, потому что я думаю, что это помогает прояснить предполагаемое использование этого значения для нуля.
Джастин Фидлер,
Так и думал. Это может помочь переводчикам понять, что происходит.
разработчик Android
13

Android использует систему множественного числа CLDR, и это просто не так, как это работает (поэтому не ожидайте, что это изменится).

Система описана здесь:

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

Короче говоря, важно понимать, что «один» не означает число 1. Вместо этого эти ключевые слова являются категориями, а конкретные числа n, которые принадлежат каждой категории, определяются правилами в базе данных CLDR:

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

Хотя, похоже, не существует языка, который использует «ноль» для чего-либо, кроме 0, есть языки, которые присваивают 0 «единице». Конечно, существует множество случаев, когда «два» содержит другие числа, чем просто 2.

Если Android позволяет вам делать то, что вы намеревались, ваши приложения не могут быть должным образом переведены на любое количество языков с более сложными правилами множественного числа.

miracle2k
источник
5
Будьте осторожны при отправке копий и вставок шаблонных / дословных ответов на несколько вопросов, они, как правило, отмечаются сообществом как "спам". Если вы это делаете, то обычно это означает, что вопросы дублируются, поэтому вместо этого отметьте
Кев
3

Я написал расширение Kotlin для обработки всех возможных сценариев.

У меня есть zeroResIdкак необязательный, поэтому иногда мы хотим обработать ноль, отображая «Нет элементов», а не «0 элементов».

В английском языке ноль грамматически трактуется как множественное число.

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

На английском языке строка для нуля игнорируется, даже если количество равно 0, потому что 0 грамматически не отличается от 2 или любого другого числа, кроме 1 («ноль книг», «одна книга», «две книги» и т. Д. на).

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)
    }
}
Морган Ко
источник
2

Если вы используете привязку данных, вы можете обойти это примерно так:

<TextView ... 
android:text="@{collection.size() > 0 ? @plurals/plural_str(collection.size(), collection.size()) : @string/zero_str}"/>
отметка
источник