rails i18n - dịch văn bản với các liên kết bên trong


101

Tôi muốn viết một văn bản giống như sau:

Đã đăng ký? Đăng nhập!

Lưu ý rằng có một liên kết trên văn bản. Trong ví dụ này, nó trỏ đến google - trên thực tế nó sẽ trỏ đến ứng dụng của tôi log_in_path.

Tôi đã tìm thấy hai cách để làm điều này, nhưng không có cách nào trong số chúng có vẻ "đúng".

Cách đầu tiên tôi biết liên quan đến việc có cái này của tôi en.yml:

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

Và theo quan điểm của tôi:

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

Điều này hoạt động , nhưng có <a href=...</a>một phần trên en.ymltrông không được sạch sẽ đối với tôi.

Tùy chọn khác mà tôi biết là sử dụng các chế độ xem được bản địa hóa - login.en.html.erblogin.es.html.erb.

Điều này cũng không ổn vì dòng khác duy nhất sẽ là dòng đã nói ở trên; phần còn lại của chế độ xem (~ 30 dòng) sẽ được lặp lại cho tất cả các chế độ xem. Nó sẽ không rất KHÔ.

Tôi đoán tôi có thể sử dụng "các thành phần được bản địa hóa" nhưng điều đó có vẻ quá dồn dập; Tôi nghĩ rằng tôi thích tùy chọn đầu tiên hơn để có nhiều tệp chế độ xem nhỏ như vậy.

Vì vậy, câu hỏi của tôi là: có một cách "thích hợp" để thực hiện điều này?



@Wuggy Foofie Bạn không nên lặp lại câu hỏi. Và câu trả lời của Simone tốt hơn những câu bạn nhận được.
kikito 19/09/12

Câu trả lời:


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
Trong Rails 3, cú pháp cho điều này đã thay đổi thành %{href}trong chuỗi dịch YAML. Ngoài ra, vì đầu ra được tự động thoát, bạn cần phải chỉ định rawhoặc .html_saferõ ràng, hoặc nối khóa dịch của bạn với _html, vì trong login_message_htmlvà thoát sẽ tự động bị bỏ qua.
coreyward

15
đề phòng nếu nó không rõ ràng (và đối với những người quá lười biếng để kiểm tra nhật ký chỉnh sửa) .. câu trả lời ở trên đã được chỉnh sửa để bao gồm nhận xét của @ coreyward.
abbood

2
Nếu bạn có nhiều hơn một từ trong các bản dịch tách văn bản liên kết như thế này sẽ tạo ra các bản dịch kỳ lạ. Ví dụ: "Chúng tôi có một <a href='x'> cung cấp các loại </a> tuyệt vời mà bạn có thể mua. Bạn gửi thứ đã cắt nhỏ cho người dịch và bạn có khả năng nhận được hai cụm từ giống như" Chúng tôi có một <a href='x'> toàn bộ các mặt hàng </a> tuyệt vời mà bạn có thể mua "bằng các ngôn ngữ khác. Tốt nhất là bạn nên tìm một giải pháp không tách chúng ra.
trcarden 10/12/14

3
@Archonic Điều đó không đúng. t('string')giống hệt với t("string"). Chúng giống nhau.
Mudgar

3
Phải yêu thích đường ray làm phức tạp f out of links. nên trông như thế nàyt('some.key', link: link_to()).html_safe
Eddie

11

Tách văn bản và liên kết trong tệp locale.yml hoạt động trong một thời gian nhưng với văn bản dài hơn, chúng khó dịch và duy trì vì liên kết nằm trong mục dịch riêng biệt (như trong câu trả lời của Simones). Nếu bạn bắt đầu có nhiều chuỗi / bản dịch với các liên kết, bạn có thể làm khô nó thêm một chút.

Tôi đã tạo một người trợ giúp trong application_helper.rb của mình:

# 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

Trong en.yml của tôi:

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

Và theo quan điểm của tôi:

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

Bằng cách này, việc dịch thư dễ dàng hơn vì văn bản liên kết cũng được xác định rõ ràng trong tệp locale.yml.


