rails i18n - traduction de texte avec des liens à l'intérieur

101

J'aimerais ajouter un texte qui ressemble à ceci:

Déjà inscrit? S'identifier!

Notez qu'il y a un lien sur le texte. Sur cet exemple, il pointe vers google - en réalité, il pointera vers mon application log_in_path.

J'ai trouvé deux façons de faire cela, mais aucune n'a l'air "correcte".

La première façon que je connais consiste à avoir ceci mon en.yml:

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

Et à mon avis:

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

Cela fonctionne , mais avoir la <a href=...</a>pièce sur le en.ymlne me semble pas très propre.

L'autre option que je connais utilise des vues localisées - login.en.html.erb, et login.es.html.erb.

Cela ne semble pas non plus juste puisque la seule ligne différente serait celle mentionnée ci-dessus; le reste de la vue (~ 30 lignes) serait répété pour toutes les vues. Ce ne serait pas très SEC.

Je suppose que je pourrais utiliser des "partiels localisés" mais cela semble trop compliqué; Je pense que je préfère la première option à avoir autant de petits fichiers de vue.

Ma question est donc la suivante: y a-t-il une manière «appropriée» de mettre en œuvre cela?

kikito
la source
@Wuggy Foofie Vous n'auriez pas dû dupliquer la question. Et la réponse de Simone est meilleure que celles que vous avez.
kikito le

Réponses:

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
la source
66
Dans Rails 3, la syntaxe pour cela a été modifiée %{href}dans la chaîne de traduction YAML. De plus, comme la sortie est automatiquement échappée, vous devez soit spécifier, rawsoit .html_safeexplicitement, soit ajouter un suffixe à votre clé de traduction _html, car dans login_message_htmlet l'échappement sera ignoré automatiquement.
coreyward
15
juste au cas où ce n'est pas évident (et pour ceux qui sont trop paresseux pour vérifier le journal d'édition) .. la réponse ci-dessus a déjà été modifiée pour inclure le commentaire de @ coreyward.
abbood
2
Si vous avez plus d'un mot dans le lien, diviser le texte des traductions comme celle-ci produira des traductions étranges. Par exemple, "Nous avons une <a href='x'> offre incroyable de produits variés </a> que vous pouvez acheter. Vous envoyez le truc haché à un traducteur et vous obtiendrez probablement deux formulations du genre" Nous ayez un <a href='x'> ensemble incroyable d'articles </a> que vous pouvez acheter "dans d'autres langues. Le mieux est de trouver une solution qui ne les sépare pas.
trcarden
3
@Archonic Ce n'est pas vrai. t('string')est identique à t("string"). C'est la même chose.
meagar
3
Je dois aimer les rails compliquant le f hors des liens. devrait ressembler à cecit('some.key', link: link_to()).html_safe
Eddie
11

La séparation du texte et du lien dans le fichier locale.yml fonctionne pendant un certain temps, mais avec du texte plus long, ils sont difficiles à traduire et à maintenir car le lien se trouve dans un élément de traduction séparé (comme dans la réponse de Simones). Si vous commencez à avoir de nombreuses chaînes / traductions avec des liens, vous pouvez les sécher un peu plus.

J'ai créé une aide dans mon 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

Dans mon en.yml:

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

Et à mon avis:

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

De cette façon, il est plus facile de traduire les messages car le texte du lien est également clairement défini dans les fichiers locale.yml.

holli
la source
6
Excellente solution. Je mets cela dans un Gem, qui vous permet de définir le lien des choses This is a %{link:link to Google}. Il vous permet d'avoir plusieurs liens dans une seule chaîne, prend en charge XSS et autorise les traductions imbriquées. Jetez un œil à github.com/iGEL/i18n_link
iGEL
je l'ai fait avec "str = t str" donc je donne juste la clé de traduction dans la fonction. plus confortable!
Tim Kretschmer
1
Je voterais davantage @iGEL si je le pouvais. Le projet a été déplacé sur github.com/iGEL/it et si vous voulez l'utiliser dans un contrôleur pour un flashmessage dans Rails 3+, faites-le comme ceciview_context.it(key, ...)
Chris Beck
Voici un meilleur exemple pour l'utiliser dans un contrôleur - github.com/iGEL/it/issues/10
Chris Beck
8

J'ai pris une solution de hollis et en ai fait une gemme appeléeit . Regardons un exemple:

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

Puis

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

Pour plus de détails, voir https://github.com/iGEL/it .

iGEL
la source
5

Dans en.yml

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

En de.yml

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

dans new.html.erb [supposé]

<%= 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 %>
Émeu
la source
3

Merci beaucoup, holli, d'avoir partagé cette approche. Cela fonctionne comme un charme pour moi. Je vous voterais si je le pouvais, mais c'est mon premier message, donc je n'ai pas la bonne réputation ... En tant que pièce supplémentaire au puzzle: le problème que j'ai réalisé avec votre approche est que cela ne fonctionnera toujours pas de l'intérieur le controlle. J'ai fait quelques recherches et combiné votre approche avec celle de Glenn sur le rubypond .

Voici ce que j'ai trouvé:

View helper, par exemple 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

Dans le contrôleur:

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

Dans le fichier locale.yml:

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

Dans la vue:

<%= render_flash_messages %>

J'espère que ce message me vaut la réputation de vous voter, holli :) Tout commentaire est le bienvenu.

emrass
la source
2

Nous avons eu ce qui suit:

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

ou plus explicitement:

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

puis dans ApplicationController.rb juste

class ApplicationController < ActionController::Base
  helper I18nHelpers

Étant donné une clé dans le en.ymlfichier comme

mykey: "Click %|here|!"

peut être utilisé dans ERB comme

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

devrait générer

Click <a href="http://foo.com">here</a>!
Jaime Cham
la source
1

Je voulais un peu plus de flexibilité que simplement ajouter des liens vers des messages flash à partir de fichiers YAML (par exemple le nom d'utilisateur connecté, etc.) donc à la place, je voulais utiliser la notation ERB dans la chaîne.

Au fur et à bootstrap_flashmesure que j'utilise, j'ai modifié le code d'aide comme suit pour décoder les chaînes ERB avant de les afficher:

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

Il est alors possible d'utiliser des chaînes comme les suivantes (en utilisant devise):

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

Cela peut ne pas fonctionner pour toutes les situations et il peut y avoir un argument selon lequel les définitions de code et de chaîne ne doivent pas être mélangées (en particulier du point de vue DRY), mais cela semble bien fonctionner pour moi. Le code doit être adaptable à de nombreuses autres situations, les bits importants étant les suivants:

require 'erb'

....

        begin
          msg = ERB.new(msg).result(binding) if msg
        rescue Exception=>e
          puts e.message
          puts e.backtrace
        end
zelanix
la source
-2

Je pense qu'un moyen simple de le faire est simplement de faire:

<%= link_to some_path do %>
<%= t '.some_locale_key' %>
<% end %>
Sankalp Singha
la source
-4

Pourquoi ne pas utiliser la première façon, mais la diviser comme

log_in_message: Already signed up?
log_in_link_text: Log in!

Puis

<p> <%= t('log_in_message') %> <%= link_to t('log_in_link_text'), login_path %> </p>
alex.zherdev
la source
Désolé, cette solution ne fonctionnera pas. Gardez à l'esprit que je voulais traduire le texte dans d'autres langues. Cela signifie que dans certaines occasions, le "lien" peut être au début ou au milieu du texte. Votre solution force le lien à être à la fin (ne se traduit pas bien).
kikito