ราง i18n - แปลข้อความพร้อมลิงก์ด้านใน


101

ฉันต้องการ i18n ข้อความที่มีลักษณะดังนี้:

ลงทะเบียนแล้ว? เข้าสู่ระบบ!

โปรดทราบว่ามีลิงก์บนข้อความ ตัวอย่างนี้จะชี้ไปที่ google - ในความเป็นจริงมันจะชี้ไปที่ของ log_in_pathapp

ฉันพบสองวิธีในการทำเช่นนี้ แต่ไม่มีวิธีใดที่ "ถูกต้อง"

วิธีแรกที่ฉันรู้ว่ามีสิ่งนี้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.erblogin.es.html.erb

สิ่งนี้ก็ไม่ถูกต้องเช่นกันเนื่องจากบรรทัดเดียวที่แตกต่างกันจะเป็นบรรทัดที่กล่าวมา ส่วนที่เหลือของมุมมอง (~ 30 บรรทัด) จะถูกทำซ้ำสำหรับมุมมองทั้งหมด มันจะไม่แห้งมาก

ฉันเดาว่าฉันสามารถใช้ "localized partials" ได้ แต่ดูเหมือนว่า cumberstone เกินไป ฉันคิดว่าฉันชอบตัวเลือกแรกในการมีไฟล์มุมมองขนาดเล็กจำนวนมาก

คำถามของฉันคือ: มีวิธีที่ "เหมาะสม" ในการนำไปใช้หรือไม่?


อะไรประมาณนี้ stackoverflow.com/questions/12334183/…
Mark Boulder

@Wuggy Foofie คุณไม่ควรทำซ้ำคำถาม และคำตอบของ Simone นั้นดีกว่าคำตอบที่คุณได้รับ
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 นอกจากนี้เนื่องจากเอาต์พุตจะถูก Escape โดยอัตโนมัติคุณจึงต้องระบุrawหรือระบุ.html_safeอย่างชัดเจนหรือต่อท้ายคีย์การแปลของคุณด้วย_htmlเนื่องจากในlogin_message_htmlและ Escape จะถูกข้ามโดยอัตโนมัติ
coreyward

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
Eddie

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
ทางออกที่ดี ฉันใส่สิ่งนี้ลงในอัญมณีซึ่งช่วยให้คุณสามารถกำหนดลิงค์สิ่งต่างๆThis is a %{link:link to Google}ได้ ช่วยให้คุณมีลิงก์หลายลิงก์ในสตริงเดียวดูแล XSS และอนุญาตการแปลที่ซ้อนกัน ดูได้ที่github.com/iGEL/i18n_link
iGEL

ฉันทำด้วย "str = t str" ดังนั้นฉันเพียงแค่ให้คีย์แปลในฟังก์ชัน สบายใจกว่า!
Tim Kretschmer

1
ฉันจะโหวตให้ @iGEL มากกว่านี้ถ้าทำได้ โปรเจ็กต์ได้ย้ายgithub.com/iGEL/itและถ้าคุณต้องการใช้ในคอนโทรลเลอร์สำหรับflashข้อความใน Rails 3+ ให้ทำเช่นนี้view_context.it(key, ...)
Chris Beck

นี่คือตัวอย่างที่ดีกว่าสำหรับการใช้งานในคอนโทรลเลอร์ - github.com/iGEL/it/issues/10
Chris Beck

8

ฉันใช้วิธีแก้ปัญหาของฮอลลิสและสร้างอัญมณีขึ้นitมา ลองดูตัวอย่าง:

log_in_message: "Already signed up? %{login:Log in!}"

แล้ว

<p><%=t_link "log_in_message", :login => login_path %></p>

สำหรับรายละเอียดเพิ่มเติมโปรดดูที่https://github.com/iGEL/it


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

ขอบคุณมาก holli สำหรับการแบ่งปันแนวทางนี้ มันเป็นเสน่ห์สำหรับฉัน จะโหวตให้คุณถ้าฉันทำได้ แต่นี่เป็นโพสต์แรกของฉันดังนั้นฉันจึงไม่มีชื่อเสียงที่เหมาะสม ... ในฐานะที่เป็นส่วนเพิ่มเติมของปริศนา: ปัญหาที่ฉันรู้จากแนวทางของคุณคือมันยังใช้ไม่ได้จากข้างใน ตัวควบคุม ฉันได้บางวิจัยและรวมวิธีการของคุณกับหนึ่งจากเกล็นบน rubypond

นี่คือสิ่งที่ฉันคิดขึ้น:

ดูตัวช่วยเช่น 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

จากนั้นเป็นไปได้ที่จะใช้สตริงดังต่อไปนี้ (โดยใช้อุปกรณ์):

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.