Використання Перекладача

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

Використання Перекладача

Уявіть, що ви хочете перекласти рядок "Symfony чудова" на французьку:

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\ArrayLoader;

$translator = new Translator('fr_FR');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array(
    'Symfony is great!' => 'Symfony est super !',
), 'fr_FR');

var_dump($translator->trans('Symfony is great!'));

В цьому прикладі, повідомлення "Symfony чудова!" буде перекладене на локаль, встановлену в конструкторі (fr_FR), якщо повідомлення існує в одному з каталогів повідомлень.

Заповнювачі повідомлень

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

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

var_dump($translated);

Однак, створення перекладу для цього рядку неможливе, тому що перекладач буде намагатися знайти точне повідомлення, включно з його змінними частинами (наприклад, "Привіт, Райан" або "Привіт, Фабієн"). Замість того, щоб писати переклад для кожної можливої ітерації змінної $name, ви можете замінити змінну "заповнювачем":

1
2
3
4
5
6
7
// ...
$translated = $translator->trans(
    'Hello %name%',
    array('%name%' => $name)
);

var_dump($translated);

Symfony тепер шукатиме переклад сирого повідомлення (Привіт %name%), а потім замінювати заповнювачі їх значеннями. Створення перекладу відбувається так само, як і раніше:

  • XML
  • PHP
  • YAML
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file source-language="en" datatype="plaintext" original="file.ext">
        <body>
            <trans-unit id="1">
                <source>Hello %name%</source>
                <target>Bonjour %name%</target>
            </trans-unit>
        </body>
    </file>
</xliff>

Note

Заповнювачі можут приймати будь-яку форму, так як повне повідомлення реконструюється з використанням PHP strtr function. Однак рекомендована форма %...% для уникнення проблем при використанні Twig.

Як ви бачили, створення перекладу - це двокроковий процес:

  1. Відокремте повідомлення, яке необхідно перекласти, обробивши його в Translator.
  2. Створіть переклад для повідомлення в кожній локалі, яку ви підтримуєте.

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

Створення перекладів

Створення файлів перекладу - це важлива частина "локалізації" (часто використовувана абревіатура L10n). Файли перекладів складаються з серії пар id - переклад для заданого домену та локалі. Джерело є ідентифікатором для індивідуального перекладу, і може бути повідомленням в головній локалі вашого додатку (наприклад, "Symfony чудова"), або унікальним ідентифікатором (наприклад, symfony.great - див. виноску нижче).

Файли перекладу можуть бути створені в декількох різних форматах, в той час як XLIFF є рекомендованим. Ці файли аналізуються одни з класів завантажувача.

  • XML
  • YAML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file source-language="en" datatype="plaintext" original="file.ext">
        <body>
            <trans-unit id="symfony_is_great">
                <source>Symfony is great</source>
                <target>J'aime Symfony</target>
            </trans-unit>
            <trans-unit id="symfony.great">
                <source>symfony.great</source>
                <target>J'aime Symfony</target>
            </trans-unit>
        </body>
    </file>
</xliff>

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

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

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

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

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

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

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

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

  • YAML
  • PHP
1
2
3
4
5
6
7
8
symfony:
    is:
        great: Symfony is great
        amazing: Symfony is amazing
    has:
        bundles: Symfony has bundles
user:
    login: Login

Множинні рівні об'єднуються в єдині пари id / переклад, шляхом додавання крапки (.) між кожним рівнем, тому приклади вище еквівалентні наступному:

  • YAML
  • PHP
1
2
3
4
symfony.is.great: Symfony is great
symfony.is.amazing: Symfony is amazing
symfony.has.bundles: Symfony has bundles
user.login: Login

Плюралізація

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

1
2
3
4
5
6
7
8
9
(($number % 10 == 1) && ($number % 100 != 11))
    ? 0
    : ((($number % 10 >= 2)
        && ($number % 10 <= 4)
        && (($number % 100 < 10)
        || ($number % 100 >= 20)))
            ? 1
            : 2
);

Як ви бачите, в російській у вас може бути три різних множинні форми, кожна з яких має індекс 0, 1 або 2. Для кожної форми множина відрізняється, а отже, переклад також відрізняється.

Коли переклад має різні форми у зв'язку з плюралізацією, ви можете надати всім формам рядок, розділенний трубою (|):

1
'Є одне яблуко |Є %count% яблук'

Щоб перекладати повідомлення з множиною, використовуйте метод transChoice():

1
2
3
4
5
6
7
8
9
10
11
12
13
// заповнювач %count% приписується другому аргументу...
$translator->transChoice(
    'Є одне яблуко |Есть %count% яблук',
    10
);

// ...але ви можете визначити більше заповнювачів за необхідності
    $translator->transChoice(
    'Посіпшай, %name%! Залишилось одне яблуко.|Залишилось %count% яблук.',
    10,
    // тут немає необхідності додавати %count%; Symfony зробить це за вас
    array('%name%' => $user->getName())
);

Другий аргумент (10 в цьому прикладі) це кількість описуваних об'єктів, яка використовується для визначення, який переклад використовувати, а також для заповнення %count%.

