Rails i18n - Übersetzung von Text mit Links im Inneren


100

Ich möchte einen Text schreiben, der so aussieht:

Schon angemeldet? Anmeldung!

Beachten Sie, dass der Text einen Link enthält. In diesem Beispiel zeigt es auf Google - in Wirklichkeit zeigt es auf meine App log_in_path.

Ich habe zwei Möglichkeiten gefunden, aber keine davon sieht "richtig" aus.

Der erste Weg, den ich kenne, besteht darin, Folgendes zu haben en.yml:

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

Und aus meiner Sicht:

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

Das funktioniert , aber das <a href=...</a>Teil auf dem zu haben, en.ymlsieht für mich nicht sehr sauber aus.

Die andere Option, die ich kenne, ist die Verwendung lokalisierter Ansichten - login.en.html.erbund login.es.html.erb.

Dies fühlt sich auch nicht richtig an, da die einzige andere Linie die oben erwähnte wäre; Der Rest der Ansicht (~ 30 Zeilen) wird für alle Ansichten wiederholt. Es wäre nicht sehr trocken.

Ich denke, ich könnte "lokalisierte Teiltöne" verwenden, aber das scheint zu schwerfällig zu sein. Ich glaube, ich bevorzuge die erste Option gegenüber so vielen kleinen Ansichtsdateien.

Meine Frage ist also: Gibt es einen "richtigen" Weg, dies umzusetzen?



@ Wuggy Foofie Du hättest die Frage nicht duplizieren sollen. Und Simones Antwort ist besser als die, die du bekommen hast.
Kikito

Antworten:


177

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
In Rails 3 wurde die Syntax hierfür %{href}in der YAML-Übersetzungszeichenfolge geändert . Da die Ausgabe automatisch maskiert wird, müssen Sie Ihren Übersetzungsschlüssel entweder angeben rawoder .html_safeexplizit angeben oder mit einem Suffix versehen _html, da in login_message_htmlund die Escape-Zeichenfolge automatisch übersprungen wird.
Coreyward

15
Nur für den Fall, dass es nicht offensichtlich ist (und für diejenigen, die zu faul sind, das Bearbeitungsprotokoll zu überprüfen). Die obige Antwort wurde bereits so bearbeitet, dass sie den Kommentar von @ coreyward enthält.
Abbood

2
Wenn der Linktext mehr als ein einzelnes Wort enthält, werden solche Übersetzungen zu seltsamen Übersetzungen führen. Zum Beispiel "Wir haben ein erstaunliches <a href='x'> Angebot an verschiedenen Dingen </a>, das Sie kaufen können. Sie senden das zerhackte Ding an einen Übersetzer, und Sie werden wahrscheinlich zwei Sätze erhalten, die wie" Wir "lauten haben eine erstaunliche <a href='x'> ganze Reihe von Artikeln </a>, die Sie "in anderen Sprachen kaufen können. Am besten finden Sie eine Lösung, die sie nicht
aufteilt

3
@Archonic Das stimmt nicht. t('string')ist identisch mit t("string"). Sie sind das gleiche.
Meagar

3
Ich muss Schienen lieben, die das F-out von Links erschweren. sollte so aussehent('some.key', link: link_to()).html_safe
Eddie

11

Das Trennen von Text und Link in der Datei locale.yml funktioniert eine Weile, aber bei längerem Text sind diese schwer zu übersetzen und zu pflegen, da sich der Link in einem separaten Übersetzungselement befindet (wie in der Antwort von Simones). Wenn Sie anfangen, viele Zeichenfolgen / Übersetzungen mit Links zu haben, können Sie diese etwas mehr trocknen.

Ich habe einen Helfer in meiner application_helper.rb erstellt:

# 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

In meinem en.yml:

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

Und meiner Meinung nach:

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

Auf diese Weise ist es einfacher, Nachrichten zu übersetzen, da auch der Linktext in den locale.yml-Dateien klar definiert ist.


