Многие сталкиваются с такой проблемой, как поиск подходящего решения в решение вопроса с мультиязычностью и переводами в целом. Мало кто знает, но в PHP
есть такое расширение Intl
, которое из коробки поддерживает различные фичи связанные с переводами, форматированием и прочими плюхами полезными не только для мультиязычных проектов, но и одноязычных тоже. Все это называется - интернационализация. И сегодня я хочу осветить такой вовсе не странный и весьма простой вопрос с переводами. Дак, как же все-таки сделать это просто, быстро и безболезненно? Читайте дальше! =)
В первую очередь, что необходимо сделать, это убедиться, что у вас установлено расширение Intl
с PHP
,
$ php -m | grep intl
(если вывод будет пустой, то значит расширения этого у вас нет и его следует установить),
да и вообще "покурить" документацию к нему. Расширение Intl
реализовано на базе ICU
, а значит, все, что справедливо для ICU
справедливо и для нас.
Перейдем к коду.
Подготовим наш конфигурационный файл с сообщениями:
// config/messages.php
<?php declare(strict_types=1);
return [
'User' => [
'ru-RU' => [
'SAY_HELLO' => 'Привет, {0}!',
'NUMBER_OF_APPLE' => 'У меня {0, plural, one{# яблоко} few{# яблока} many{# яблок} other{# яблока}}',
],
'en-US' => [
'SAY_HELLO' => 'Hello, {0}!',
'NUMBER_OF_APPLE' => 'I have {0, plural, one{# apple} other{# apples}}',
],
],
];
И сделаем интерфейс от которого мы позже можем плясать и без проблемно заменять реализации в проекте (привет SOLID):
<?php declare(strict_types=1);
namespace I18n;
interface TranslatorInterface
{
public function t(string $message, string $domain, ...$params): string;
}
Здесь все просто: первый аргумент - сообщение, второй - группа сообщений, третий - дор. параметры для сообщения, если есть.
А сейчас реализуем сам транслятор сообщений:
<?php declare(strict_types=1);
namespace I18n;
use MessageFormatter;
class FsTranslator implements TranslatorInterface
{
/**
* @var array
*/
private $messages;
/**
* @var string
*/
private $locale;
public function __construct(array $messages, string $locale)
{
$this->messages = $messages;
$this->locale = $locale;
}
public function t(string $message, string $domain, ...$params): string
{
if (isset($this->messages[$domain][$this->locale][$message])) {
$message = $this->messages[$domain][$this->locale][$message];
}
return (new MessageFormatter($this->locale, $message))->format($params);
}
}
В классе FsTranslator
мы использовали MessageFormatter
из Intl
, документация к нему здесь.
Использование:
// Russian
$translator = new \I18n\FsTranslator(require __DIR__ . '/config/messages.php', 'ru-RU');
$translator->t('SAY_HELLO', 'User', 'Иван'); // Привет, Иван!
$translator->t('NUMBER_OF_APPLE', 'User', 1); // У меня 1 яблоко
$translator->t('NUMBER_OF_APPLE', 'User', 2); // У меня 2 яблока
$translator->t('NUMBER_OF_APPLE', 'User', 3); // У меня 3 яблока
$translator->t('NUMBER_OF_APPLE', 'User', 5); // У меня 5 яблок
$translator->t('NUMBER_OF_APPLE', 'User', 1.5); // У меня 1,5 яблока
// English
$translator = new \I18n\FsTranslator(require __DIR__ . '/config/messages.php', 'en-US');
echo $translator->t('SAY_HELLO', 'User', 'Ivan'); // Hello, Ivan!
$translator->t('NUMBER_OF_APPLE', 'User', 1); // I have 1 apple
$translator->t('NUMBER_OF_APPLE', 'User', 2); // I have 2 apples
$translator->t('NUMBER_OF_APPLE', 'User', 3); // I have 3 apples
$translator->t('NUMBER_OF_APPLE', 'User', 5); // I have 5 apples
$translator->t('NUMBER_OF_APPLE', 'User', 1.5); // I have 1,5 apples
Вот и все. Это, конечно, не все возможности. Все я, к сожалению, охватить не могу. Поэтому, читайте документацию к ICU. =)
Полезные ссылки: