rails i18n-翻译带有链接的文本


101

我想输入一个看起来像这样的文本:

已经注册?登录!

请注意,文本上有一个链接。在此示例中,它指向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.erblogin.es.html.erb

这也感觉不对,因为唯一不同的行是上述行。其余视图(约30行)将对所有视图重复。不会很干。

我想我可以使用“本地化的局部词”,但这似乎太麻烦了。我想我比拥有这么多小视图文件更喜欢第一种选择。

所以我的问题是:是否有“适当”的方法来实现这一目标?



@Wuggy Foofie您应该没有重复这个问题。西蒙妮的答案比你得到的答案要好。
kikito 2012年

Answers:


178

英文

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,因为in login_message_html和转义将被自动跳过。
coreyward 2011年

15
以防万一,如果不是很明显(对于那些懒得检查编辑日志的人)。. 上面的答案已经过编辑,以包含@coreyward的注释。
2013年

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's

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中,它允许您定义事物link This is a %{link:link to Google}。它使您可以在单个字符串中具有多个链接,可以处理XSS并允许嵌套翻译。看看github.com/iGEL/i18n_link
iGEL 2011年

我是用“ str = t str”完成的,所以我只在函数中提供了翻译键。更舒服!
蒂姆·克雷奇默

1
如果可以的话,我会更多地投票@iGEL。该项目已移至github.com/iGEL/it,如果您想flash在Rails 3+中的消息中将其用于控制​​器中,请执行以下操作view_context.it(key, ...)
Chris Beck

这是在控制器中使用它的一个更好的示例-github.com/iGEL/it/issues/10
Chris Beck

8

我拿了hollis解决方案,并从中制成了一颗宝石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

霍利,非常感谢您分享这种方法。对我来说,它就像是一种魅力。如果可以的话,请投票给您,但这是我的第一篇文章,所以我没有适当的声誉。。。另外一个难题:我意识到您的方法存在的问题是,它仍然无法从内部使用控制器。我做了一些研究,并将您的方法与Glenn在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 %>

希望这篇文章能赢得我的声誉,以投票支持你,霍利:)欢迎任何反馈。


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文件的Flash消息(例如,已登录的用户名等),所以我想在字符串中使用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>

抱歉,此解决方案不起作用。请记住,我想将文本翻译成其他语言。这意味着在某些情况下,“链接”可能位于文本的开头或中间。您的解决方案会强制链接位于末尾(转换效果不佳)。
kikito 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.