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 позицій". Чи підтримується значення "нуль" лише на арабській мові, як вказується в документації? Або мені чогось не вистачає?


21
Про цю проблему повідомляється тут code.google.com/p/android/issues/detail?id=8287 . Ще не встановлено: /
OcuS

5
Я не думаю, що це помилка: зауважте, що вибір здійснюється на основі граматичних потреб. Рядок для нуля англійською мовою буде ігноруватися, навіть якщо кількість дорівнює 0, оскільки 0 не граматично відрізняється від 2 або будь-якого іншого числа, крім 1 ("нуль книг", "одна книга", "дві книги" тощо on)
ByteMe

1
На жаль, не всі мови ідентичні англійській
Арман,

2
Я думаю, що це помилка в тому сенсі, що, якщо не брати до уваги граматику, 99% випадків, коли мені потрібно було це використовувати, мені потрібна була відсутність функціональних можливостей. Так, так, я вважаю, що ця помилка, яку Google, мабуть, ніколи не виправить. Як такий, необхідний обхідний шлях. Розумієте, API існують для того, щоб допомогти споживачам, зробити щось, а не представляти виключно абстрактні поняття з реального світу. Якби Zero підтримували нестандартно, обидві групи (і ті, хто потребує граматичної коректності, і ті, хто цього не робить), могли б піти, не використовуючи щось інше.
Мартін Маркончіні

Відповіді:


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

Ви можете прочитати більше про рядки форматування в javadocs для MessageFormat


7
Хороший обхідний шлях. Я можу тільки зараз уявити, який безлад люди, які перекладають мої програми, вкладуть у мої файли ... :)
OcuS

1
Як скласти рядок формату для "кількох" чисел (цифр, що закінчуються 2,3,4, але не закінчуються 12,13,14)?
vokilam

3
Ого, це негарно. Така логіка повинна йти в коді, а не в перекладах. Множини повинні допомагати лише з різницею мов для чисел. З рекомендацією для "кількох", ви успішно перенесли логіку в переклади та переклади в логіку.
DennisK

2
Це, звичайно, не хороший обхідний шлях. Просто додайте рядок для no_items та речення 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 (і так далі) всі трактувались однаково, але по-різному для інших кількості. Покладайтеся на свого перекладача, щоб знати, на яких відмінностях насправді наполягає їхня мова.

На закінчення "нуль" використовується лише для певних мов (те саме стосується "двох" декількох "тощо), оскільки інші мови не мають спеціального спряження, і тому поле" нуль "вважається непотрібним


4
"Рядок для нуля англійською мовою буде ігноруватися, навіть якщо кількість дорівнює 0, оскільки 0 граматично не відрізняється від 2" ... як щодо "У мене немає фруктів" проти "У мене є яблуко та апельсин"?
Джеффрі Блаттман,

4
Існує безліч різних способів сформулювати слово 0 проти 2. Але ідея цих ресурсів полягає у використанні їх із числом. Отже, щось на зразок: у мене 0 яблук. У мене є 1 яблуко. У мене є 2 яблука.
ByteMe

4
це дуже погано. їм не слід було намагатися робити припущення для конкретної мови. всіх, кого я знаю, що вживають множину, це бентежило.
Джеффрі Блаттман,

2
@ miracle2k: Повідомлення не перекладаються за категоріями, тому будь-які категорії чи повідомлення на одній мові не впливають на іншу мову. Отже, якщо у вас є повідомлення класу "нуль" англійською мовою лише для нульового рахунку, а інша мова використовує клас "нуль" для лічильників 10 100, що не має значення, оскільки ви не перекладаєте повідомлення класу "нуль". З іншого боку: якщо ви створюєте окремий ресурс повідомлень для підрахунку 0 англійською мовою, то ви створюєте проблему робочого циклу для перекладачів, оскільки цей випадок вже охоплений оригінальним повідомленням нульового класу їхньою мовою.
Базель Шишані,

2
Це правда, що включення категорії "нуль" англійською мовою як зручності можна зробити, але це не "чисто", а лише заохочує погану поведінку. Ви можете отримати ідею розміщення тексту, що заохочує користувача додавати елементи в "нульовий" рядок - тоді, можливо, не перекладається. Власне кажучи, приклад OP ("Немає елементів") уже демонструє цю проблему, оскільки перекладач, можливо, захоче також сформулювати 0-регістр за допомогою слів. Це не проблема робочого процесу, якщо ви вважаєте, що "% s item" є єдиною версією "% s items", а "No items" - це щось, що мовно відрізняється.
miracle2k

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 quantity =" zero ">" насправді ніколи не буде використано?
розробник android

4
Правильний '<item quantity = "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 де дозволити вам робити те, що задумали, ваші програми не можуть бути належним чином перекладені на будь-яку кількість мов із більш складними правилами множини.


5
Будьте обережні, публікуючи копіювальні та вставні шаблонні / дослівні відповіді на декілька запитань, які спільнота, як правило, позначає як "спам". Якщо ви робите це, то це, як правило, означає, що запитання є дублікатами, тому натомість позначте їх як такі: stackoverflow.com/questions/8473816
Кев

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}"/>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.