Переклади

Дата оновлення перкладу 2022-12-23

Переклади

Термін "інтернаціоналізація" (часто скорочуваний як i18n) відноситься до процесу абстрагування рядків та інших специфічних для конкретної локалі частин з вашого додатку, та їх переміщення на такий рівень, де вони можуть бути перекладені та конвертовані на підставі локалі користувача (тобто в залежності від мови та країни). Для тексту це означає, що його потрібно оточити спеціальною функцією, здатною перекладати текст (або "повідомлення") мовою користувача:

1
2
3
4
5
6
// текст буде *завжди* відображатися англійською
echo 'Hello World';

// текст може бути перекладений мовою кінцевого користувача, або,
// за замовчуванням, англійською
echo $translator->trans('Hello World');

Note

Термін локаль у загальних рисах відноситься до мови та країни користувача. Це може бути будь-який рядок, використовуваний вашим додатком для управління перекладами та іншими різницями форматів (наприклад, формат валюти). Рекомендується використовувати стандарт ISO 639-1 для мовних кодів, підкреслювання (_), а потім ISO 3166-1 alpha-2 для кодів країн (наприклад, для French/France (французька, Франція) вийде fr_FR).

Процес перекладу має декілька кроків:

  1. Підключити та сконфігурувати сервіс перекладів Symfony.
  2. Абстрагувати рядки (так звані "повідомлення"), огорнувши їх у виклики Translator ("_");
  3. Створити ресурси/файли перекладів для кожної підтримуваної локалі, які будуть перекладати кожне повідомлення у додатку;
  4. Визначити, установити та управляти локаллю користувачат для запиту і, за бажанням, для всієї сесії користувача.

Установка

Спочатку виконайте цю команду, щоб встановити перекладач, перед його використанням:

1
$ composer require symfony/translation

Конфігурація

Попередня команда створює початковий файл конфігурації, де ви можете визначити локаль додатку за замовчуванням та каталог, де знаходяться файли перекладу:

  • YAML
  • XML
  • PHP
1
2
3
4
5
# config/packages/translation.yaml
framework:
    default_locale: 'en'
    translator:
        default_path: '%kernel.project_dir%/translations'

Локаль, використовувана у перекладах, та ж, що зберігається за запитом. Вона зазвичай встановлюється за допомогою атрибута _locale на ваших маршрутах (дивіться ).

Базовий переклад

Переклад тексту здійснюється сервісом translator (Translator). Для перекладу текстового блоку (який називається повідомленням), використайте метод trans(). Припустимо, наприклад, що ви перекладаєте просте повідомлення всередині контролера:

1
2
3
4
5
6
7
8
9
// ...
use Symfony\Contracts\Translation\TranslatorInterface;

public function index(TranslatorInterface $translator)
{
    $translated = $translator->trans('Symfony is great');

    // ...
}

При виконанні цього коду, Symfony спробує перекласти повідомлення "Symfony is great" («Symfony чудова»), засновуючись на locale користувача. Для цього вам необхідно вказати Symfony, як перекласти це повідомлення за допомогою "ресурсу для перекладу", який зазвичай являє собою набір перекладених повідомлень для даної локалі. Цей "словник" перекладів може бути створений у декількох різних форматах:

  • YAML
  • XML
  • PHP
1
2
# translations/messages.fr.yaml
Symfony is great: J'aime Symfony

Щоб дізнатися, де ці файли мають бути розташовані, дивіться .

Тепер, якщо мовною локаллю користувача буде французька (наприклад, fr_FR або fr_BE), це повідомлення буде перкладено як J'aime Symfony. Ви також можете перекладати повідомлення всередині ваших шаблонів .

Використання реальних повідомлень або повідомленю ключових слів

Цей приклад ілюструє дві різні філософії при створенні повідомлень для перекладу:

1
2
3
$translator->trans('Symfony is great');

$translator->trans('symfony.great');

У першому методі, повідомлення написані мовою локалі за замовчуванням (у даному випадку - англійською). Це повідомлення потім використовується як "id" при створенні перекладів.