Грунтуючись на заданому числі, переклад обирає правильну множинну форму. В англійській, більшість слів мають форму однини, коли є один об'єкт, і множинну форму з усіма іншими числами (0, 2, 3...). Тому, якщо count - 1, перекладач буде використовувати перший рядок в якості перекладу. В інших випадках він буде використовувати Є %count% яблук.

Ось французький переклад:

1
'Il y a %count% pomme|Il y a %count% pommes'

Навіть якщо рядок виглядає схоже (він складається з двох підрядків, розділенніх трубою), правила французької мови відрізнаються: перша форма (не множинна) використовується, коли count - 0 або 1. Тому перекладач автоматично використовує перший рядок (Il y a %count% pomme), коли count - 0 або 1.

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

1
2
3
'one: There is one apple|some: There are %count% apples'

'none_or_one: Il y a %count% pomme|some: Il y a %count% pommes'

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

Tip

Так як теги опціональні, перекладач не використовує їх (перекладач просто отримає рядок, що грунтується на його положенні в рядку).

Чітка інтервальна плюралізація

Найпростіший спосіб плюралізувати повідомлення - дозволити Перекладачу використовувати внутрішню логіку, щоб обрати, який рядок використати, виходячи з заданого номеру. Інколи вам необхідно більше контролю або хочеться інший преклад для певних випадків (для 0, або коли число від'ємне, наприклад). Для таких випадків ви можете використовувати чітку інтервальну плюралізацію:

1
'{0} There are no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf[ There are many apples'

Інтервали дотримуються нотації ISO 31-11. Рядок зверху вказує 4 різних інтервали: рівно 0, рівно 1, 2-19, та 20 і більше.

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

1
'{0} There are no apples|[20,Inf[ There are many apples|There is one apple|a_few: There are %count% apples'

Наприклад, для 1 яблука, буде використане стандартне правило There is one apple. Для 2-19 яблук, буде обране друге стандартне правило There are %count% apples.

Interval може представляти обмежений набір цифр:

1
{1,2,3,4}

Або числа між двома іншими числами:

1
2
[1, +Inf[
]-1,2[

Лівий розділювач може бути [ (тим, що включає) або ] (тим, що виключає). Правий розділювач може бути [ (тим, що виключає) або ] (тим, що включає). Окрім цифр ви можете використовувати -Inf та +Inf для нескінченності.

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$translator->trans(
    'Symfony is great',
    array(),
    'messages',
    'fr_FR'
);

$translator->transChoice(
    '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
    10,
    array(),
    'messages',
    'fr_FR'
);

Note

Починаючи з Symfony 3.2, третій аргумент transChoice() опціональний, якщо єдиний використовуваний заповнювач - %count%. В попередніх версіях Symfony вам завжди треба було його визначати:

1
2
3
4
5
6
7
$translator->transChoice(
    '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
    10,
    array('%count%' => 10),
    'messages',
    'fr_FR'
);

Добування каталогу повідомлень

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

1
2
3
4
5
$catalogue = $translator->getCatalogue('fr_FR');
$messages = $catalogue->all();
while ($catalogue = $catalogue->getFallbackCatalogue()) {
    $messages = array_replace_recursive($catalogue->all(), $messages);
}

Змінна $messages тепер матиме наступну структуру:

1
2
3
4
5
6
7
8
9
array(
    'messages' => array(
        'Hello world' => 'Bonjour tout le monde',
    ),
    'validators' => array(
        'Value should not be empty' => 'Valeur ne doit pas être vide',
        'Value is too long' => 'Valeur est trop long',
    ),
);

Додавання нотатків до змісту перекладу

Інколи перекладачам потрібен додатковий контекст, щоб вирішити, як краще перекласти деякий зміст. Такий контекст можна надати у вигляді нотаток, які є набором коментарів, що використовуються для зберігання читаємої інформації кінцевого користувача. Єдиний формат, який підтримує завантаження та скидання нотаток - XLIFF версії 2.0.

Якщо документ XLIFF 2.0 містить вузли <notes>, то вони автоматично завантажуються або скидаються при використанні цього компоненту всередині додатку Symfony:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0"
       srcLang="fr-FR" trgLang="en-US">
  <file id="messages.en_US">
    <unit id="LCa0a2j" name="original-content">
      <notes>
        <note category="state">new</note>
        <note category="approved">true</note>
        <note category="section" priority="1">user login</note>
      </notes>
      <segment>
        <source>original-content</source>
        <target>translated-content</target>
      </segment>
    </unit>
  </file>
</xliff>

При використанні компоненту Перекладач окремо, викличте метод каталогу setMetadata() та передайте нотатки в якості масивів. Ось, наприклад, код, необхідний для генерування попереднього XLIFF-файлу:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Dumper\XliffFileDumper;

$catalogue = new MessageCatalogue('en_US');
$catalogue->add([
    'original-content' => 'translated-content',
]);
$catalogue->setMetadata('original-content', ['notes' => [
    ['category' => 'state', 'content' => 'new'],
    ['category' => 'approved', 'content' => 'true'],
    ['category' => 'section', 'content' => 'user login', 'priority' => '1'],
]]);

$dumper = new XliffFileDumper();
$dumper->formatCatalogue($catalogue, 'messages', [
    'default_locale' => 'fr_FR',
    'xliff_version' => '2.0'
]);