Как выполнить локализацию в программах Phoenix с помощью Gettext

1656530056 kak vypolnit lokalizacziyu v programmah phoenix s pomoshhyu

от Анастасии

YeVWrTAtoLW1vnKlFenMJ2vfDvwgi-SwAz5d

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

Возможно, вы раньше не слышали о Фениксе, поэтому позвольте мне сказать пару слов о нем. Это серверный фреймворк MVC, написанный на Elixir, являющийся функциональным языком программирования, работающим на виртуальной машине Erlang. Сам фреймворк достаточно молод, но все еще очень перспективен благодаря возможностям Erlang и Elixir. Он очень быстр, масштабирован и ориентирован на параллельность, что очень важно для сильно загруженных программ.

Gettext, в свою очередь, является инструментом I18n, поддерживаемым GNU, который можно использовать для веб-приложений, настольных программ и даже в операционных системах.

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

Привет, Gettext!

Ладно, давайте погрузимся непосредственно в код и понаблюдаем за локализацией программ Phoenix на практике. Создайте новый проект без СУБД по умолчанию и измените каталог на проект:

mix phx.new lokalise_demo --no-ecto cd lokalise_demo

Кажется, Phoenix имеет поддержку Gettext из коробки: вам не нужно устанавливать сторонние библиотеки. Кроме того, если вы перейдете к demo/lib/demo_web/templates/page/index.html.eex файл, вы заметите следующую строку кода:

<h2><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h2>

Что здесь происходит? Ну, gettext это функция, которая пытается загрузить перевод для строки "Welcome to %{name}!". %{name} вот заполнитель, который будет заменен на a "Phoenix" строка в соответствии со вторым аргументом name: "Phoenix" (этот аргумент содержит т. н. привязки).

По умолчанию приложения Phoenix имеют по умолчанию английский язык, а другие языковые стандарты не поддерживаются. Однако вы можете легко изменить это, добавив новую строчку к config/config.exs файл:

config :lokalise_demo, LokaliseDemoWeb.Gettext, locales: ~w(en ru)

Теперь мы поддерживаем как английский, так и русский языки.

Следующим шагом является предоставление переводов для строки, переданной в gettext функция внутри index.html.eex файл. Самый простой способ сделать это – автоматически вытащить все строки перевода в отдельные файлы:

mix gettext.extract mix gettext.merge priv/gettext mix gettext.merge priv/gettext --locale ru

Эти команды создадут три новых файла внутри priv/gettext папку. Поэтому давайте остановимся на секунду и поговорим немного больше об этих файлах.

Типы файлов Gettext

Первая команда выше, mix gettext.extractищет все текстовые сообщения Gettext, требующие перевода, и размещает их в файле priv/gettext/default.pot файл. POT означает «шаблон переносного объекта» и такие файлы служат шаблонами для переводов на определенный язык. Наши default.pot имеет такое содержание:

## This file is a PO Template file. ## ## msgid here are often extracted from source code. ## Add new translations manually only if they're dynamic ## translations that can't be statically extracted. ## ## Run mix gettext.extract to bring this file up to ## date. Leave msgstr empty as changing them here as no ## effect: edit them in PO (.po) files instead. msgid "" msgstr "" #, elixir-format #: lib/lokalise_demo_web/templates/page/index.html.eex:2 msgid "Welcome to %{name}!" msgstr ""

Шаблон удобно показывает строки, где расположены извлеченные сообщения. msgid это строчка для перевода (некоторые разработчики могут называть его «ключом»). msgstr это, конечно, подлинный перевод.

Имя файла POT default — также есть а Доменное имя который служит пространством имен. Сначала существует только одно пространство имен, но для больших сайтов с сотнями переводов может быть хорошей идеей создать несколько доменов и, следовательно, разделить переводы в разные файлы.

The mix gettext.merge priv/gettext --locale LOCALE_CODE_HERE Команда создает файлы перевода для данного языка на основе шаблона. Эти файлы перевода имеют .po расширение (“переносной объект”) и жить внутри priv/gettext/LOCALE_CODE_HERE/LC_MESSAGES папку. Помните, что для того чтобы предоставлять переводы для сообщений, вы должны редактировать эти PO-файлы, а не шаблоны напрямую!

Домены Gettext

Как уже упоминалось выше, Gettext поддерживает несколько доменов или пространств имен. Когда вы используете gettext/4 функцию, вы всегда предполагаете a default домена. Если вы хотите использовать другое пространство имен, воспользуйтесь dgettext/6 вместо этого функция, принимающая домен, сообщения, необязательные привязки и некоторые другие аргументы:

<%= dgettext "custom_domain", "message is ${placeholder}", placeholder: "my binding" %>

Теперь mix gettext.extract команда собирается создать новую custom_domain.pot файл. Так же бег mix gettext.merge создает а custom_domain.po файл на основе шаблона.

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

Предоставление переводов

