Ruby on Rails 6/7, урок #13 | Перевод, интернационализация, локализация (i18n, l10n)
Вставка
- Опубліковано 14 чер 2024
- В этом уроке по Ruby on Rails 6/7 мы узнаем, как выполнять перевод приложения на несколько языков, то есть внедрять интернационализацию (i18n) и локализацию (l10n). Поговорим о том, как сохранять язык в маршруте, сохранять его при переходе между страницами, переключать язык и "угадывать" язык на основе предпочтений пользователя. Также узнаем, что такое TMS и рассмотрим в качестве примере сервис Lokalise.
Таймкоды:
00:00 Введение
01:38 Поддерживаемые языки и rails-i18n
04:10 Простой перевод текста и "ленивые" переводы
08:50 Локализация даты-времени
10:45 Перевод атрибутов моделей и самих моделей
13:28 Перевод Pagy (постраничная навигация)
14:15 Перевод сообщений flash в контроллере
15:15 Хранение языка в маршрутах (scope)
17:05 Считывание и установка запрошенного языка
19:10 Локаль в ссылках с помощью default_url_options
20:18 Переключение языка в выпадающем меню
23:03 Считывание предпочитаемых языков пользователя
25:00 Парсинг предпочитаемых языков
27:20 Система управления переводами Lokalise
35:00 Заключение
Станьте спонсором канала, и вы получите доступ к эксклюзивным бонусам: / @ilyabodrovkrukowski
Аккаунт Ethereum (ETH): 0x719C2d2bcC155c85190f20E1Cc3710F90FAFDa16
Boosty: boosty.to/bodrovis
Patreon: / bodrovis
DonationAlerts: www.donationalerts.com/r/bodr...
Исходный код: github.com/bodrovis-learning/...
Rails-i18n: github.com/svenfuchs/rails-i18n/
Rack-contrib (Locale): github.com/rack/rack-contrib/...
Lokalise: lokalise.com
LokaliseRails: github.com/bodrovis/lokalise_...
Ещё примеры i18n в Rails: lokalise.com/blog/rails-i18n/
И видео по теме на моём канале: • Интернационализация пр...
Канал Telegram: t.me/dev_in_ruby_colors
Наш чат в Telegram: t.me/joinchat/MxYT6-01eeA1NTYy
Мой сайт: bodrovis.tech
Спасибо Вам огромное за труд! Уже два учебных проекта запилил, пользуясь Вашей инструкцией! Дякую!
Ого, круто
Огромное спасибо! Очень полезный материал! 🔥🔥🔥
Это просто кайф!
*Впервые в жизни нажал на КОЛОКОЛЬЧИК*
Рад, что понравилось :) Ну, всё бывает впервые :D
Друзья, ещё раз подчеркну, что показать все примеры переводов в видео я не смог из-за временных ограничений, но полную версию переведённого приложения можно найти на GitHub: github.com/bodrovis-learning/RailsSeriesYT/tree/lesson_13 (другие примеры см. в описании к ролику)
Спасибо огромное за ваш труд! Суперский материал!
Спасибо за материал и понятное объяснение
Рад, что понравилось!
Грамотная реализация локализации. Показан нечасто используемый механизм: request.env.fetch('HTTP_ACCEPT_LANGUAGE', '').scan(/[a-z]{2}/).first
У себя использовал чуть более сложный алгоритм определения локали. Создаем в модели пользователя (или в профиле пользователя) record locale, где зарегистрированный пользователь (в панели настроек аккаунта) может сохранить предпочитаемый язык. При определении локали приложение вначале проверяет наличие этой записи и выставляет локализацию. При ее отсутствии предпочтение отдается локали переданной в params, т.е. фактически выбранной пользователем в пункте меню. Если в params нет последнего, то дальше проверяем наличие соответствующей записи в cookies, запись в которую происходит после первого успешного выбора языка. Как вариант можно использовать local storage (session менее предпочтителен, если надо обьясню). Запись в куки необходима чтобы гость мог сохранить предпочитаемый язык независимо от 'HTTP_ACCEPT_LANGUAGE или 18n.default_locale при обновлении страничек или рестарте браузера. Только после этого используется механизм предложенный автором. В качестве эксперимента, использовал последним в данном алгоритме загрузку локали из файла настроек yml. Каждый раз проверяем locale на вхождение в список доступных (для куки I18n.available_locales.include?(cookies_locale&.to_sym))
Кроме того, как альтернативу установки локали в params использовал механизм добавления к url '?lang=' . Ссылка для lвыбора русского языка будет выглядеть так: link_to '?lang=ru'.prepend(request.path)). Соотвественно root_path для английского языка будет иметь такой вид: localhost:3000/index?lang=eng Эта реализация locale params упрощает извлечение локали из params (банальный params[:lang]), не требует изменений в route.rb. В тоже время реализация локали от автора видео - в виде субдомена - выглядит более профессионально. Так что это лишь моя вкусовщина.
Есть также полезный gem i18n-tasks, который позволяет найти неиспользуемые переводы, отсутствующие переводы , перевести ключи в google translate и многое другое (i18n-tasks -help в помощь). Например, автоматом добавить отсутствующие ключи со значениями в другой lang.yml или normilize.
Тема неисчерпаемая. Многое осталось за бортом. В любом случае автору спасибо и лайк.
Спасибо, интересные замечания! Да, конечно тема объёмная, это правда - возможно, ещё к ней вернёмся. Собственно, именно из-за сложности с ключами я и показал систему управления переводами, тк даже для такого небольшого приложения уже в итоге ключей вышло прилично - можно запутаться, даже если разбивать дальше по файлам. :) В общем да, надо подумать насчёт второй части.
Кстати сказать, что касается преобразования пользовательских данных в символы: раньше был такой тип атаки, который создавал кучу символов в памяти сервера. Ну то есть просто хакер спамит запросами, в которые, например, подставляет всякие "левые" локали, мы их потом конвертируем в символы, память не очищается, и в итоге сервер повисает. Хотя, кажется, сейчас такая атака уже не проходит, но я на всякий случай стараюсь данные, полученные от юзера, в символы без предварительной проверки не переводить - мало ли
@@IlyaBodrovKrukowski верное замечание, не мешало санитаризовать все данные которые передаются в params. По поводу типа атаки с конвертацией в символ не слышал. Как вариант конвертировать в строки. В символах только данные из конфига. Впрочем замечание резонное, учту. Спасибо
@@ledockol Собственно, да, именно поэтому я преобразовываю локали в строку, а не params[:locale] в символ. Эта атака описана вот тут внизу brakemanscanner.org/docs/warning_types/denial_of_service/ но, судя по всему, более новые версии ruby ей уже не подвержены. В общем, паранойя :))
@@IlyaBodrovKrukowski не такая уж и паранойя. Чистить и проверять надо всегда. На любительском уровне этим можно конечно пренебречь, а так надо проверять все, типы, содержание. Даже в route rb
@@ledockol Да, это правда
Спасибо вам огромное!
будет React или Vue + Rails?
Будет Stimulus, я думаю. Хотя может и React тоже
Спасибо большое!
:D
В Rails 7 бутстраповский dropdown не будет работать после указанных инструкций по установке. Насколько понял, теперь нет метода javascript_pack_tags который раньше добавляет все примочки для js. Теперь видимо прописывается руками или как-то по-другому. Я сделал так - создал файл assets/javascripts/application.js, там добавил зависимости как указано в репозитории bootstrap:
//= require jquery3
//= require popper
//= require bootstrap
Затем в файле assets/config/manifest.js добавил строку:
//= link application.js
После этого выпадающий список заработал.
PS. Спасибо за ваши труды и что делитесь с нами своим опытом.
Про Rails 7, esbuild и propshaft есть отдельные уроки (ближе к концу плейлиста)
Спасибо за видео
На здоровье!
Хотел спросить для того чтобы сделать перевод к примеру статей это надо создавать поля типа title_en title_ru body_en body_ru и самому их заполнять перед публикацией ? это будет правильное решение ?
Спасибо за классные уроки! что-то я упустил что вы сделали что у вас на 6:19 работает метод t(....), у меня работает только с I18n.t
Ничего не сделал, он должен работать так в контроллерах и представлениях. В моделях - нет
Спасибо большое. Впервые узнал про lazy переводы. Все на высоте. Единственное, зачем доп гем для переменок окружения? По моему в 5.1-6.* уже есть secrets.yml для этих целей?
Да есть, я просто привык как-то, если честно :)
What %i[...] means?
Для того, чтобы при выборе локали не редиректило каждый раз на главную страницу, вместо root_path использовал url_for(locale: locale) - вроде работает норм.
Или можно еще request.params.merge(locale: locale) - это в GoRails подсмотрел. Не подскажете, как в плане безопасности данные решения?
@@alexanderyakovlev5201 Один из вариантов - делать по принципу "проброса", то есть создать отдельный маршрут для переключения. В соответствующем действии контроллера проверять, откуда пришёл юзер и правильно редиректить его туда с нужной локалью. Либо использовать JS и отлавливать нажатие клика по локали, ставя в сессию ту страницу, где юзер был, а потом опять же делать редирект туда. Но можно и так - выглядит всё логично, вроде бы, проблем с безопасностью не вижу (хотя в обсуждении на SO ниже упоминается, что вроде как params.merge не очень-то безопасно - я бы тоже такой способ использовать не стал, наверное). Но тут всё равно всё не так просто, тк в запросе могут быть другие GET-параметры ещё, которые по-хорошему нужно сохранять. Вот обсуждение по теме, например: stackoverflow.com/questions/44066435/how-to-keep-url-parameters-when-changing-the-locale и про url_for: stackoverflow.com/questions/35189892/rails5-params-merge-causes-errors-about-being-insecure
@@alexanderyakovlev5201 В целом, да - url_for должен подойти в нашем случае, но он, к сожалению, не сохраняет дополнительные GET-параметры. Например, localhost:3000/ru/questions/40/answers/15/edit?test=str&other=test - редирект пойдёт на localhost:3000/ru/questions/40/answers/15/edit, всё после знака вопроса потеряется. В простых случаях это вполне ок
@@IlyaBodrovKrukowski Спасибо за этот ответ! Тоже посчитала, что можно использовать простое решение типа url_for controller: controller_name, action: action_name, locale: locale. В предыдущих настройках locale я так и делала.
что за синтаксис? scope "(:locale)", locale: /en|ru/ do нигде не нашел документации
Необязательный параметр в маршруте
@@IlyaBodrovKrukowski да, спасибо, про это вроде накопал. А вот второй параметр это что опция? что мы ей обозначаем?
@@user-gf1qz4vm6u Это регулярное выражение, которое говорит, какие значения могут быть