При разработке многоязычного приложения мы рано или поздно столкнемся с необходимостью локализации строк используемых в javascript. В Ruby on Rails для этих целей есть замечательный гем i18n-js. Мне понадобилось некоторое время, чтобы разобраться как он работает, не смотря на наличие документации. В какой-то момент я даже было подумал, что это безнадежный вариант и чуть было не бросил эту затею, хотя на самом деле все оказалось просто.
Локализуем надписи select2 из примера одной из предыдущих статей.
Сначала просто подключим мультиязычность в приложении - сделаем все по руководству Ruby on Rails.
Прописываем опции локализации в config/initializers/locale.rb
# config/initializers/locale.rb # ищем файлы локализации рекурсивно в папке config/locales config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')] config.i18n.default_locale = :en config.i18n.available_locales = [:ru, :en]
Настраиваем установку локали из адреса страницы в ApplicationController
# app/controllers/application_controller.rb before_action :set_locale def set_locale I18n.locale = params[:locale] || I18n.default_locale end
Добавляем локаль ко всем генерируемым ссылкам
# app/controllers/application_controller.rb def default_url_options(options={}) { locale: I18n.locale } end
И, наконец, задаем локаль в роутах
# config/routes.rb scope "/:locale", locale: /#{I18n.available_locales.join("|")}/ do resources :posts root to: redirect("/%{locale}/posts", status: 302) end root to: redirect("/#{I18n.default_locale}", status: 302), as: :redirected_root get "/*path", to: redirect("/#{I18n.default_locale}/%{path}", status: 302), constraints: {path: /(?!(#{I18n.available_locales.join("|")})/).*/}, format: false
А магия редиректов, к которой мы прибегли выше, подробнее описывается в другом моем посте.
Добавим переключение локали в лейаут, используя специальный хелпер
# app/helpers/application_helper.rb module ApplicationHelper def lang_switcher content_tag(:ul, class: 'lang-switcher clearfix') do I18n.available_locales.each do |loc| locale_param = request.path == root_path ? root_path(locale: loc) : params.merge(locale: loc) concat content_tag(:li, (link_to loc, locale_param), class: (I18n.locale == loc ? "active" : "")) end end end end
# app/views/layouts/application.html.erb <%= lang_switcher %>
Обычно я дополнительно использую гем rails-i18n для некоторых базовых локализаций, однако в данном примере он нам не понадобится. Подключаем гем i18n-js в Gemfile. Ставим гемы через bundler, выполнив команду bundle install
Добавляем клиентские скрипты, обеспечивающие локализацию в javascript
# app/assets/javascripts/application.js //= require i18n
Создадим partial лейаут, в котором настройки локализации с сервеной части будем передавать в клиентскую часть
# app/views/layouts/_js_locales_info.html.erb <%= javascript_tag do %> I18n.defaultLocale = "<%= I18n.default_locale %>"; I18n.locale = "<%= I18n.locale %>"; I18n.fallbacks = true; <% end %>
И подключаем этот partial в основном лейауте в разделе head
после подключения общих js.
# app/views/layouts/application.html.erb <%= render 'layouts/js_locales_info' %>
При этом стоит отметить, что в таком случае локализацию мы сможем использовать только после загрузки страницы или в тех файлах, которые были подключены после нашего partial'а. Если же мы хотим использовать локализацию вообще везде и сразу, то нам нужно будет отдельно подключить javascript i18n
и сразу после этого подключить настройки локализации и только потом — вcе остальные скрипты.
Теперь нам осталось настроить яваскриптовые локали. Для этого создаем файл конфигурации:
# config/i18n-js.yml translations: - file: "app/assets/javascripts/application/i18n/translations.js" only: '*.js.*'
В данном файле директивой - file
мы указываем путь, куда будут складывать компилируемые словари переводов для js (их нужно компилировать вручную, об этом позже). Стоит отметить, что в данном примере указана компиляция в папку app/assets/javascripts/application
, поскольку в нашей конфигурации из этой папки собираются все ассеты. Директива only
указывает какие ключи в файлах переводов будут компилироваться в яваскриптовый словарь переводов. В нашем примере собираются все ключи, в которых встречается подключ js
, примеры:
en: admin: js: title: 'Sample' js: copyright: 'Another sample' post: js: name: 'One more sample'
Итак, подготовим файлы локализации для нашего случая:
# config/locales/js/en.yml en: js: posts: select2: placeholder: 'Please, select tags' no-matches: 'No tags found'
# config/locales/js/ru.yml ru: js: posts: select2: placeholder: 'Пожалуйста, укажите теги' no-matches: 'Тегов не найдено'
И самый интересный момент, который я долго не мог понять: чтобы экспортировать локали в словари js - нужно запустить вручную команду bin/rake i18n:js:export
. После ее выполнения мы получим javascript файл app/assets/javascripts/application/i18n/translations.js
, в котором и будет храниться наш словарь с переводами. И работать он будет, поскольку он будет автоматически подключаться через механизм ассетов. Ну и теперь мы наконец можем использовать локализацию в клиентских скриптах. Расширим инициализацию select2 для использования локалей
# app/assets/specific/posts/_form.js.coffee $ -> $('#post_tag_list').select2 tags: if gon? then gon.tags else [] tokenSeparators: [","] placeholder: I18n.t('js.posts.select2.placeholder') formatNoMatches: (term) -> I18n.t('js.posts.select2.no-matches') width: '200'
Готово!
К слову сказать, i18n-js вполне независимая библиотека, поэтому данный подход может применяться и в случае использования других языков программирования на стороне сервера.