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


101

Я хотів би i18n текст, який виглядає приблизно так:

Вже підписалися? Увійти!

Зверніть увагу, що на тексті є посилання. У цьому прикладі він вказує на 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 Ви не повинні дублювати питання. І відповідь Сімони краща за ту, яку ти отримав.
кікіто

Відповіді:


178

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, так як вхідне login_message_htmlі прохідне буде пропущено автоматично.
coreyward

15
на випадок, якщо це не очевидно (і для тих, хто лінивий перевірити журнал редагування) .. відповідь вище вже було відредаговано, щоб включити коментар @ coreyward.
абат

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 працює деякий час, але з більш довгим текстом їх важко перекласти та підтримувати, оскільки посилання знаходиться в окремому елементі перекладу (як у відповіді Simones). Якщо у вас є багато рядків / перекладів із посиланнями, ви можете висушити їх трохи більше.

Я зробив одного помічника у своєму 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/isissue/10
Кріс Бек


5

В ен.імл

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

У деімл

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

Тоді можна використовувати рядки типу: (використовуючи пристрій):

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

Це може працювати не у всіх ситуаціях, і може бути аргумент, що визначення коду та рядка не слід змішувати (особливо з точки зору DRY), але це, здається, працює добре для мене. Код повинен бути адаптованим для багатьох інших ситуацій, важливими бітами є наступні:

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>

На жаль, це рішення не працюватиме. Майте на увазі, що я хотів перекласти текст іншими мовами. Це означає, що в деяких випадках "посилання" може бути на початку, або в середині тексту. Ваше рішення змушує посилання бути в кінці (не добре перекладається).
кікіто
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.