При запуске нового веб-приложения я склонен думать, что интернационализация - это не проблема первой версии. Однако, как только ваш побочный проект начинает превращаться в реальное приложение, ваша пользовательская база растет, и когда вы живете не в англоязычной стране (например, во Франции), вы можете захотеть, чтобы ваше приложение было доступно на английском языке. .
Хотя часто довольно просто справиться с интернационализацией в Symfony или в React самостоятельно, справиться с этим в обоих одновременно оказалось довольно сложной задачей для нас в Windoo.
В этом посте я расскажу о том, как мы поддерживаем единый файл перевода для Symfony и React. Я не буду касаться пользовательской стороны настройки локали, поскольку это не проблема.
Перевод в проекте Symfony
Вам необходимо настроить компонент перевода. Вероятно, вам нужно только потребовать его и сразу же использовать, если вы используете Flex (и, вероятно, вам следует это сделать):
composer require symfony/translation
Если вы используете twig для своих шаблонов (страниц, электронных писем ...), вам нужно будет иметь свои переводы в файле, таком как messages.fr.yml
и messages.en.yml
, и использовать следующий синтаксис в ваших шаблонах:
<a href="{{ path('booking_list') }}"> {{ 'common.see_all'|trans }} </a>
Если вы хотите перевести сообщение в контроллер или в службу, вам необходимо внедрить службу TranslatorInterface
:
public function greet(TranslatorInterface $translator): Response { return new Response($translator->trans('common.greet')); }
В итоге вы получите большой yaml-файл, например:
common: post: Post greet: Hello ! see_all: See all hide: Hide home: Home post: title: Post title
Таким образом, вы можете использовать ключи Yaml в своем переводе. Вы можете управлять своим локальным компьютером с помощью Symfony, иметь два файла, один на английском, один на французском, и готово!
Прежде чем некоторые люди прокомментируют, я должен использовать вместо этого формат xliff, я уже могу решить этот вопрос: я считаю, что файлы переводов Yaml намного проще поддерживать и визуализировать в проекте для одного разработчика. Формат xliff является официальным, рекомендованным лучшими практиками Symfony, но при необходимости преобразование выполняется довольно легко. Подробнее об этом позже в статье.
Перевод в проект React
В React есть большая стандартная библиотека с названием react-i18next. После того, как вы его настроили, вы в конечном итоге используете его следующим образом (есть несколько вариантов).
import { useTranslation } from 'react-i18next'; const { t, i18n } = useTranslation(); export default () => <h1>{t('common.greet')}</h1>
И инициализация ваших переводов будет выглядеть так:
import i18n from "i18next"; import { initReactI18next } from "react-i18next"; i18next .use(initReactI18next) .init({ resources: { en: { common: { greet: 'hello!' }, post: { title: 'Post title' } }, fr: { common: { greet: 'Bonjour !' }, post: { title: 'Titre d\'article' } } } });
Таким образом, вам в основном нужен файл JSON, который можно анализировать в объекте Javascript.
Совместное использование переводов React и Symfony
Теперь все это хорошо, когда вы работаете над React SPA или полнофункциональным проектом Symfony. Но когда вы начинаете делиться своими переводами в обоих проектах, все становится… беспорядочно.
Во-первых, форматы Symfony и React-i18n не совсем совместимы. React-i18n не читает формат xliff, ни yaml… на самом деле ему нужен только объект javascript в стиле JSON в качестве входного файла. Таким образом, это также одна из причин, по которой мы не использовали формат xliff для хранения наших переводов.
Чтобы использовать один и тот же исходный файл для обоих проектов в Windoo, мы решили написать сценарий преобразования формата Symfony на лету, который будет внедрен в проект React. Для этого мы использовали загрузчик yaml для преобразования файлов yaml в объект javascript.
// utils/i18n.js import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import frYaml from 'js-yaml-loader!./translations/messages.fr.yml'; import enYaml from 'js-yaml-loader!./translations/messages.en.yml'; i18n .use(initReactI18next) .init({ resources: { fr: { translation: frYaml, }, en: { translation: enYaml, }, }, lng: (window && window.locale) || 'fr', fallbackLng: 'fr', }); export default i18n;
Таким образом, нам нужно обновить только один тип файла (а я предпочитаю формат Yaml, он красивее для просмотра и проще для поиска), и оба проекта могут использовать общие переводы. Все в порядке, правда?
Остерегайтесь множественного числа и параметров!
Мы думали, что мы все это выяснили, потому что предыдущие работы с простыми строками, такими как метки кнопок, заголовки ... но затем быстро стало ясно, что множественные формулировки не отображались одинаково, и это привело к неправильному переводу впереди. По той же причине вообще не работали переводы шаблонов с переменными.
Вот синтаксис response-i18n для множественного числа в словаре ключей:
{ "key": "item", "key_plural": "items", "keyWithCount": "{{count}} item", "keyWithCount_plural": "{{count}} items" }
И вот синтаксис Symfony для того же самого:
key: "item|items", keyWithCount: "%count% item|%count% items"
И я уже не говорю о полном формате для каждого языка. Мы переводим только французский и английский (по крайней мере, на сегодняшний день), поэтому мы продолжали использовать простой синтаксис.
Таким образом, количество ключей в React не то же самое, что затрудняет перенос с Symfony-Yaml на i18n-JS. В идеале мы хотели бы использовать формат ICU для обоих, но я смог найти для этого достаточно документации. Возможно, мы вернемся к этому позже в свое время.
Формат параметра
Перед внедрением файла мы обновили весь синтаксис параметров с помощью простой функции, обрабатывающей все записи:
// replace all %param% with {{param}} elem = String(elem).replace(/%([^%]+(?=%))%/gi, '{{$1}}');
Затем нам пришлось разделить ключи множественного числа в Symfony
// newObject = {}; -- the new translation dictionary //while looping on the keys if (elem.includes('|')) { const plural = elem.split('|'); newObject[key] = plural[0]; newObject[`${key}_plural`] = plural[1]; }
Таким образом, мы на лету полностью перенесли наши переводы с Symfony на React. Вот полный пример в одном виде:
После этого вы просто импортируете новую службу i18n.js
в компоненты React и вуаля!
Я напишу продолжение о том, как мы улучшили этот рабочий процесс с помощью службы перевода посередине, используя Localize.