Итак, обсудив некоторые внутренние элементы Gettext, мы можем перевести Welcome to %{name}! строка на русском языке (это сообщение уже на английском языке, поэтому перевод для этого языка, конечно, не требуется). Изменить priv/gettext/ru/LC_MESSAGES/default.po такой файл:

# ... some other stuff goes here ... #, elixir-format #: lib/lokalise_demo_web/templates/page/index.html.eex:2 msgid "Welcome to %{name}!" msgstr "Вас приветствует %{name}"

Это! У нас нет механизма переключения языка, поэтому установите русский язык как язык по умолчанию:

# config/config.exs config :lokalise_demo, LokaliseDemoWeb.Gettext, locales: ~w(en ru), default_locale: "ru" # <== modify this line

Теперь запустите сервер, выполнив:

mix phx.server

ОТКРЫТО страницу в вашем браузере и убедитесь, что переведенное сообщение показано!

Gettext Pluralization

Еще одна важная особенность, которую я хотел бы осветить, – это множество. Различные языки имеют разные правила множества, и Gettext поддерживает многие из них из коробки. Однако наша работа состоит в том, чтобы обеспечить надлежащий перевод для всех возможных случаев.

Как очень простой пример, скажем, сколько яблок у пользователя. Предположим, мы не знаем точного количества, а это означает, что предложение может звучать как 1 яблоко или X яблок. Чтобы поддержать плюризацию, мы должны придерживаться ngettext/5 функция:

ngettext "You have 1 apple", "You have %{count} apples", 2

Эта функция принимает формы предложения как единственного, так и множественного числа, а также формы count. Под капотом Gettext берет этот счет и выбирает правильный перевод на основе правил множества.

Далее можно обновить файлы POT и PO с помощью следующих команд:

mix gettext.extract --merge priv/gettext mix gettext.extract --merge priv/gettext --locale=ru

Вы найдете несколько новых строк в файлах Gettext:

msgid "You have 1 apple" msgid_plural "You have %{count} apples" msgstr[0] "" msgstr[1] ""

msgstr[0] и msgstr[1] содержат переводы для форм единственного и множественного числа соответственно. Для английского нам ничего больше делать не нужно, но русский язык требует некоторых дополнительных шагов:

msgid "You have one message" msgid_plural "You have %{count} messages" msgstr[0] "У вас одно яблоко" msgstr[1] "У вас %{count} яблока" msgstr[2] "У вас %{count} яблок"

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

Выбор местности программы

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

В общем, у нас есть два потенциальных решения:

  • Используйте решения сторонних разработчиков, например плагин set_locale (простой способ)
  • Пиши все с нуля (способ воина)

Если вы решите использовать розетку посторонних изготовителей, все будет очень просто. Вам нужно выполнить только три быстрых шага:

  1. Установите пакет
  2. Добавьте новую вилку в router.ex файл
  3. Добавьте новый :locale область маршрутизации

После этого язык будет определен с URL-адреса, файлов cookie или файла accept-language название запроса. Просто.

Однако на этом уроке я предлагаю выбрать более сложный способ и написать эту функцию с нуля.

Чтение языкового кода по URL-адресу

Самый распространенный способ указать нужный язык – это URL. Код языка может быть частью доменного имени или частью пути:

Давайте остановимся на последнем варианте и предоставим локаль в качестве параметра GET. Чтобы прочитать значение локали и сделать что-то с этим, нам нужен пользовательский плагин. Создайте новый lib/lokalise_demo_web/plugs/set_locale_plug.ex файл с таким содержимым:

defmodule LokaliseDemoWeb.Plugs.SetLocale do import Plug.Conn # 1 @supported_locales Gettext.known_locales(LokaliseDemoWeb.Gettext) # 2 def init(_options), do: nil # 3 def call(%Plug.Conn{params: %{"locale" => locale}} = conn, _options) when locale in @supported_locales do # 4 end def call(conn, _options), do: conn # 5 end

Давайте обсудим этот фрагмент кода:

  1. В этой строке мы импортируем поведение. Это требует от нас выполнения определенного контракта (см. ниже).
  2. Это атрибут модуля со списком поддерживаемых языковых стандартов
  3. Это фактическое исполнение контракта: обратный вызов, вызываемый автоматически. Он может возвращать параметры, передаваемые в call/2 функция, или просто nil
  4. The call/2 инициализируется всеми параметрами запроса GET. Нас интересует только locale часть и получить ее с помощью механизма соответствия шаблону. Также в этой строке мы имеем защитное предложение, гарантирующее, что выбранный язык действительно поддерживается
  5. Это вызываемое резервное предложение, когда переданный языковой стандарт не поддерживается. В этом случае мы просто возвращаем соединение без каких-либо изменений.

Последнее, что нам нужно сделать, это уточнить первый пункт call/2 функция. Он просто должен установить выбранную локаль как текущую:

def call(%Plug.Conn{params: %{"locale" => locale}} = conn, _options) when locale in @supported_locales do LokaliseDemoWeb.Gettext |> Gettext.put_locale(locale) conn end

Заметьте, что conn должно быть возвращено call/2 функция!

Вилка готова, и вы можете поместить ее внутрь :browser трубопровод:

# lib/router.ex # ... pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers plug LokaliseDemoWeb.Plugs.SetLocale end

Теперь перезагрузите сервер и перейдите к /?locale=en. Приветственное сообщение должно быть на английском, что означает, что пользовательский плагин работает должным образом!

Наша следующая задача – сохранить выбранную локаль среди запросов, чтобы пользователю не нужно было указывать ее каждый раз. Идеальным кандидатом для такой стойкости будут файлы cookie: небольшие текстовые файлы, хранящиеся на ПК пользователя. Phoenix действительно имеет поддержку файлов cookie из коробки, поэтому просто используйте a put_resp_cookies/4 функция внутри вашей вилки:

def call(%Plug.Conn{params: %{"locale" => locale}} = conn, _options) when locale in @supported_locales do LokaliseDemoWeb.Gettext |> Gettext.put_locale(locale) conn |> put_resp_cookie "locale", locale, max_age: 365*24*60*60 end

Мы изменяем соединение, сохраняя файл cookie с именем "locale". Он имеет срок службы 1 год, что фактически означает вечность с точки зрения Интернета.

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

def call(conn, _options) do case fetch_locale_from(conn) do nil -> conn locale -> LokaliseDemoWeb.Gettext |> Gettext.put_locale(locale) conn |> put_resp_cookie "locale", locale, max_age: 365*24*60*60 end end

В общем, логика остается неизменной: мы получаем локаль, проверяем ее, а затем либо ничего не делаем, либо сохраняем как текущую.

Добавьте две частные функции для завершения этой функции:

defp fetch_locale_from(conn) do (conn.params["locale"] || conn.cookies["locale"]) |> check_locale end defp check_locale(locale) when locale in @supported_locales, do: locale defp check_locale(_), do: nil

Здесь мы читаем локаль из параметра GET или файла cookie, а затем проверяем, поддерживается ли нужный язык. Затем либо поверните код этого языка, либо просто nil. Отличная работа!

Еще один достаточно распространенный способ установки локали – это использование Accept-Language HTTP-заголовок. Если вы хотите реализовать этот механизм, попробуйте использовать код из плагина set_locale, уже предоставляющий все необходимые регулярные выражения и другие фантастические вещи.

Управление переключателем локали

Итак, SetLocale плагин готов, но мы до сих пор не предоставили никаких элементов управления для выбора языка веб-сайта. Поэтому давайте отразим две ссылки в верхней части страницы. Определите нового помощника внутри lib/views/layout_view.ex файл:

defmodule LokaliseDemoWeb.LayoutView do use LokaliseDemoWeb, :view def new_locale(conn, locale, language_title) do "<a href=\"#{page_path(conn, :index, locale: locale)}\">#{language_title}</a>" |> raw end end

Позвоните этому помощнику из templates/layout/app.html.eex шаблон:

<body> <div class="container"> <header class="header"> <%= new_locale @conn, :en, "English" %> <%= new_locale @conn, :ru, "Russian" %> </header> <!-- other stuff --> </div> </body>

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

Упростите свою жизнь с помощью Lokalise

Теперь вы, вероятно, думаете, что поддержка нескольких языков на большом веб-сайте, вероятно, проблема. И, честно, вы правы. Разумеется, переводы можно разместить с помощью доменов. Но все равно вы должны убедиться, что все ключи переведены для каждой локали. К счастью, есть решение этой проблемы: платформа Lokalise, значительно упрощающая работу с файлами локализации. Позвольте мне провести вас через начальную настройку, которая на самом деле не сложна.

  • Чтобы начать, воспользуйтесь бесплатной пробной версией
  • Создайте новый проект, дайте ему название и установите английский как базовый язык
  • Щелкните «Загрузить языковые файлы»
  • Загрузите PO файлы для всех языков
  • Перейдите к проекту и при необходимости отредактируйте свои переводы
  • Вы также можете связаться с профессиональным переводчиком, чтобы сделать работу за вас
  • Далее просто загрузите свои PO-файлы вспять и замените их внутри priv/gettext папку
  • Прибыль!

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

Вывод

В сегодняшнем уроке мы увидели, как выполнить локализацию программ Phoenix с помощью Gettext. Мы обсудили, что такое Gettext и какие полезности он может предложить. Мы видели, как извлекать переводы, генерировать шаблоны и создавать PO-файлы на основе этих шаблонов. Вы также узнали, что такое домены и как использовать плюрализацию. Кроме того, мы успешно создали наш пользовательский плагин для получения и сохранения выбранной локали на основе пользовательских предпочтений. Неплохо для одной статьи!

Чтобы узнать больше о Phoenix I18n, я рекомендую вам ознакомиться с официальным руководством, содержащим как общие пояснения, так и документацию для отдельных функций. Дополнительные сведения о Gettext и его функциях см. в документации GNU. И конечно, если у вас возникли вопросы, смело задавайте их в комментариях!

Первоначально опубликовано на blog.lokalise.co 27 ​​сентября 2018 года.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *