Статьи

Демистификация локализации: Php-Intl для всех

Иллюстрация глобус

Большинство приложений выполняют операции с учетом локали, такие как работа с текстами, датами, часовыми поясами и т. Д. Расширение PHP Intl предоставляет хороший API для доступа к широко известным функциям библиотеки ICU .

Установка

Расширение установлено по умолчанию на PHP 5.3 и выше. Вы можете найти его, выполнив следующую команду:

php -m | grep 'intl'

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

 sudo apt-get update
sudo apt-get install php5-intl

Если вы используете PHP7 на своем компьютере, вам нужно добавить PPA ( ppa:ondrej/php

 # Add PPA
sudo add-apt-repository ppa:ondrej/php-7.0
# Update repository index
sudo apt-get update
# install extension
sudo apt-get install php7.0-intl

Форматирование сообщений

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

Простые сообщения

Мы собираемся начать с простого сообщения, содержащего заполнитель. Заполнители — это шаблоны, заключенные в фигурные скобки. Вот пример:

 var_dump(
    MessageFormatter::formatMessage(
        "en_US",
        "I have {0, number, integer} apples.",
        [ 3 ]
    )
);
 // output

string(16) "I have 3 apples."

Аргументы, передаваемые методу MessageFormatter::formatMessage

  • Локаль сообщения.
  • Строковое сообщение.
  • Заполнители данных.

Заполнитель {0, number, integer}numberinteger Мы также можем использовать именованные аргументы для заполнителей. Пример ниже выдаст тот же результат.

 var_dump(
    MessageFormatter::formatMessage(
        "en_US",
        "I have {number_apples, number, integer} apples.",
        [ 'number_apples' => 3 ]
    )
);

Разные языки имеют разные системы счисления, такие как арабский , индийский и т. Д.

арабские цифры

Предыдущий пример ориентирован на локаль en_US Давайте изменим его на ar

 var_dump(
    MessageFormatter::formatMessage(
        "ar",
        "I have {number_apples, number, integer} apples.",
        [ 'number_apples' => 3 ]
    )
);
 string(17) "I have ٣ apples."

Мы также можем изменить его на бенгальский язык ( bn

 var_dump(
    MessageFormatter::formatMessage(
        "bn",
        "I have {number_apples, number, integer} apples.",
        [ 'number_apples' => 3 ]
    )
);
 string(18) "I have ৩ apples."

Пока что мы работали только с числами. Давайте посмотрим на другие типы, которые мы можем использовать.

 $time = time();
var_dump( MessageFormatter::formatMessage(
    "en_US",
    "Today is {0, date, full} - {0, time}",
    array( $time )
) );
 string(47) "Today is Wednesday, April 6, 2016 - 11:21:47 PM"
 var_dump( MessageFormatter::formatMessage(
    "en_US",
    "duration: {0, duration}",
    array( $time )
) );
 string(23) "duration: 405,551:27:58"

Мы также можем прописать пропущенные числа.

 var_dump( MessageFormatter::formatMessage(
    "en_US",
    "I have {0, spellout} apples",
    array( 34 )
) );
 string(25) "I have thirty-four apples"

Он также работает на разных языках. Вот пример использования арабского языка.

 var_dump( MessageFormatter::formatMessage(
    "ar",
    "لدي {0, spellout} تفاحة",
    array( 34 )
) );
 string(44) "لدي أربعة و ثلاثون تفاحة"
тип аргумента argStyle
число целое число, валюта, проценты
Дата короткий, средний, длинный, полный
время короткий, средний, длинный, полный
spellout короткий, средний, длинный, полный
порядковый
продолжительность

плюрализация

Важной частью локализации нашего приложения является управление множественными сообщениями, чтобы сделать наш интерфейс как можно более интуитивным. Пример яблок выше сделает эту работу. Вот как должны выглядеть сообщения в этом случае.

  • ( number_apples
  • ( number_apples
  • ( number_apples
 var_dump( MessageFormatter::formatMessage(
    "en_US",
    'I have {number_apples, plural, =0{no apples} =1{one apple} other{# apples}}',
    array('number_apples' => 10)
) );
 // number_apples = 0
string(16) "I have no apples"

// number_apples = 1
string(16) "I have one apple"

// number_apples = 10
string(16) "I have 10 apples"

Синтаксис действительно прост, и большинство пакетов плюрализации используют этот синтаксис. Проверьте документацию для более подробной информации.

 {data, plural, offsetValue =value{message}... other{message}}
  • data
  • plural
  • offsetValueoffset:value Вычитает смещение из значения.
  • =value{message} Мы можем повторить эту часть несколько раз ( =0{no apples} =1{one apple} =2{two apple}
  • other{message}switch - case Символ #data

Выбор

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

 var_dump( MessageFormatter::formatMessage(
    "en_US",
    'The value of {0,number} is {0, choice,
                                        0 # between 0 and 19 |
                                        20 # between 20 and 39 |
                                        40 # between 40 and 59 |
                                        60 # between 60 and 79 |
                                        80 # between 80 and 100 |
                                        100 < more than 100 }',
    array(60)
) );
 string(38) "The value of 60 is between 60 and 79 "

В этом случае argTypechoice

 {value, choice, choiceStyle}

Официальное определение из документации ICU :

 choiceStyle = number separator message ('|' number separator message)*

number = normal_number | ['-']  ∞ (U+221E, infinity)
normal_number = double value (unlocalized ASCII string)

separator = less_than | less_than_or_equal
less_than = '<'
less_than_or_equal = '#' |  ≤ (U+2264)

Примечание . Разработчики ICU не рекомендуют использовать тип выбора.

Выбрать

Иногда нам нужно что-то вроде компонента выбора опции интерфейса. Страницы профиля используют это для обновления сообщений пользовательского интерфейса в соответствии с полом пользователя и т. Д. Вот пример:

 var_dump( MessageFormatter::formatMessage(
    "en_US",
    "{gender, select, ".
      "female {She has some apples} ".
      "male {He has some apples.}".
      "other {It has some apples.}".
    "}",
    array('gender' => 'female')
) );
 string(19) "She has some apples"

Шаблон определяется следующим образом:

 {value, select, selectStyle}

// selectStyle
selectValue {message} (selectValue {message})*

Аргумент message Следующая часть объяснит сложный пример, где мы объединяем несколько шаблонов. Проверьте документацию ICU для более подробной информации.

Сложные случаи

До сих пор мы видели несколько простых примеров, таких как множественное число, выбор и т. Д. Некоторые случаи являются более сложными, чем другие. Документация ICU имеет очень хороший пример, иллюстрирующий это. Мы будем вставлять по частям, чтобы было проще понять.

 var_dump( MessageFormatter::formatMessage(
    "en_US",
    "{gender_of_host, select, ".
      "female {She has a party} ".
      "male {He has some apples.}".
      "other {He has some apples.}".
    "}",
    array('gender_of_host' => 'female', "num_guests" => 5, 'host' => "Hanae", 'guest' => 'Younes' )
) );

Это тот же пример, который мы использовали ранее, но вместо простого сообщения мы настраиваем его в зависимости от значения num_guests

 var_dump( MessageFormatter::formatMessage(
    "en_US",
    "{gender_of_host, select, ".
      "female {".
        "{num_guests, plural, offset:1 ".
          "=0 {{host} does not have a party.}".
          "=1 {{host} invites {guest} to her party.}".
          "=2 {{host} invites {guest} and one other person to her party.}".
          "other {{host} invites {guest} and # other people to her party.}}}".
      "male {He has some apples.}".
      "other {He has some apples.}}",
    array('gender_of_host' => 'female', "num_guests" => 5, 'host' => "Hanae", 'guest' => 'Younes' )
) );

Обратите внимание, что мы используем offset:1num_guests

 string(53) "Hanae invites Younes and 4 other people to her party."

Вот полный фрагмент этого примера.

 var_dump( MessageFormatter::formatMessage(
    "en_US",
    "{gender_of_host, select, ".
      "female {".
        "{num_guests, plural, offset:1 ".
          "=0 {{host} does not have a party.}".
          "=1 {{host} invites {guest} to her party.}".
          "=2 {{host} invites {guest} and one other person to her party.}".
          "other {{host} invites {guest} and # other people to her party.}}}".
      "male {".
        "{num_guests, plural, offset:1 ".
          "=0 {{host} does not have a party.}".
          "=1 {{host} invites {guest} to his party.}".
          "=2 {{host} invites {guest} and one other person to his party.}".
          "other {{host} invites {guest} and # other people to his party.}}}".
      "other {".
        "{num_guests, plural, offset:1 ".
          "=0 {{host} does not have a party.}".
          "=1 {{host} invites {guest} to their party.}".
          "=2 {{host} invites {guest} and one other person to their party.}".
          "other {{host} invites {guest} and # other people to their party.}}}}",
    array('gender_of_host' => 'female', "num_guests" => 5, 'host' => "Hanae", 'guest' => 'Younes' )
) );

Измените количество гостей, чтобы проверить все типы сообщений.

 // num_guests = 2
string(55) "Hanae invites Younes and one other person to her party."

// num_guests = 1
string(34) "Hanae invites Younes to her party."

// num_guests = 0
string(28) "Hanae does not have a party."

Разбор сообщений

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

 $messageFormater = new MessageFormatter("en_US", 'I have {0, number}');
var_dump( $messageFormater->parse("I have 10 apples") );
 array(1) {
  [0]=>
  int(10)
}

Проверьте документацию для более подробной информации о разборе сообщений.

Вывод

В этой вводной статье мы узнали о локализации наших сообщений с использованием расширения PHP Intl. Следующая часть будет посвящена форматированию чисел и дат, а также тому, как работать с календарями. Если у вас есть какие-либо вопросы о том, что мы уже рассмотрели, вы можете оставить их в комментариях ниже!