6
Giải pháp tuyệt vời. Tôi đặt nó vào một Gem, cho phép bạn xác định mối liên kết của mọi thứ This is a %{link:link to Google}. Nó cho phép bạn có nhiều liên kết trong một chuỗi duy nhất, chăm sóc XSS và cho phép các bản dịch lồng nhau. Hãy xem github.com/iGEL/i18n_link
iGEL vào

tôi đã làm điều đó với "str = t str" vì vậy tôi chỉ cung cấp khóa dịch trong hàm. thoải mái hơn!
Tim Kretschmer,

1
Tôi sẽ ủng hộ @iGEL nhiều hơn nếu có thể. Dự án đã chuyển github.com/iGEL/it và nếu bạn muốn sử dụng nó trong bộ điều khiển cho flashtin nhắn trong Rails 3+, hãy làm như thế nàyview_context.it(key, ...)
Chris Beck

Dưới đây là một ví dụ tốt hơn để sử dụng nó trong bộ điều khiển - github.com/iGEL/it/issues/10
Chris Beck


5

Trong en.yml

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

Trong de.yml

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

trong new.html.erb [giả định]

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

Cảm ơn bạn rất nhiều, holli, vì đã chia sẻ phương pháp này. Nó hoạt động như một sự quyến rũ đối với tôi. Sẽ bình chọn cho bạn nếu tôi có thể, nhưng đây là bài đăng đầu tiên của tôi nên tôi thiếu danh tiếng thích hợp ... Như một phần bổ sung cho câu đố: Vấn đề tôi nhận ra với cách tiếp cận của bạn là nó vẫn không hoạt động từ bên trong bộ điều khiển. Tôi đã thực hiện một số nghiên cứu và kết hợp phương pháp của bạn với phương pháp của Glenn trên rubypond .

Đây là những gì tôi nghĩ ra:

Xem trình trợ giúp, ví dụ: 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

Trong bộ điều khiển:

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

Trong locale.yml:

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

Theo quan điểm:

<%= render_flash_messages %>

Hy vọng bài viết này giúp tôi có được danh tiếng để bình chọn bạn, holli :) Mọi phản hồi đều được hoan nghênh.


2

Chúng tôi đã có những điều sau đây:

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

hoặc rõ ràng hơn:

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

thì trong ApplicationController.rb chỉ

class ApplicationController < ActionController::Base
  helper I18nHelpers

Đưa ra một khóa trong en.ymltệp như

mykey: "Click %|here|!"

có thể được sử dụng trong ERB như

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

nên tạo ra

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

1

Tôi muốn linh hoạt hơn một chút so với việc chỉ thêm liên kết vào tin nhắn flash từ tệp YAML (ví dụ: tên người dùng đã đăng nhập, v.v.) vì vậy thay vào đó tôi muốn sử dụng ký hiệu ERB trong chuỗi.

Vì tôi đang sử dụng bootstrap_flashnên tôi đã sửa đổi mã trình trợ giúp như sau để giải mã chuỗi ERB trước khi hiển thị:

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

Sau đó, có thể sử dụng các chuỗi như sau (sử dụng devise):

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

Điều này có thể không hoạt động cho tất cả các tình huống và có thể có một lập luận rằng định nghĩa mã và chuỗi không nên trộn lẫn (đặc biệt là từ quan điểm KHÔ), nhưng điều này dường như hoạt động tốt đối với tôi. Mã phải có thể thích ứng cho nhiều trường hợp khác, các bit quan trọng là sau:

require 'erb'

....

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

-2

Tôi nghĩ một cách đơn giản để làm điều này là làm:

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

-4

Tại sao không sử dụng cách đầu tiên mà lại chia nhỏ nó ra như

log_in_message: Already signed up?
log_in_link_text: Log in!

Và sau đó

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

Xin lỗi, giải pháp này sẽ không hoạt động. Hãy nhớ rằng tôi muốn dịch văn bản sang các ngôn ngữ khác. Điều này có nghĩa là trong một số trường hợp, "liên kết" có thể nằm ở đầu hoặc ở giữa văn bản. Giải pháp của bạn buộc liên kết phải ở cuối (dịch không tốt).
kikito
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.