rails i18n - tłumaczenie tekstu z linkami w środku

101

Chciałbym i18n napisać następujący tekst:

Jesteś już zarejestrowany? Zaloguj sie!

Zwróć uwagę, że w tekście znajduje się link. W tym przykładzie wskazuje na Google - w rzeczywistości będzie wskazywać na moją aplikację log_in_path.

Znalazłem dwa sposoby, aby to zrobić, ale żaden z nich nie wygląda „dobrze”.

Pierwszy sposób, jaki znam, obejmuje posiadanie tego mojego en.yml:

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

I moim zdaniem:

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

To działa , ale założenie <a href=...</a>części en.ymlnie wygląda dla mnie zbyt czysto.

Inną opcją, jaką znam, jest użycie zlokalizowanych widoków - login.en.html.erbi login.es.html.erb.

To również nie wydaje się właściwe, ponieważ jedyną inną linią byłaby wspomniana wyżej; reszta widoku (~ 30 linii) zostanie powtórzona dla wszystkich widoków. Nie byłoby bardzo SUCHE.

Myślę, że mógłbym użyć „zlokalizowanych części składowych”, ale to wydaje się zbyt trudne; Myślę, że wolę pierwszą opcję niż tyle małych plików widoku.

Więc moje pytanie brzmi: czy istnieje „właściwy” sposób na wdrożenie tego?

kikito
źródło
A co z tym? stackoverflow.com/questions/12334183/…
Mark Boulder
@Wuggy Foofie Nie powinieneś powielać pytania. A odpowiedź Simone jest lepsza niż ta, którą masz.
kikito,

Odpowiedzi:

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>
Simone Carletti
źródło
66
W Railsach 3 składnia tego została zmieniona na %{href}w ciągu tłumaczenia YAML. Ponadto, ponieważ dane wyjściowe są automatycznie chronione, musisz określić rawlub .html_safejawnie lub dodać sufiks do klucza tłumaczenia z _html, tak jak in, login_message_htmla ucieczka zostanie automatycznie pominięta.
coreyward
15
na wszelki wypadek, jeśli nie jest to oczywiste (i dla tych, którzy są zbyt leniwi, aby sprawdzić dziennik edycji) .. powyższa odpowiedź została już zredagowana tak, aby zawierała komentarz @ coreyward.
opactwo
2
Jeśli masz coś więcej niż jedno słowo w tekście linku, dzielenie tłumaczeń w ten sposób da dziwne tłumaczenia. Na przykład „Mamy niesamowitą <a href='x'> ofertę różnych rzeczy </a>, które możesz kupić. Wysyłasz pokrojone rzeczy do tłumacza i prawdopodobnie otrzymasz dwa zdania, które brzmią jak„ My masz niesamowitą <a href='x'> całą masę przedmiotów </a>, które możesz kupić "w innych językach. Najlepiej znaleźć rozwiązanie, które ich nie rozdzieli.
trcarden
3
@Archonic To nieprawda. t('string')jest identyczny z t("string"). To ta sama rzecz.
meagar
3
Uwielbiam railsy, ​​które komplikują brak linków. powinien wyglądać takt('some.key', link: link_to()).html_safe
Eddie
11

Oddzielenie tekstu i linku w pliku locale.yml działa przez chwilę, ale przy dłuższym tekście są one trudne do przetłumaczenia i utrzymania, ponieważ link znajduje się w oddzielnym elemencie tłumaczenia (jak w odpowiedzi Simones). Jeśli zaczniesz mieć wiele ciągów / tłumaczeń z linkami, możesz je trochę bardziej wysuszyć.

Zrobiłem jeden pomocnik w moim 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

W moim en.yml:

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

A w moich poglądach:

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

W ten sposób łatwiej jest tłumaczyć wiadomości, ponieważ również tekst odsyłacza jest jasno zdefiniowany w plikach locale.yml.

