Подтвердить что ты не робот

Локализация сообщений динамического множественного существительного (например, "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, но скорее должен иметь грамматическую логику в файле строк. Есть ли способ сделать это? Как люди переводят динамический текст для своих приложений, чтобы он оставался грамматически правильным?

4b9b3361

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