rails i18n - traduzione di testo con collegamenti all'interno


101

Mi piacerebbe i18n un testo che assomiglia a questo:

Già registrato? Accesso!

Nota che c'è un collegamento sul testo. In questo esempio punta a google - in realtà punterà a quella della mia app log_in_path.

Ho trovato due modi per farlo, ma nessuno di loro sembra "giusto".

Il primo modo che conosco implica avere questo mio en.yml:

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

E a mio avviso:

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

Questo funziona , ma avendo la <a href=...</a>parte sul en.ymlnon sembra molto pulito per me.

L'altra opzione che conosco sta usando viste localizzate - login.en.html.erbe login.es.html.erb.

Anche questo non sembra giusto poiché l'unica linea diversa sarebbe quella di cui sopra; il resto della visualizzazione (~ 30 righe) sarebbe ripetuto per tutte le visualizzazioni. Non sarebbe molto SECCO.

Immagino di poter usare "parziali localizzati" ma sembra troppo sbrigativo; Penso di preferire la prima opzione ad avere così tanti piccoli file di visualizzazione.

Quindi la mia domanda è: esiste un modo "corretto" per implementarlo?



@ Wuggy Foofie Non avresti dovuto duplicare la domanda. E la risposta di Simone è migliore di quelle che hai ricevuto.
kikito

Risposte:


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
In Rails 3 la sintassi per questo è cambiata %{href}nella stringa di traduzione YAML. Inoltre, poiché l'output viene automaticamente sottoposto a escape, è necessario specificare rawo .html_safeesplicitamente o aggiungere un suffisso alla chiave di traduzione _html, come in login_message_htmle l'escaping verrà ignorato automaticamente.
coreyward

15
nel caso in cui non fosse ovvio (e per quelli troppo pigri per controllare il registro delle modifiche) .. la risposta sopra era già stata modificata per includere il commento di @ coreyward.
abbood

2
Se hai qualcosa di più di una singola parola nel link, la divisione del testo di traduzioni come questa produrrà traduzioni strane. Ad esempio "Abbiamo una straordinaria <a href='x'> offerta di articoli assortiti </a> che puoi acquistare. Invia la cosa tagliata a un traduttore e probabilmente riceverai due frasi che recitano come" Noi avere un fantastico <a href='x'> intero gruppo di articoli </a> che puoi acquistare "in altre lingue. Meglio trovare una soluzione che non li divida.
trcarden

3
@ Archonic Non è vero. t('string')è identico a t("string"). Sono la stessa cosa.
meagar

3
Devo amare i binari che complicano la fuoriuscita dei collegamenti. dovrebbe assomigliare a questot('some.key', link: link_to()).html_safe
Eddie

11

Separare testo e collegamento nel file locale.yml funziona per un po ', ma con testo più lungo sono difficili da tradurre e mantenere poiché il collegamento si trova in un elemento di traduzione separato (come nella risposta di Simones). Se inizi ad avere molte stringhe / traduzioni con link puoi asciugarlo un po 'di più.

Ho creato un aiuto nel mio 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

Nel mio en.yml:

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

E secondo me:

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

In questo modo è più facile tradurre i messaggi poiché anche il testo del collegamento è chiaramente definito nei file locale.yml.


6
Ottima soluzione. Lo metto in una gemma, che ti consente di definire il collegamento delle cose This is a %{link:link to Google}. Ti consente di avere più collegamenti in una singola stringa, si prende cura di XSS e consente traduzioni annidate.
Dai

l'ho fatto con "str = t str" quindi ho solo dato la chiave di traduzione nella funzione. più comodo!
Tim Kretschmer

1
Voterei di più @iGEL se potessi. Il progetto è stato spostato su github.com/iGEL/it e se vuoi usarlo in un controller per un flashmessaggio in Rails 3+ fallo in questo modoview_context.it(key, ...)
Chris Beck

Ecco un esempio migliore per utilizzarlo in un controller: github.com/iGEL/it/issues/10
Chris Beck

8

Ho preso la soluzione di hollis e ne ho fatto una gemma chiamatait . Diamo un'occhiata a un esempio:

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

E poi

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

Per maggiori dettagli, vedere 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 [assunto]

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

Grazie mille, Holli, per aver condiviso questo approccio. Funziona come un fascino per me. Ti voterei se potessi, ma questo è il mio primo post quindi mi manca la giusta reputazione ... Come ulteriore tassello del puzzle: il problema che ho capito con il tuo approccio è che non funzionerà ancora dall'interno il controller. Ho fatto delle ricerche e ho combinato il tuo approccio con quello di Glenn su Rubypond .

Ecco cosa mi è venuto in mente:

Visualizza helper, ad esempio 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

Nel controller:

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

Nel locale.yml:

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

Nella vista:

<%= render_flash_messages %>

Spero che questo post mi faccia guadagnare la reputazione di votarti, holli :) Qualsiasi feedback è il benvenuto.


2

Abbiamo avuto quanto segue:

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

o più esplicitamente:

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

quindi in ApplicationController.rb solo

class ApplicationController < ActionController::Base
  helper I18nHelpers

Data una chiave nel en.ymlfile come

mykey: "Click %|here|!"

può essere utilizzato in ERB come

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

dovrebbe generare

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

1

Volevo un po 'più di flessibilità rispetto alla semplice aggiunta di collegamenti ai messaggi flash da file YAML (ad esempio il nome utente registrato, ecc.) Così invece volevo utilizzare la notazione ERB nella stringa.

Mentre sto usando bootstrap_flash, ho modificato il codice helper come segue per decodificare le stringhe ERB prima di visualizzare:

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

È quindi possibile utilizzare stringhe come le seguenti (utilizzando devise):

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

Questo potrebbe non funzionare per tutte le situazioni e potrebbe esserci un argomento secondo cui le definizioni di codice e stringa non dovrebbero essere mescolate (specialmente da una prospettiva DRY), ma questo sembra funzionare bene per me. Il codice dovrebbe essere adattabile per molte altre situazioni, i bit importanti sono i seguenti:

require 'erb'

....

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

-2

Penso che un modo semplice per farlo sia semplicemente facendo:

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

-4

Perché non usare il primo modo, ma dividendolo come

log_in_message: Already signed up?
log_in_link_text: Log in!

E poi

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

Spiacenti, questa soluzione non funzionerà. Tieni presente che volevo tradurre il testo in altre lingue. Ciò significa che in alcune occasioni il "collegamento" potrebbe essere all'inizio o al centro del testo. La tua soluzione impone che il collegamento sia alla fine (non si traduce bene).
kikito
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.