Локализация сообщений динамического множественного существительного (например, "5 элементов обработано" ) на iPhone с помощью Objective-C
В моем текущем приложении у меня есть код, который отображает сообщение, например. "Обработано 5 предметов". Чтобы фраза была грамматически правильной, то есть, должен ли он быть "5 Item" или "5 Items", я использую следующий код:
int numItems = 5;
NSString *myString = [[NSString alloc] initWithFormat:@"%d Item%@ Processed", numItems, (numItems == 1 ? @"" : @"s")];
Теперь это отлично. Но я локализую свое приложение и хочу, чтобы текст был грамматически корректным на всех языках, на которых я переводил приложение. Я мог бы сделать что-то вроде этого:
int numItems = 5;
NSString *myString = (numItems == 1 ?
NSLocalizedStringWithTable(@"%d Item Processed", @"myApp", @"singular version") :
NSLocalizedStringWithTable(@"%d Items Processed", @"myApp", @"plural version"));
Однако не все языки имеют одинаковые правила для использования множественных чисел! Например, (простить мой очень конкретный пример здесь) на русском языке, существительные, модифицированные номерами, заканчивающимися последней цифрой 1 (т.е. 21, 31, но не 11), принимают номинативный случай, числа, оканчивающиеся на 2-4, принимают родительный падеж единственного числа, и 5+ принимают родительный падеж множественного случая. Это потребует гораздо более серьезной логики для обработки того, как правильно распределить определенное существительное в грамматически правильной форме, и эта логика не будет соответствовать английской логике. Поэтому теоретически я не могу иметь грамматическую логику в моем коде Objective-C, но скорее должен иметь грамматическую логику в файле строк. Есть ли способ сделать это? Как люди переводят динамический текст для своих приложений, чтобы он оставался грамматически правильным?
Ответы
Ответ 1
Начиная с iOS 7, Foundation framework имеет встроенную поддержку для плюрализации.
Вот краткое руководство по его использованию:
Создайте файл plist с именем Localizable.stringsdict
Английская локализация:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>%d tasks waiting for action</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@[email protected] waiting for action</string>
<key>tasks</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>A task is</string>
<key>two</key>
<string>Two tasks are</string>
<key>other</key>
<string>%d tasks are</string>
</dict>
</dict>
</dict>
</plist>
Польская локализация:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>%d tasks waiting for action</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>Masz %#@[email protected] do zrobienia</string>
<key>zadanie</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>jedno zadanie</string>
<key>few</key>
<string>%d zadania</string>
<key>other</key>
<string>%d zadań</string>
</dict>
</dict>
</dict>
</plist>
И, наконец, в вашем файле реализации вы можете вызвать словарь следующим образом:
cell.tasksInfoLabel.text = [NSString localizedStringWithFormat:NSLocalizedString(@"%d tasks waiting for action", @"%d tasks waiting for action"), (long)taskCount];
EDIT:
Спасибо Zaphod за указание этого → : вам также нужно создать файл Localizable.strings вместе с .stringdict, чтобы работать с плюрализацией (даже если он пуст).
Ответ 2
Моя команда разработала библиотеку с открытым исходным кодом для обработки именно этой ситуации, просмотрите нашу iOS i18n множественную библиотеку на github.
Основная предпосылка заключается в том, что ключи для множественных строк расширены, чтобы содержать их множественную форму в соответствии с
Затем поиск будет выполнен с использованием функции SLPluralizedString
SLPluralizedString(@"%d Items Processed", numItems, @"Number of items processed");
Во время выполнения для английского языка строка "1 элемент обработана" или "% d элементов обработана" будет возвращена в зависимости от значения numItems.
Русский файл будет выглядеть следующим образом:
"%d Items Processed##{one}" = "%d элемент обработан";
"%d Items Processed##{few}" = "%d элемента обработано";
"%d Items Processed##{many}" = "%d элементов обработано";
"%d Items Processed##{other}" = "%d элемента обработано";
Затем ваш код для поиска "Обработанные элементы" для русского или любого другого языка не должен был меняться, и библиотека вернет правильную строку в соответствии с множественными правилами CLDR для этого конкретного языка.
Пожалуйста, не стесняйтесь делиться своими мыслями о библиотеке, предложениями, улучшениями и т.д.
Ответ 3
На английском языке имеется всего 2 формы множественного числа, например. "1 файл" и "5 файлов". На русском языке существует 3 множественные формы (101 файл, 2 файла, 11 файлов), если вы не подсчитаете нецелые числа. Фактически, на языке может быть до 6 множественных форм (например, арабский имеет 6). Кажется, есть 3 способа решения проблемы, просто выберите то, что достаточно хорошо, но не слишком сложно для вас:
-
Попробуйте использовать сообщения с множественной нейтралью, например. "Количество обработанных элементов:% d" вместо "% d обработанный элемент" обработано% d элементов.
-
Поддержка локализации для каждой формы множественного числа, до 6.
"%d Gold Coins##{PluralForm0}" -> "%d золотая монета" // e.g. 1 gold coin
"%d Gold Coins##{PluralForm1}" -> "%d золотые монеты" // e.g. 2 gold coins
"%d Gold Coins##{PluralForm2}" -> "%d золотых монет" // e.g. 5 gold coins
…
"%d Gold Coins##{PluralForm5}" -> "%d How did we get here if this is not Arabic???"
Зная значение% d и целевого языка, вам придётся обнаруживать номер формы множественного числа во время выполнения, т.е. реализовать что-то вроде
unsigned int "NumberToPluralFormNumber(unsigned int number, const std::string& langCode);
метод. Если вы поддерживаете только 2-5 языков, а числа в сообщениях всегда являются неотрицательными ints, на самом деле довольно просто реализовать его без какой-либо 3d-партии lib, вы можете копировать/вставлять C-совместимые однострочные для каждого язык из http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html. Обратите внимание, что он действителен только для неотрицательных целых чисел, поэтому число множественных форм может отличаться от того, что говорит unicode.org.
- 3-сторонние библиотеки.
Ответ 4
Я бы рассмотрел альтернативные способы отображения одной и той же информации, возможно, что-то вроде:
@"Items processed: %d"
MDC имеет длинный, исчерпывающий список правил плюрализации, если вы заинтересованы, но я действительно не думаю, что это стоит усилий для реализации всех этих правил. Еще одна вещь, которую вы, возможно, придется рассмотреть, - это если какие-либо языки будут появляться после плюрализации, так как она выкинет вашу строку формата (я не могу думать о каких-либо языках, которые были бы на моей голове, но, возможно, месте для осторожности в любой из ваших более сложных локализованных строк с задействованными числами).