【问题标题】:rails i18n - translating text with links insiderails i18n - 翻译带有链接的文本
【发布时间】:2011-02-02 09:21:21
【问题描述】:

我想输入如下所示的文本:

已经注册了? Log in!

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

有效,但在en.yml 上包含&lt;a href=...&lt;/a&gt; 部分对我来说看起来不是很干净。

我知道的另一个选项是使用localized views - login.en.html.erblogin.es.html.erb

这也感觉不对,因为唯一不同的就是前面提到的那一行;视图的其余部分(约 30 行)将对所有视图重复。它不会很干燥。

我想我可以使用“本地化部分”,但这似乎太麻烦了;我想我更喜欢第一个选项,而不是拥有这么多微小的视图文件。

所以我的问题是:是否有“正确”的方式来实现这一点?

【问题讨论】:

标签: ruby-on-rails internationalization


【解决方案1】:

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>

【讨论】:

  • 在 Rails 3 中,YAML 翻译字符串中的语法已更改为 %{href}。此外,由于输出会自动转义,因此您需要明确指定 raw.html_safe,或者在您的翻译密钥后添加 _html,如 login_message_html 那样,转义将被自动跳过。
  • 以防万一它不是很明显(以及那些懒得检查编辑日志的人)。上面的answer 已经被编辑为包含@coreyward 的评论。
  • 如果链接文本中的单词不止一个,这样的拆分翻译会产生奇怪的翻译。例如,“我们有一个很棒的提供各种各样的东西,您可以购买这些东西。您将切碎的东西发送给翻译,您很可能会得到两个类似“我们拥有令人惊叹的一大堆物品,您可以用其他语言购买”。最好找到一种不会将它们分开的解决方案。
  • @Archonic 这不是真的。 t('string')t("string") 相同。它们是一样的。
  • 爱上了让 f out of links 复杂化的 Rails。应该看起来像这样t('some.key', link: link_to()).html_safe
【解决方案2】:

在 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 文件中明确定义。

【讨论】:

  • 很好的解决方案。我将它放入一个 Gem,它允许您定义事物链接 This is a %{link:link to Google}。它允许您在单个字符串中拥有多个链接,处理 XSS 并允许嵌套翻译。看看github.com/iGEL/i18n_link
  • 我是用“str = t str”做的,所以我只是在函数中给出翻译键。更舒服!
  • 如果可以的话,我会更多地支持@iGEL。该项目已移动github.com/iGEL/it,如果您想在控制器中使用它来获取 Rails 3+ 中的flash 消息,请像这样view_context.it(key, ...)
  • 这是一个在控制器中使用它的更好示例 -- github.com/iGEL/it/issues/10
【解决方案3】:

我采用了 hollis 解决方案并从中提取了a gem called 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

【讨论】:

    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      非常感谢 holli 分享这种方法。它对我来说就像一个魅力。如果可以的话,我会投票给你,但这是我的第一篇文章,所以我缺乏适当的声誉......作为谜题的补充:我意识到你的方法的问题是它仍然无法从内部工作控制器。我做了一些研究,并将您的方法与 Glenn on 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 %>
      

      希望这篇文章能让我赢得投票给你的声誉,霍莉 :) 欢迎任何反馈。

      【讨论】:

        【解决方案6】:

        我们有以下:

        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>!
        

        【讨论】:

          【解决方案7】:

          我想要更多的灵活性,而不仅仅是从 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."
          

          这可能不适用于所有情况,并且可能有人认为不应混合代码和字符串定义(尤其是从 DRY 的角度来看),但这似乎对我很有效。该代码应该适用于许多其他情况,重要的部分如下:

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

          【讨论】:

            【解决方案8】:

            我认为一个简单的方法就是这样做:

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

            【讨论】:

              【解决方案9】:

              为什么不使用第一种方式,而是像拆分一样

              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>
              

              【讨论】:

              • 抱歉,此解决方案不起作用。请记住,我想将文本翻译成其他语言。这意味着在某些情况下,“链接”可能位于文本的开头或中间。您的解决方案强制链接位于末尾(翻译不好)。
              猜你喜欢
              • 2017-01-16
              • 1970-01-01
              • 1970-01-01
              • 2016-10-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多