rails i18n - перевод текста со ссылками внутри


101

Я бы хотел написать такой текст:

Уже зарегистрировались? Авторизоваться!

Обратите внимание, что в тексте есть ссылка. В этом примере он указывает на Google - на самом деле он указывает на мое приложение log_in_path.

Я нашел два способа сделать это, но ни один из них не выглядит «правильным».

Первый способ, который я знаю, предполагает наличие моего en.yml:

log_in_message: "Already signed up? <a href='{{url}}'>Log in!</a>"

И на мой взгляд:

<p> <%= t('log_in_message', :url => login_path) %> </p>

Это работает , но мне кажется , что эта <a href=...</a>деталь en.ymlне очень чиста.

Другой известный мне вариант - это использование локализованных представлений - login.en.html.erbи login.es.html.erb.

Это также не кажется правильным, поскольку единственной другой строкой будет вышеупомянутая; остальная часть представления (~ 30 строк) будет повторяться для всех представлений. Было бы не очень СУХОЕ.

Думаю, я мог бы использовать «локализованные частичные файлы», но это кажется слишком громоздким; Я думаю, что предпочитаю первый вариант иметь так много маленьких файлов просмотра.

Итак, мой вопрос: есть ли «правильный» способ реализовать это?



@Wuggy Foofie Не надо было дублировать вопрос. И ответ Симоны лучше, чем у тебя.
— kikito

Ответы:


179

en.yml

log_in_message_html: "This is a text, with a %{href} inside."
log_in_href: "link"

login.html.erb

<p> <%= t("log_in_message_html", href: link_to(t("log_in_href"), login_path)) %> </p>

66
В Rails 3 синтаксис для этого был изменен на %{href}строку перевода YAML. Кроме того, поскольку вывод автоматически экранируется, вам нужно либо указать, rawлибо .html_safeявно, либо суффиксировать ключ перевода с помощью _html, так как in login_message_htmlи экранирование будет пропущено автоматически.
— Coreyward 09

15
на всякий случай, если это не очевидно (и для тех, кому лень проверять журнал редактирования) .. ответ выше уже был отредактирован, чтобы включить комментарий @ coreyward.
— abbood

2
Если у вас есть что-то большее, чем одно слово в тексте ссылки, такие переводы приведут к странным переводам. Например, «У нас есть удивительное <a href='x'> предложение разных вещей </a>, которое вы можете купить. Вы отправляете нарезанный предмет переводчику, и вы, вероятно, получите две фразы, которые читаются как« Мы у вас есть потрясающая <a href='x'> куча товаров </a>, которые вы можете купить "на других языках. Лучше всего найти решение, которое не разделяет их на части.
— trcarden

3
@Archonic Это неправда. t('string')идентично t("string"). Это одно и то же.
— meagar

3
Полюбил рельсы, усложняющие поиск ссылок. должен выглядеть такt('some.key', link: link_to()).html_safe
— Эдди

11

Разделение текста и ссылки в файле locale.yml работает некоторое время, но с более длинным текстом их трудно переводить и поддерживать, поскольку ссылка находится в отдельном элементе перевода (как в ответе Симонеса). Если у вас появляется много строк / переводов со ссылками, вы можете еще немного подсушить.

Я сделал один помощник в своем application_helper.rb:

# Converts
# "string with __link__ in the middle." to
# "string with #{link_to('link', link_url, link_options)} in the middle."
def string_with_link(str, link_url, link_options = {})
  match = str.match(/__([^_]{2,30})__/)
  if !match.blank?
    raw($` + link_to($1, link_url, link_options) + $')
  else
    raise "string_with_link: No place for __link__ given in #{str}" if Rails.env.test?
    nil
  end
end

В моем en.yml:

log_in_message: "Already signed up? __Log in!__"

И на мой взгляд:

<p><%= string_with_link(t('.log_in_message'), login_path) %></p>

Таким образом, легче переводить сообщения, так как текст ссылки четко определен в файлах locale.yml.


6
Отличное решение. Я поместил это в Gem, который позволяет вам определять ссылки на вещи This is a %{link:link to Google}. Он позволяет вам иметь несколько ссылок в одной строке, заботится о XSS и разрешает вложенные переводы. Посмотрите на github.com/iGEL/i18n_link
— iGEL

Я сделал это с помощью "str = t str", поэтому я просто даю ключ перевода в функции. более удобный!
— Тим Кречмер

1
Если бы я мог, я бы проголосовал больше за @iGEL. Проект перемещен на github.com/iGEL/it, и если вы хотите использовать его в контроллере для flashсообщений в Rails 3+, сделайте это так,view_context.it(key, ...)
— Крис Бек,

Вот лучший пример использования его в контроллере - github.com/iGEL/it/issues/10
— Крис Бек


5

В en.yml

registration:
    terms:
      text: "I do agree with the terms and conditions: %{gtc} / %{stc}"
      gtc: "GTC"
      stc: "STC"

In de.yml

registration:
    terms:
      text: "Ich stimme den Geschäfts- und Nutzungsbedingungen zu: %{gtc} / %{stc}"
      gtc: "AGB"
      stc: "ANB"

в new.html.erb [предполагается]

<%= t(
   'registration.terms.text',
    gtc:  link_to(t('registration.terms.gtc'),  terms_and_conditions_home_index_url + "?tab=gtc"),
    stc: link_to(t('registration.terms.stc'), terms_and_conditions_home_index_url + "?tab=stc")
 ).html_safe %>

3

Большое спасибо, Холли, за то, что поделились этим подходом. На меня это действует как оберег. Я бы проголосовал за вас, если бы мог, но это мой первый пост, поэтому мне не хватает должной репутации ... В качестве дополнительной части головоломки: проблема, которую я понял с вашим подходом, заключается в том, что он все еще не работает изнутри контроллер. Я провел небольшое исследование и совместил ваш подход с подходом Гленна на Рубинском пруду .

Вот что я придумал:

Просмотр помощника, например application_helper.rb

  def render_flash_messages
    messages = flash.collect do |key, value|
      content_tag(:div, flash_message_with_link(key, value), :class => "flash #{key}") unless key.to_s =~ /_link$/i
    end
    messages.join.html_safe
  end

  def flash_message_with_link(key, value)
    link = flash["#{key}_link".to_sym]
    link.nil? ? value : string_with_link(value, link).html_safe
  end

  # Converts
  # "string with __link__ in the middle." to
  # "string with #{link_to('link', link_url, link_options)} in the middle."
  # --> see http://stackoverflow.com/questions/2543936/rails-i18n-translating-text-with-links-inside (holli)
  def string_with_link(str, link_url, link_options = {})
    match = str.match(/__([^_]{2,30})__/)
    if !match.blank?
      $` + link_to($1, link_url, link_options) + $'
    else
      raise "string_with_link: No place for __link__ given in #{str}" if Rails.env.test?
      nil
    end
  end

В контроллере:

flash.now[:alert] = t("path.to.translation")
flash.now[:alert_link] = here_comes_the_link_path # or _url

В locale.yml:

path:
  to:
    translation: "string with __link__ in the middle"

В представлении:

<%= render_flash_messages %>

Надеюсь, этот пост заработает мне репутацию, чтобы проголосовать за вас, привет :) Любые отзывы приветствуются.


