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

КОМЕНТАРІ • 42

  • @rickbacker1
    @rickbacker1 Рік тому +3

    Спасибо Вам огромное за труд! Уже два учебных проекта запилил, пользуясь Вашей инструкцией! Дякую!

  • @ELDAR011288
    @ELDAR011288 8 місяців тому +2

    Огромное спасибо! Очень полезный материал! 🔥🔥🔥

  • @EdritKolotit
    @EdritKolotit 2 роки тому +4

    Это просто кайф!
    *Впервые в жизни нажал на КОЛОКОЛЬЧИК*

    • @IlyaBodrovKrukowski
      @IlyaBodrovKrukowski  2 роки тому +1

      Рад, что понравилось :) Ну, всё бывает впервые :D

  • @IlyaBodrovKrukowski
    @IlyaBodrovKrukowski  2 роки тому +3

    Друзья, ещё раз подчеркну, что показать все примеры переводов в видео я не смог из-за временных ограничений, но полную версию переведённого приложения можно найти на GitHub: github.com/bodrovis-learning/RailsSeriesYT/tree/lesson_13 (другие примеры см. в описании к ролику)

  • @user-hc6yf9lr3n
    @user-hc6yf9lr3n 2 роки тому +3

    Спасибо огромное за ваш труд! Суперский материал!

  • @gekphoenix6844
    @gekphoenix6844 2 роки тому +4

    Спасибо за материал и понятное объяснение

  • @ledockol
    @ledockol 2 роки тому +4

    Грамотная реализация локализации. Показан нечасто используемый механизм: 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
      @IlyaBodrovKrukowski  2 роки тому +1

      Спасибо, интересные замечания! Да, конечно тема объёмная, это правда - возможно, ещё к ней вернёмся. Собственно, именно из-за сложности с ключами я и показал систему управления переводами, тк даже для такого небольшого приложения уже в итоге ключей вышло прилично - можно запутаться, даже если разбивать дальше по файлам. :) В общем да, надо подумать насчёт второй части.
      Кстати сказать, что касается преобразования пользовательских данных в символы: раньше был такой тип атаки, который создавал кучу символов в памяти сервера. Ну то есть просто хакер спамит запросами, в которые, например, подставляет всякие "левые" локали, мы их потом конвертируем в символы, память не очищается, и в итоге сервер повисает. Хотя, кажется, сейчас такая атака уже не проходит, но я на всякий случай стараюсь данные, полученные от юзера, в символы без предварительной проверки не переводить - мало ли

    • @ledockol
      @ledockol 2 роки тому +1

      @@IlyaBodrovKrukowski верное замечание, не мешало санитаризовать все данные которые передаются в params. По поводу типа атаки с конвертацией в символ не слышал. Как вариант конвертировать в строки. В символах только данные из конфига. Впрочем замечание резонное, учту. Спасибо

    • @IlyaBodrovKrukowski
      @IlyaBodrovKrukowski  2 роки тому +2

      @@ledockol Собственно, да, именно поэтому я преобразовываю локали в строку, а не params[:locale] в символ. Эта атака описана вот тут внизу brakemanscanner.org/docs/warning_types/denial_of_service/ но, судя по всему, более новые версии ruby ей уже не подвержены. В общем, паранойя :))

    • @ledockol
      @ledockol 2 роки тому +1

      @@IlyaBodrovKrukowski не такая уж и паранойя. Чистить и проверять надо всегда. На любительском уровне этим можно конечно пренебречь, а так надо проверять все, типы, содержание. Даже в route rb

    • @IlyaBodrovKrukowski
      @IlyaBodrovKrukowski  2 роки тому +1

      @@ledockol Да, это правда

  • @vinogradova82
    @vinogradova82 Рік тому +2

    Спасибо вам огромное!

  • @eg0rfull
    @eg0rfull 2 роки тому +6

    будет React или Vue + Rails?

    • @IlyaBodrovKrukowski
      @IlyaBodrovKrukowski  2 роки тому +3

      Будет Stimulus, я думаю. Хотя может и React тоже

  • @neuromancersmith9890
    @neuromancersmith9890 2 роки тому +1

    Спасибо большое!

  • @user-gf1qz4vm6u
    @user-gf1qz4vm6u 2 роки тому +1

    В 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. Спасибо за ваши труды и что делитесь с нами своим опытом.

    • @IlyaBodrovKrukowski
      @IlyaBodrovKrukowski  2 роки тому +1

      Про Rails 7, esbuild и propshaft есть отдельные уроки (ближе к концу плейлиста)

  • @xaogao
    @xaogao 2 роки тому +1

    Спасибо за видео

  • @dextermd1938
    @dextermd1938 2 роки тому

    Хотел спросить для того чтобы сделать перевод к примеру статей это надо создавать поля типа title_en title_ru body_en body_ru и самому их заполнять перед публикацией ? это будет правильное решение ?

  • @igorshkuryn2384
    @igorshkuryn2384 Рік тому

    Спасибо за классные уроки! что-то я упустил что вы сделали что у вас на 6:19 работает метод t(....), у меня работает только с I18n.t

    • @IlyaBodrovKrukowski
      @IlyaBodrovKrukowski  Рік тому

      Ничего не сделал, он должен работать так в контроллерах и представлениях. В моделях - нет

  • @user-ei7ip6nw6s
    @user-ei7ip6nw6s 2 роки тому +1

    Спасибо большое. Впервые узнал про lazy переводы. Все на высоте. Единственное, зачем доп гем для переменок окружения? По моему в 5.1-6.* уже есть secrets.yml для этих целей?

    • @IlyaBodrovKrukowski
      @IlyaBodrovKrukowski  2 роки тому

      Да есть, я просто привык как-то, если честно :)

  • @alexanonym1584
    @alexanonym1584 3 місяці тому

    What %i[...] means?

  • @alexanderyakovlev5201
    @alexanderyakovlev5201 2 роки тому

    Для того, чтобы при выборе локали не редиректило каждый раз на главную страницу, вместо root_path использовал url_for(locale: locale) - вроде работает норм.

    • @alexanderyakovlev5201
      @alexanderyakovlev5201 2 роки тому

      Или можно еще request.params.merge(locale: locale) - это в GoRails подсмотрел. Не подскажете, как в плане безопасности данные решения?

    • @IlyaBodrovKrukowski
      @IlyaBodrovKrukowski  2 роки тому +2

      ​@@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

    • @IlyaBodrovKrukowski
      @IlyaBodrovKrukowski  2 роки тому +2

      @@alexanderyakovlev5201 В целом, да - url_for должен подойти в нашем случае, но он, к сожалению, не сохраняет дополнительные GET-параметры. Например, localhost:3000/ru/questions/40/answers/15/edit?test=str&other=test - редирект пойдёт на localhost:3000/ru/questions/40/answers/15/edit, всё после знака вопроса потеряется. В простых случаях это вполне ок

    • @kremkasu
      @kremkasu Рік тому

      @@IlyaBodrovKrukowski Спасибо за этот ответ! Тоже посчитала, что можно использовать простое решение типа url_for controller: controller_name, action: action_name, locale: locale. В предыдущих настройках locale я так и делала.

  • @user-gf1qz4vm6u
    @user-gf1qz4vm6u 2 роки тому +1

    что за синтаксис? scope "(:locale)", locale: /en|ru/ do нигде не нашел документации

    • @IlyaBodrovKrukowski
      @IlyaBodrovKrukowski  2 роки тому +2

      Необязательный параметр в маршруте

    • @user-gf1qz4vm6u
      @user-gf1qz4vm6u 2 роки тому

      @@IlyaBodrovKrukowski да, спасибо, про это вроде накопал. А вот второй параметр это что опция? что мы ей обозначаем?

    • @IlyaBodrovKrukowski
      @IlyaBodrovKrukowski  2 роки тому +1

      @@user-gf1qz4vm6u Это регулярное выражение, которое говорит, какие значения могут быть