rails i18n-内部にリンクがあるテキストの翻訳


101

次のようなテキストをi18nにしたい:

すでにサインアップしていますか?ログインする!

テキストにリンクがあることに注意してください。この例では、それはgoogleを指しています-実際にはそれは私のアプリのものを指していlog_in_pathます。

私はこれを行う2つの方法を見つけましたが、どれも「正しく」見えません。

私が知っている最初の方法はこれを私のものにすること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は私にはあまりきれいに見えません。

私が知っているもう1つのオプションは、ローカライズされたビューを使用することです- login.en.html.erbおよびlogin.es.html.erb

これはまた、上記の1行だけが異なるため、正しくありません。残りのビュー(〜30行)はすべてのビューで繰り返されます。あまり乾燥しないでしょう。

「ローカライズされたパーシャル」を使用できると思いますが、それは面倒すぎるようです。私は、非常に多くの小さなビューファイルを持つよりも、最初のオプションを好むと思います。

だから私の質問は:これを実装するための「適切な」方法はありますか?



@Wuggy Foofie質問を重複してはいけません。そして、シモンの答えはあなたが得たものよりも優れています。
kikito 2012

回答:


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のコメントを含むように既に編集されています。
13

2
リンクテキストに複数の単語がある場合、このように翻訳を分割すると、奇妙な翻訳が生成されます。たとえば、「私たちはあなたが購入できる素晴らしい<a href='x'>盛り合わせ品</a>を提供しています。細かく切ったものを翻訳者に送ると、「私たちはアイテムは、あなたが他の言語で「買うことができるという</A>という驚くべき<a href='x'>全体の束を持っているベスト離れてそれらを分割しない解決策を見つけるために。。
trcarden

3
@Archonicそれは真実ではありません。t('string')と同じですt("string")。それらは同じものです。
貧弱

3
リンクからのfを複雑にするレールが好きになりました。このように見えるはずですt('some.key', link: link_to()).html_safe
エディ

11

locale.ymlファイルでテキストとリンクを分離することはしばらくの間は機能しますが、長いテキストでは、リンクが別個の翻訳項目にあるため(Simonesの回答のように)、それらを翻訳および維持することが困難です。リンクを含む多くの文字列/翻訳がある場合は、もう少し乾燥させることができます。

私はapplication_helper.rbにヘルパーを1つ作成しました。

# 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-filesで明確に定義されるため、メッセージの翻訳が容易になります。


6
素晴らしいソリューション。これをGemに入れますThis is a %{link:link to Google}。これにより、物事のリンクを定義できます。1つの文字列に複数のリンクを含めることができ、XSSを処理し、ネストされた翻訳を可能にします。見ていgithub.com/iGEL/i18n_link
IGEL

私は「str = t str」でそれをやったので、関数に翻訳キーを与えるだけです。より快適に!
Tim Kretschmer、2012

1
できれば@iGELをもっと賛成します。プロジェクトはgithub.com/iGEL/itを移動しました。Rails3以降flashメッセージのコントローラーで使用する場合は、次のようにしますview_context.it(key, ...)
Chris Beck

これは、コントローラで使用するためのより良い例です-github.com/iGEL/it/issues/10
Chris Beck


5

ではen.yml

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

では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 %>

この投稿が私をあなたに投票するという評判を獲得してくれることを願っています、holli :)どんなフィードバックでも大歓迎です。


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."

これはすべての状況で機能するわけではなく、コードと文字列の定義を(特にDRYの観点から)混在させるべきではないという議論があるかもしれませんが、これは私にはうまく機能しているようです。コードは他の多くの状況に適応可能でなければなりません、重要なビットは次のとおりです:

require 'erb'

....

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

-2

これを行う簡単な方法は、単に次のようにすることだと思います。

<%= link_to some_path do %>
<%= t '.some_locale_key' %>
<% 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>

このソリューションは機能しません。テキストを他の言語に翻訳したかったことを覚えておいてください。これは、場合によっては、「リンク」がテキストの最初または途中にあることを意味します。あなたの解決策はリンクが最後になるように強制します(うまく翻訳されません)。
キキト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.