rails i18n - menerjemahkan teks dengan tautan di dalamnya


101

Saya ingin memberi teks yang terlihat seperti ini:

Sudah mendaftar? Gabung!

Perhatikan bahwa ada link di teks tersebut. Pada contoh ini, ini menunjuk ke google - pada kenyataannya itu akan mengarah ke aplikasi saya log_in_path.

Saya telah menemukan dua cara untuk melakukan ini, tetapi tidak satupun dari mereka terlihat "benar".

Cara pertama yang saya tahu melibatkan memiliki ini milik saya en.yml:

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

Dan menurut saya:

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

Ini berfungsi , tetapi memiliki <a href=...</a>bagian pada en.ymltidak terlihat sangat bersih bagi saya.

Opsi lain yang saya tahu adalah menggunakan tampilan yang dilokalkan - login.en.html.erb, dan login.es.html.erb.

Ini juga terasa tidak benar karena satu-satunya baris yang berbeda adalah baris yang disebutkan di atas; sisa tampilan (~ 30 baris) akan diulangi untuk semua tampilan. Ini tidak akan terlalu KERING.

Saya kira saya bisa menggunakan "parsial yang dilokalkan" tetapi itu tampaknya terlalu konyol; Saya rasa saya lebih suka opsi pertama untuk memiliki begitu banyak file tampilan kecil.

Jadi pertanyaan saya adalah: apakah ada cara yang "tepat" untuk menerapkan ini?



@Wuggy Foofie Anda seharusnya tidak menduplikasi pertanyaan itu. Dan jawaban Simone lebih baik dari yang Anda dapatkan.
kikito

Jawaban:


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
Di Rails 3 sintaks untuk ini telah diubah menjadi %{href}dalam string terjemahan YAML. Selain itu, karena keluaran secara otomatis di-escape, Anda perlu menentukan rawatau .html_safesecara eksplisit, atau menambahkan akhiran kunci terjemahan Anda dengan _html, karena in login_message_htmldan escaping akan dilewati secara otomatis.
coreyward

15
untuk jaga-jaga kalau kurang jelas (dan bagi yang malas cek di edit log) .. jawaban diatas sudah di edit untuk menyertakan komentar @ coreyward.
abbood

2
Jika Anda memiliki lebih dari satu kata dalam terjemahan yang memisahkan teks tautan seperti ini akan menghasilkan terjemahan yang aneh. Misalnya "Kami memiliki <a href='x'> penawaran berbagai macam barang </a> menakjubkan yang dapat Anda beli. Anda mengirimkan potongan-potongan itu kepada penerjemah dan kemungkinan besar Anda akan mendapatkan dua frasa yang berbunyi seperti" Kami memiliki <a href='x'> banyak sekali item </a> yang luar biasa yang dapat Anda beli "dalam bahasa lain. Terbaik untuk menemukan solusi yang tidak memisahkan mereka.
trcarden

3
@Archonic Itu tidak benar. t('string')identik dengan t("string"). Mereka sama saja.
meagar

3
Harus mencintai rel yang memperumit tautan. harus terlihat seperti init('some.key', link: link_to()).html_safe
Eddie

11

Memisahkan teks dan tautan dalam file locale.yml berfungsi untuk sementara waktu tetapi dengan teks yang lebih panjang sulit untuk diterjemahkan dan dipelihara karena tautan berada dalam item terjemahan terpisah (seperti dalam jawaban Simones). Jika Anda mulai memiliki banyak string / terjemahan dengan tautan, Anda dapat mengeringkannya sedikit lagi.

Saya membuat satu pembantu di application_helper.rb saya:

# 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

Di en.yml saya:

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

Dan menurut saya:

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

Dengan cara ini, lebih mudah untuk menerjemahkan pesan karena teks tautan juga didefinisikan dengan jelas di file locale.yml.


6
Solusi bagus. Saya memasukkan ini ke dalam Permata, yang memungkinkan Anda untuk menentukan tautan sesuatu This is a %{link:link to Google}. Ini memungkinkan Anda memiliki banyak tautan dalam satu string, menangani XSS, dan mengizinkan terjemahan bertingkat. Silakan
iGEL

saya melakukannya dengan "str = t str" jadi saya hanya memberikan kunci terjemahan dalam fungsinya. lebih nyaman!
Tim Kretschmer

1
Saya akan memberi suara lebih pada @iGEL jika saya bisa. Proyek telah memindahkan github.com/iGEL/it dan jika Anda ingin menggunakannya di pengontrol untuk flashpesan di Rails 3+ lakukan seperti iniview_context.it(key, ...)
Chris Beck

Berikut adalah contoh yang lebih baik untuk menggunakannya dalam pengontrol - github.com/iGEL/it/issues/10
Chris Beck


5

Dalam en.yml

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

Di de.yml

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

di new.html.erb [diasumsikan]

<%= 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

Terima kasih banyak, holli, telah membagikan pendekatan ini. Ini bekerja seperti pesona bagi saya. Akan memilih Anda jika saya bisa, tetapi ini adalah posting pertama saya jadi saya kurang memiliki reputasi yang tepat ... Sebagai tambahan dari teka-teki: Masalah yang saya sadari dengan pendekatan Anda adalah bahwa itu masih tidak akan berhasil dari dalam pengontrol. Saya melakukan beberapa penelitian dan menggabungkan pendekatan Anda dengan pendekatan dari Glenn di rubypond .

Inilah yang saya dapatkan:

Lihat helper, misalnya 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

Di pengontrol:

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

Di locale.yml:

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

Dalam tampilan:

<%= render_flash_messages %>

Semoga posting ini membuat saya mendapat reputasi untuk memilih Anda, holli :) Setiap umpan balik diterima.


2

Kami memiliki yang berikut ini:

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

atau lebih eksplisit:

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

lalu di ApplicationController.rb saja

class ApplicationController < ActionController::Base
  helper I18nHelpers

Diberikan kunci di en.ymlfile seperti

mykey: "Click %|here|!"

dapat digunakan di ERB sebagai

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

harus menghasilkan

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

1

Saya ingin sedikit lebih fleksibel daripada hanya menambahkan tautan ke pesan flash dari file YAML (misalnya nama pengguna yang masuk, dll.) Jadi saya ingin menggunakan notasi ERB dalam string.

Seperti yang saya gunakan, bootstrap_flashjadi saya memodifikasi kode pembantu sebagai berikut untuk memecahkan kode string ERB sebelum menampilkan:

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

Kemudian dimungkinkan untuk menggunakan string seperti berikut (menggunakan devise):

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

Ini mungkin tidak berfungsi untuk semua situasi dan mungkin ada argumen bahwa definisi kode dan string tidak boleh dicampur (terutama dari perspektif KERING), tetapi ini tampaknya berfungsi dengan baik untuk saya. Kode harus dapat beradaptasi untuk banyak situasi lain, bit penting adalah sebagai berikut:

require 'erb'

....

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

-2

Menurut saya cara sederhana untuk melakukannya adalah dengan melakukan:

<%= link_to some_path do %>
<%= t '.some_locale_key' %>
<% end %>

-4

Kenapa tidak menggunakan cara pertama, tapi membaginya seperti

log_in_message: Already signed up?
log_in_link_text: Log in!

Lalu

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

Maaf, solusi ini tidak akan berhasil. Ingatlah bahwa saya ingin menerjemahkan teks ke bahasa lain. Ini berarti bahwa dalam beberapa kesempatan, "tautan" bisa berada di awal, atau di tengah teks. Solusi Anda memaksa tautan berada di akhir (tidak diterjemahkan dengan baik).
kikito
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.