2

У нас было следующее:

module I18nHelpers
  def translate key, options={}, &block
    s = super key, options  # Default translation
    if block_given?
      String.new(ERB::Util.html_escape(s)).gsub(/%\|([^\|]*)\|/){
        capture($1, &block)  # Pass in what's between the markers
      }.html_safe
    else
      s
    end
  end
  alias :t :translate
end

или более явно:

module I18nHelpers

  # Allows an I18n to include the special %|something| marker.
  # "something" will then be passed in to the given block, which
  # can generate whatever HTML is needed.
  #
  # Normal and _html keys are supported.
  #
  # Multiples are ok
  #
  #     mykey:  "Click %|here| and %|there|"
  #
  # Nesting should work too.
  #
  def translate key, options={}, &block

    s = super key, options  # Default translation

    if block_given?

      # Escape if not already raw HTML (html_escape won't escape if already html_safe)
      s = ERB::Util.html_escape(s)

      # ActiveSupport::SafeBuffer#gsub broken, so convert to String.
      # See https://github.com/rails/rails/issues/1555
      s = String.new(s)

      # Find the %|| pattern to substitute, then replace it with the block capture
      s = s.gsub /%\|([^\|]*)\|/ do
        capture($1, &block)  # Pass in what's between the markers
      end

      # Mark as html_safe going out
      s = s.html_safe
    end

    s
  end
  alias :t :translate


end

то в ApplicationController.rb просто

class ApplicationController < ActionController::Base
  helper I18nHelpers

Учитывая ключ в en.ymlфайле, например

mykey: "Click %|here|!"

может использоваться в ERB как

<%= t '.mykey' do |text| %>
  <%= link_to text, 'http://foo.com' %>
<% end %>

должен генерировать

Click <a href="http://foo.com">here</a>!

1

Мне нужно было немного больше гибкости, чем просто добавление ссылок на флэш-сообщения из файлов YAML (например, имя пользователя, вошедшее в систему и т. Д.), Поэтому вместо этого я хотел использовать нотацию ERB в строке.

Поскольку я использую, bootstrap_flashя изменил вспомогательный код следующим образом, чтобы декодировать строки ERB перед отображением:

require 'erb'

module BootstrapFlashHelper
  ALERT_TYPES = [:error, :info, :success, :warning] unless const_defined?(:ALERT_TYPES)

  def bootstrap_flash
    flash_messages = []
    flash.each do |type, message|
      # Skip empty messages, e.g. for devise messages set to nothing in a locale file.
      next if message.blank?

      type = type.to_sym
      type = :success if type == :notice
      type = :error   if type == :alert
      next unless ALERT_TYPES.include?(type)

      Array(message).each do |msg|
        begin
          msg = ERB.new(msg).result(binding) if msg
        rescue Exception=>e
          puts e.message
          puts e.backtrace
        end
        text = content_tag(:div,
                           content_tag(:button, raw("&times;"), :class => "close", "data-dismiss" => "alert") +
                           msg.html_safe, :class => "alert fade in alert-#{type}")
        flash_messages << text if msg
      end
    end
    flash_messages.join("\n").html_safe
  end
end

Затем можно использовать следующие строки (с помощью devise):

signed_in: "Welcome back <%= current_user.first_name %>! <%= link_to \"Click here\", account_path %> for your account."

Это может работать не во всех ситуациях, и может быть аргумент, что определения кода и строк не следует смешивать (особенно с точки зрения СУХОЙ), но мне кажется, что это хорошо работает. Код должен быть адаптирован для многих других ситуаций, при этом важны следующие моменты:

require 'erb'

....

        begin
          msg = ERB.new(msg).result(binding) if msg
        rescue Exception=>e
          puts e.message
          puts e.backtrace
        end


-4

Почему бы не использовать первый способ, а разделить его как

log_in_message: Already signed up?
log_in_link_text: Log in!

А потом

<p> <%= t('log_in_message') %> <%= link_to t('log_in_link_text'), login_path %> </p>

Извините, это решение не сработает. Имейте в виду, что я хотел перевести текст на другие языки. Это означает, что в некоторых случаях «ссылка» может быть в начале или в середине текста. Ваше решение заставляет ссылку быть в конце (плохо переводится).
— kikito
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.