У другому методі, повідомлення насправді є "ключовими словами", які передають ідею повідомлення. Повідомлення ключових слів потім використовується як "id" для будь-яких перекладів. У даному випадку, переклади мають бути зроблені для локалі за замовчуванням (тобто для перекладу symfony.great на Symfony is great).

Другий метод зручний, так як повідомлення ключових слів не треба буде змінювати в кожному файлі перекладу, якщо ви вирішите, що повідомлення насправді має читатися як "Symfony is really great" у локалі за замовчуванням.

Вибір методу для використання залежить лише від вас, але формат "ключових слів" часто рекомендується для багатомовних додатків, в той час як для спільних пакетів, що містять джерела перекладу, ми рекомендуємо реальні повідомлення, щоб ваш застосунок міг обирати вимкнути шар перекладу, і ви побачите читане повідомлення.

Додатково, формати файлів php та yaml підтримують вкладені id, щоб уникнути повторень, якщо ви використовуєте ключові слова замість реального тексту для ваших id:

  • YAML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
symfony:
    is:
        # id - symfony.is.great
        great: Symfony is great
        # id = symfony.is.amazing
        amazing: Symfony is amazing
    has:
        # id - symfony.has.bundles
        bundles: Symfony has bundles
user:
    # id - user.login
    login: Login

Процес перекладу

Для того щоб перекласти повідомлення, Symfony використовує наступний процес, використовуючи метод trans():

  • Визначається locale поточного користувача, яка зберігається у запиті;
  • Завантажується каталог (тобто велика колекція) перекладів повідомлень з відповідних джерел, визначених для locale (наприклад, fr_FR). Повідомлення з резервних локалей , також завантажуються та додаються у каталог, якщо вони ще не були створені. В кінцевому рахунку виходить великий "словник" з перекладами. Цей каталог кешується у виробництві для мінімізації впливу на продуктивність.
  • Якщо повідомлення знаходиться у каталозі, то повертається його переклад. Якщо ж ні, перекладач повертає оригінал повідомлення.

Tip

При перекладі рядків, які знаходяться не у домені за замовчуванням (messages), ви маєте вказати домен в якості третього аргументу trans():

1
$translator->trans('Symfony is great', [], 'admin');

Формат повідомлень

Іноді, повідомлення, що містить змінну, має бути перекладеним:

1
2
// ...
$translated = $translator->trans('Hello '.$name);

Однак, створення перекладу для цього рядку неможливе, так як перекладач намагається знайти повідомлення включно зі змінними частинами (наприклад, "Hello Ryan" або "Hello Fabien").

Інша складність виникає, коли у вас є переклади, які можуть бути або не бути множинними, в залежності від якоїсь змінної:

1
2
There is one apple.
There are 5 apples.

Щоб впоратися з такими ситуаціями, Symfony слідує синтаксису ICU MessageFormat, використовуючи PHP-клас MessageFormatter. Прочитайте більше про це у .

Tip

Якщо ви не використовуєте синтаксис ICU MessageFormat у ваших файлах перекладів, передайте параметр під назвою "%count%", щоб обрати кращу множинну форму повідомлення:

1
{{ message|trans({'%name%': '...', '%count%': 1}, 'app') }}

Змінна message має включати в себе всі версії цього повідомлення, засновані на значенні параметра count. Наприклад:

1
{0}%name% has no apples|{1}%name% has one apple|]1,Inf[ %name% has %count% apples

Об'єкти, що перекладаються

Іноді переклад змісту в шаблонах може бути громіздким, так як вам потрібні початкове повідомлення, параметри перекладу та домен перекладу для кожного повідомлення. Створення перекладу у вашому контролері або сервісах спрощує ваші шаблони, але вимагає впровадження сервісу перекладача в інші частини вашого додатку, та його імітації у ваших тестах.

Замість перекладу рядку під час його створення, ви можете використати "об'єкт, що перекладається", який є екземпляром класу TranslatableMessage. Цей об'єкт зберігає всю необхідно інформацію для повного перекладу його змісту за необхідності:

1
2
3
4
5
6
7
use Symfony\Component\Translation\TranslatableMessage;

// перший аргумент обов'язковий і є оригіналом повідомлення
$message = new TranslatableMessage('Symfony is great!');
// необов'язковий другий аргумент визначає параметри перекладу, а
// необов'язковий третій аргумент є доменом перекладу
$status = new TranslatableMessage('order.status', ['%status%' => $order->getStatus()], 'store');

Тепер шаблони набагато простіші, так як ви можете передати об'єкти, що перекладаються, фільтру trans:

1
2
<h1>{{ message|trans }}</h1>
<p>{{ status|trans }}</p>

Tip

Також існує функція під назвою t(), доступна як у Twig, так і у PHP, як скорочення для створення об'єктів, що перекладаються.

Переклади у шаблонах

У більшості випадків, переклад відбувається у шаблонах. Symfony надає нативну підтримку як для шаблонів Twig, так і PHP.

Використання тегів Twig

Symfony надає спеціальний тег Twig trans, щоб допомогти з перекладом повідомлень статичних болків тексту:

1
{% trans %}Hello %name%{% endtrans %}

Caution

Нотація заповнювачів %var% необхідна при перекладі у шаблонах Twig, що використовують тег.

Tip

Якщо вам потрібно використати символ процента (%) у рядку, екрануйте його шляхом дублювання: {% trans %}Percent: %percent%%%{% endtrans %}

Ви також можете вказати домен повідомлення та передати якісь додаткові змінні:

1
2
3
{% trans with {'%name%': 'Fabien'} from 'app' %}Hello %name%{% endtrans %}

{% trans with {'%name%': 'Fabien'} from 'app' into 'fr' %}Hello %name%{% endtrans %}

Використання фільтрів Twig

Фільтр trans може бути використаний для перекладу змінних текстів та складних виразів:

1
2
3
{{ message|trans }}

{{ message|trans({'%name%': 'Fabien'}, 'app') }}

Tip

Використання тегів та фільтрів перекладу має однаковий ефект, але з однією тонкою відмінністю: автоматичне екранування виведення застосовується лише до перекладів, що використовують фільтр. Іншими словами, якщо вам треба бути впевненим, що ваше повідомлення не буде екрановане при виведенні, ви маєте застосувати фільтр raw після фільтра перекладу:

1
2
3
4
5
6
7
8
9
10
{# текст, перекладений між тегами, ніколи не екранується #}
{% trans %}
    <h3>foo</h3>
{% endtrans %}

{% set message = '<h3>foo</h3>' %}

{# рядки та змінні, перекладені за допомогою фільтра, екрануються за замовчуванням #}
{{ message|trans|raw }}
{{ '<h3>bar</h3>'|trans|raw }}

Tip

Ви можете встановити домен перекладу для всього шаблону Twig одним тегом:

1
{% trans_default_domain 'app' %}

Відмітьте, що це впливає лише на поточний шаблон, а не на "доданий" шаблон (для уникнення побічних дій).

Форсування локалі перекаладача

При перекладі повідомлення, перекладач використовує вказану локаль або локаль fallback, якщо це необхідно. Ви також можете вручну вказати, яку локаль використовувати для перекладу:

1
2
3
4
5
6
$translator->trans(
    'Symfony is great',
    [],
    'messages',
    'fr_FR'
);

Вилучення змісту перекладу та автоматично оновлення каталогів

Найбільш витратними за часом задачами при перекладі додатку є отримання всього змісту шаблону для перекладу та змісту всіх файлів перекладу у синхроні. Symfony має команду під назвою translation:extract, яка допомагає вам з цими задачами:

1
2
3
4
5
6
7
8
# відобразити всі повідомлення, які мають бути перекладені для французької мови
$ php bin/console translation:extract --dump-messages fr

# оновити файли французького перекладу з відсутніми рядками для цієї локалі
$ php bin/console translation:extract --force fr

# переглянути допомогу команди, щоб побачити її опції (префікс, формат виведення, домен, сортування і т.д.)
$ php bin/console translation:extract --help

Команда translation:update шукає відстуні переклади в:

  • Шаблонах, що зберігаються в каталозі templates/ (або будь-якому іншому каталозі, визначеному в опціях конфігурації twig.default_path та twig.paths);
  • Будь-якому PHP файлі/класі, який впроваджує або автомонтує сервіс translator і робить виклики до методу trans().
  • Будь-якому PHP файлі/класі, який зберігається в каталозі src/, який створює , використовуючи конструктор або метод t(), або викликає метод trans().
  • Будья-якому PHP файлі/класі, який зберігається у каталозі src/, що використовує атрибути обмеження з іменованим(и) аргументам(и) *message.

6.2

Підтримка PHP файлів/класів, які використовують атрибути обмеження, була представлена в Symfony 6.2.

Імена ресурсу/файлу та розташування перекладу

Symfony шукає файли повідомлень (тобто переклади) у наступних місцях за замовчуванням:

  • у каталозі ``translations/` (в корені проекту)

app/Resources/translations; * у каталозі Resources/translations/ всередині будь-якого пакета.

Розташування перераховані у порядку пріоритету. Тобто, ви можете перевизначити переклад повідомлень пакета у першому каталозі.

Механізм перевизначення працює на рівні ключів: лише перевизначені ключі треба вказувати в файлі повідомлення з найвищим пріоритетом. Якщо ключ не знайдено у файлі повідомлення, перекладач автоматично звернеться до файлів повідомлень з нижчим пріоритетом.

Найменування файлів перекладів також важливе: кожний файл має бути названий у відповідності до наступного шляху: domain.locale.loader:

  • domain: Домени допомагають організовувати повідомлення у групи. Якщо частини додатку чітко не відокремлені одна від одної, рекомендується використовувати лише домен за замовчуванням messages (наприклад, messages.en.yaml).
  • locale: Локаль, якій відповідає переклад (наприклад, en_GB, en, і т.д.);
  • loader: Як Symfony має завантажити та аналізувати файл (наприклад, xlf, php, yml і т.д.).

Завантажувач (loader) може бути імене будь-якого зареєстрованого завантажувача. За замовчуванням, в Symfony представлені багато завантажувачів:

6.1

Підтримка розширення файлу .xliff була представлена в Symfony 6.1.

Вибір завантажувача залежить лише від вас та вашого смаку. Рекомендованим варіантом є використання YAML для простих проектів, та XLIFF, якщо ви генеруєте переклади зі спеціалізованими програмами або командами.

Caution

Кожний раз, коли створюєте новий каталог повідомлень (або встановлюєте пакет, що містить каталог повідомлень), не забудьте очистити ваш кеш, щоб Symfony могла виявити ваші джерела перекладу:

1
$ php bin/console cache:clear

Note

Ви можете додавати інші каталоги з опцією шляху у конфігурацію:

  • YAML
  • XML
  • PHP
1
2
3
4
5
# config/packages/translation.yaml
framework:
    translator:
        paths:
            - '%kernel.project_dir%/custom/path/to/translations'

Note

Ви також можете зберігати переклади у базі даних, або будь-якому іншому сховищі, за допомогою вашого власного класу, що реалізує інтерфейс LoaderInterface. Дивіться тег , щоб дізнатися подробиці.

Постачальники перекладів

При використанні зовнішніх перекладачів для перекладу вашого додатку, ви маєте відправляти їм новий зміст на переклад регулярно, та злияти результати назад із додатком.

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

Установка та конфігурація стороннього постачальника

До того як пушити/пулити переклади у сторонній постачальник, ви маєте встановити пакет, що надає інтеграцію з цим постачальником:

???????????? ?????????? ?? ?????????
Crowdin composer require symfony/crowdin-translation-provider
Loco (localise.biz) composer require symfony/loco-translation-provider
Lokalise composer require symfony/lokalise-translation-provider

Кожна бібліотека має рецепт Symfony Flex , який додасть приклад конфігурації у ваш файл .env. Наприклад, припустимо, що ви хочете використовувати Loco. Спочатку встановіть його:

1
$ composer require symfony/loco-translation-provider

Теперу вашому файлі .env буде новий рядок, який ви можете розкоментувати:

1
2
# .env
LOCO_DSN=loco://API_KEY@default

LOCO_DSN не є справжньою адресою: це зручний формат, який знімає більшість роботи з конфігурації з Symfony. Схема loco активує постачальника Loco, який ви щойно встановили, і який знає все про те, як пушити та пулити переклади через Loco. Єдине, що вам потрібно змінити - заповнювач API_KEY.

Ця таблиця відображає повний список доступних DSN-форматів для кожного постачальника:

Щоб підключити постачальника перекладів, додайте коректний DSN у ваш файл .env, та сконфігуруйте опцію providers:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
# config/packages/translation.yaml
framework:
    translator:
        providers:
            loco:
                dsn: '%env(LOCO_DSN)%'
                domains: ['messages']
                locales: ['en', 'fr']

Tip

Якщо ви використовуєте Lokalise в якості постачальника, та формат локалі, що слідує ISO 639-1 (наприклад, "en" або "fr"), вам потрібно встановити Налаштування користувацького найменування мови в Lokalise для кожної з ваших локалей, щоб перевизначити значення за замовчуванням (яке слідуж ISO 639-1 з наступним підкодом великими літерами, що вказують на варіацію національності (наприклад, "GB" або "US" у відповідності до ISO 3166-1 alpha-2)).

Пушинг та пулінг перекладів

Після конфігурації сертифікації для доступу до постачальника перекладів, ви тепер можете використовувати наступні команди для пушингу (завантаження) та пулнгу (скачування) перекладів:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# завантажити всі локальні переклади у постачальник Loco для локалей і доменів,
# сконфігурованих у файлі config/packages/translation.yaml.
# це оновить існуючі переклади вже у постачальнику.
$ php bin/console translation:push loco --force

# завантажити нові локальні переклади у постачальник Loco для французької локалі
# та домена валідаторів.
# це **не** оновлюватиме вже існуючі переклади у постачальнику.
$ php bin/console translation:push loco --locales fr --domain validators

# завантажити нові локальні переклади та видалити переклади постачальника, які більше не
# існують в локальних файлах для французької локалі та домена валідаторів.
# це **не** оновлюватиме вже існуючі переклади у постачальнику.
$ php bin/console translation:push loco --delete-missing --locales fr --domain validators

# перевірити допомогу команди, щоб побачити її опції (формат, домени, локалі, і т.д.)
$ php bin/console translation:push --help
1
2
3
4
5
6
7
8
9
10
11
12
# завантажити всі переклади постачальника в локальні файли для локалей та доменів,
# сконфігурованих у файлі config/packages/translation.yaml.
# повністю перепише ваші локальні файли.
$ php bin/console translation:pull loco --force

# завантажити нові переклади з постачальника Loco у локальні файли для французької
# локалі та домена валідаторів.
# це **не** перепише ваші локальні файли, лише завантажить нові переклади.
$ php bin/console translation:pull loco --locales fr --domain validators

# перевірити допомогу команди, щоб побачити її опції (формат, домени, локалі, intl-icu і т.д.)
$ php bin/console translation:pull --help

Робота з локаллю користувача

Переклад відбувається на підставі локалі користувача. Прочитайте , щоб дізнатися більше про роботу з нею.

Резервні локалі перекладу

Уявіть собі, що локаль користувача – es_AR, і що ви перекладаєте ключ Symfony is great. Для того, щоб знайти іспанський переклад, Symfony перевіряє джерела перекладу для декількох локалей:

  1. Спочатку Symfony шукає переклад у джерелі перекладів es_AR (аргентинська іспанська) (наприклад, messages.es_AR.yaml);
  2. Якщо переклад не був знайдений, Symfony шукає переклад у батьківській локалі, яка автоматично визначається лише для деяких локалей. У цьому прикладі, батьківська локаль - es_419 (латиноамериканська іспанська);
  3. Якщо переклад не був знайдений, Symfony шукає переклад у джерелі перекладу es (іспанська) (наприклад, messages.es.yaml);
  4. Якщо переклад все ще не був знайдений, Symfony використовує опцію fallbacks, яку можна сконфігурувати наступним чином.

    • YAML
    • XML
    • PHP
    1
    2
    3
    4
    5
    # config/packages/translation.yaml
    framework:
        translator:
            fallbacks: ['en']
            # ...

Note

Коли Symfony не знаходить переклад у заданій локалі, вона додає відсутній переклад у файл логів. Для того, щоб дізнатися деталі, дивіться .

Програмна зміна локалі

6.1

LocaleSwitcher було представлено в Symfony 6.1.

Іноді вам потрібно змінити локаль додатку динамічно, просто щоб виконати якийсь код. Уявіть консольну команду, яка відображає шаблони Twig або електронні листи різними мовами. Вам потрібно змінити цю локаль лише для відображення цих шаблонів.

Клас LocaleSwitcher дозволяє вам одночасно змінювати локаль:

  • Всіх сервісів, які теговані kernel.locale_aware;
  • \Locale::setDefault();
  • Якщо запит доступний, атрибут запиту _locale.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
use Symfony\Component\Translation\LocaleSwitcher;

class SomeService
{
    private LocaleSwitcher $localeSwitcher;

    public function __construct(LocaleSwitcher $localeSwitcher)
    {
        $this->localeSwitcher = $localeSwitcher;
    }

    public function someMethod()
    {
        // ви можете отривати поточну локаль додатку таким чином:
        $currentLocale = $this->localeSwitcher->getLocale();

        // ви можете встановити локаль для всього додатку таким чином:
        // (відтепер, додаток використовуватиме 'fr' (французька) як локаль;
        // включно з локаллю за замовчуванням, використовуваною для перекладу шаблонів Twig)
        $this->localeSwitcher->setLocale('fr');

        // перезапустити поточну локаль вашого додатку у сконфігуровану локаль вашого додатку за замовчуванням
        // в config/packages/translation.yaml, опцією 'default_locale'
        $this->localeSwitcher->reset();

        // ви також можете виконати якийсь код з певною локаллю, без зміни
        // локалі для решти додатку
        $this->localeSwitcher->runWithLocale('es', function() {

            // наприклад, відобразити тут якісь шаблони Twig, використовуючи локаль 'es' (іспанська)

        });

        // ...
    }
}

При використанні автомонтування , додайте підказку класу LocaleSwitcher у будь-який контролер або аргумент сервісу, щоб впровадити сервіс перемикача локалі. Інакше, сконфігуруйте ваші сервіси вручну та впровадьте сервіс translation.locale_switcher.

Переклад змісту бази даних

Переклад змісту бази даних має оброблятися Doctrine через розширення, що перекладається або поведінку, що перекладається (PHP 5.4+). Щоб дізнатися більше, дивіться документацію для цих бібліотек.

Налагодження перекладів

Коли ви працюєте з багатьма повідомленнями перекладу різними миовами, може бути важко встежити, яких перекладів не вистачає, а які більше не використовуються. Прочитайте , щоб дізнатися, як виявляти такі повідомлення.

Висновок

За допомогою компонента Symfony Translation, створення інтернаціонального додатку бульше не має бути болісним процесом, та полягає у наступних базових кроках:

  • Отримайте повідомлення вашого додатку, огорнувши кожне у метод trans();
  • Перекладіть кожне повідомлення для різних локалей, створивши файли перекладів повідомлень. Symfony знайде та обробить кожний файл, так як їх імена слідують специфічним угодам;
  • Управляйте локаллю користувача, яка зберігається у запиті, але також може бути встановлена у користувацькій сесії.