holli
źródło
6
Świetne rozwiązanie. Umieściłem to w Gemie, który pozwala zdefiniować link rzeczy This is a %{link:link to Google}. Pozwala na posiadanie wielu linków w jednym ciągu, obsługuje XSS i umożliwia zagnieżdżone tłumaczenia. Zajrzyj na github.com/iGEL/i18n_link
iGEL.
zrobiłem to z "str = t str", więc po prostu podaję klucz translacji w funkcji. wygodniejszy!
Tim Kretschmer
1
Gdybym mógł, zagłosowałbym więcej za @iGEL. Projekt przeniósł się na github.com/iGEL/it i jeśli chcesz go użyć w kontrolerze do wysyłaniaflash wiadomości w Railsach 3+, zrób to w ten sposóbview_context.it(key, ...)
Chris Beck,
Oto lepszy przykład użycia go w kontrolerze - github.com/iGEL/it/issues/10
Chris Beck,
8

Wziąłem roztwór Hollis i zrobiłem z niego klejnotit . Spójrzmy na przykład:

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

I wtedy

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

Aby uzyskać więcej informacji, zobacz https://github.com/iGEL/it .

iGEL
źródło
5

W en.yml

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

W de.yml

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

w new.html.erb [założono]

<%= 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 %>
Emu
źródło
3

Bardzo dziękuję, Holli, za podzielenie się tym podejściem. Dla mnie działa jak urok. Zagłosowałbym na ciebie, gdybym mógł, ale to jest mój pierwszy post, więc brakuje mi odpowiedniej reputacji ... Jako dodatkowy element układanki: Problem, który zdałem sobie z twoim podejściem, polega na tym, że nadal nie działa od wewnątrz kontroler. Zrobiłem kilka badań i połączyłem twoje podejście z podejściem Glenna na rubypond .

Oto co wymyśliłem:

Wyświetl pomocnika, np. 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

W kontrolerze:

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

W pliku locale.yml:

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

W widoku:

<%= render_flash_messages %>

Mam nadzieję, że dzięki temu postowi zyskam reputację, dzięki której zagłosuję na Ciebie, Holli :) Wszelkie uwagi są mile widziane.

wstyd
źródło
2

Mieliśmy:

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

lub bardziej wyraźnie:

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

następnie w ApplicationController.rb tylko

class ApplicationController < ActionController::Base
  helper I18nHelpers

Biorąc pod uwagę klucz w en.ymlpliku, taki jak

mykey: "Click %|here|!"

może być używany w ERB jako

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

powinien generować

Click <a href="http://foo.com">here</a>!
Jaime Cham
źródło
1

Chciałem trochę więcej elastyczności niż tylko dodawanie linków do wiadomości flash z plików YAML (na przykład nazwa zalogowanego użytkownika itp.), Więc zamiast tego chciałem użyć notacji ERB w ciągu.

Ponieważ używam bootstrap_flash, zmodyfikowałem kod pomocnika w następujący sposób, aby zdekodować ciągi ERB przed wyświetleniem:

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

Następnie można użyć ciągów takich jak poniżej (używając devise):

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

Może to nie działać we wszystkich sytuacjach i może istnieć argument, że definicje kodu i ciągów znaków nie powinny być mieszane (szczególnie z punktu widzenia DRY), ale wydaje mi się, że działa to dobrze. Kod powinien być przystosowany do wielu innych sytuacji, z których ważne są następujące:

require 'erb'

....

        begin
          msg = ERB.new(msg).result(binding) if msg
        rescue Exception=>e
          puts e.message
          puts e.backtrace
        end
zelanix
źródło
-2

Myślę, że prostym sposobem na to jest po prostu wykonanie:

<%= link_to some_path do %>
<%= t '.some_locale_key' %>
<% end %>
Sankalp Singha
źródło
-4

Dlaczego nie skorzystać z pierwszego sposobu, ale jak go podzielić

log_in_message: Already signed up?
log_in_link_text: Log in!

I wtedy

<p> <%= t('log_in_message') %> <%= link_to t('log_in_link_text'), login_path %> </p>
alex.zherdev
źródło
Przepraszamy, to rozwiązanie nie zadziała. Pamiętaj, że chciałem przetłumaczyć tekst na inne języki. Oznacza to, że w niektórych przypadkach „link” może znajdować się na początku lub w środku tekstu. Twoje rozwiązanie wymusza umieszczenie linku na końcu (nie jest dobrze tłumaczone).
kikito