6
Tolle Lösung. Ich habe dies in einen Edelstein eingefügt, mit dem Sie die Verknüpfung von Dingen definieren können This is a %{link:link to Google}. Es ermöglicht Ihnen, mehrere Links in einer einzigen Zeichenfolge zu haben, kümmert sich um XSS und ermöglicht verschachtelte Übersetzungen.
Werfen

Ich habe es mit "str = t str" gemacht, also gebe ich einfach den Übersetzungsschlüssel in der Funktion. bequemer!
Tim Kretschmer

1
Ich würde @iGEL mehr positiv bewerten, wenn ich könnte. Das Projekt wurde github.com/iGEL/it verschoben und wenn Sie es in einem Controller für eine flashNachricht in Rails 3+ verwenden möchten, machen Sie es soview_context.it(key, ...)
Chris Beck

Hier ist ein besseres Beispiel für die Verwendung in einem Controller - github.com/iGEL/it/issues/10
Chris Beck

8

Ich nahm Hollis-Lösung und machte daraus einen Edelsteinit . Schauen wir uns ein Beispiel an:

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

Und dann

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

Weitere Informationen finden Sie unter https://github.com/iGEL/it .


5

In en.yml

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

In de.yml

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

in new.html.erb [angenommen]

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

Vielen Dank, Holli, dass Sie diesen Ansatz geteilt haben. Es funktioniert wie ein Zauber für mich. Ich würde dich abstimmen, wenn ich könnte, aber dies ist mein erster Beitrag, daher fehlt mir der richtige Ruf ... Als zusätzliches Teil des Puzzles: Das Problem, das ich bei deinem Ansatz erkannt habe, ist, dass es von innen immer noch nicht funktioniert Der Controller. Ich habe einige Nachforschungen angestellt und Ihren Ansatz mit dem von Glenn am Rubypond kombiniert .

Folgendes habe ich mir ausgedacht:

Helfer anzeigen, z. B. 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

In der Steuerung:

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

In der locale.yml:

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

In der Ansicht:

<%= render_flash_messages %>

Hoffe, dieser Beitrag bringt mir den Ruf ein, dich abzustimmen, holli :) Jedes Feedback ist willkommen.


2

Wir hatten folgendes:

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

oder expliziter:

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

dann in ApplicationController.rb nur

class ApplicationController < ActionController::Base
  helper I18nHelpers

Gegeben ein Schlüssel in der en.ymlDatei wie

mykey: "Click %|here|!"

kann in ERB als verwendet werden

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

sollte erzeugen

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

1

Ich wollte ein bisschen mehr Flexibilität als nur das Hinzufügen von Links zu Flash-Nachrichten aus YAML-Dateien (zum Beispiel dem angemeldeten Benutzernamen usw.), also wollte ich stattdessen die ERB-Notation in der Zeichenfolge verwenden.

Während ich dies verwende bootstrap_flash, habe ich den Hilfecode wie folgt geändert, um die ERB-Zeichenfolgen vor der Anzeige zu dekodieren:

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

Es ist dann möglich, Zeichenfolgen wie die folgenden zu verwenden (mit devise):

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

Dies funktioniert möglicherweise nicht in allen Situationen und es gibt möglicherweise ein Argument dafür, dass Code- und Zeichenfolgendefinitionen nicht gemischt werden sollten (insbesondere aus DRY-Sicht), aber dies scheint für mich gut zu funktionieren. Der Code sollte für viele andere Situationen anpassbar sein, wobei die folgenden wichtigen Bits sind:

require 'erb'

....

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

-2

Ich denke, ein einfacher Weg, dies zu tun, besteht darin, einfach Folgendes zu tun:

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

-4

Warum nicht den ersten Weg benutzen, sondern ihn wie aufteilen?

log_in_message: Already signed up?
log_in_link_text: Log in!

Und dann

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

Entschuldigung, diese Lösung wird nicht funktionieren. Denken Sie daran, dass ich den Text in andere Sprachen übersetzen wollte. Dies bedeutet, dass sich der "Link" in einigen Fällen am Anfang oder in der Mitte des Textes befinden kann. Ihre Lösung erzwingt, dass der Link am Ende steht (nicht gut übersetzt).
Kikito
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.