114 votes

Rails i18n - traduire du texte contenant des liens à l'intérieur

Je voudrais internationaliser un texte qui ressemble à ceci :

Déjà inscrit ? Connectez-vous !

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

J'ai trouvé deux façons de le faire, mais aucune ne semble "juste".

La première façon que je connais implique d'avoir ceci dans mon en.yml :

log_in_message: "Déjà inscrit ? Connectez-vous !"

Et dans ma vue :

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

Cela fonctionne, mais avoir la partie dans le en.yml ne me semble pas très propre.

L'autre option que je connais consiste à utiliser des vues localisées - login.en.html.erb et login.es.html.erb.

Cela ne semble pas non plus correct car 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 DRY.

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

Alors ma question est : y a-t-il un moyen "correct" de mettre en œuvre cela ?

0 votes

Que diriez-vous de ceci ? stackoverflow.com/questions/12334183/…

0 votes

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

199voto

Simone Carletti Points 77653

fr.yml

log_in_message_html: "Il s'agit d'un texte, avec un %{href} à l'intérieur."
log_in_href: "lien"

login.html.erb

 <%= t("log_in_message_html", href: link_to(t("log_in_href"), login_path)) %>

71 votes

Dans Rails 3, la syntaxe a changé pour %{href} dans la chaîne de traduction YAML. De plus, puisque la sortie est automatiquement échappée, vous devez soit spécifier raw ou .html_safe explicitement, soit ajouter le suffixe _html à votre clé de traduction, comme dans login_message_html et l'échappement sera automatiquement ignoré.

20 votes

Juste au cas où ce n'est pas évident (et pour ceux trop paresseux pour vérifier le journal des modifications).. la réponse ci-dessus a déjà été modifiée pour inclure le commentaire de @coreyward.

4 votes

Si vous avez autre chose qu'un seul mot dans le texte du lien, en fractionnant les traductions de cette manière, vous obtiendrez des traductions étranges. Par exemple, "Nous avons une incroyable offre de choses assorties que vous pouvez acheter. Vous envoyez l'objet découpé à un traducteur et vous risquez d'obtenir deux phrases qui se lisent comme "Nous avons une incroyable tas d'articles que vous pouvez acheter" dans d'autres langues. Il est préférable de trouver une solution qui ne les divise pas.

11voto

holli Points 793

Séparer le texte et le lien dans le fichier locale.yml fonctionne pendant un certain temps mais avec un texte plus long, il est difficile de traduire et de maintenir car le lien se trouve dans un élément de traduction séparé (comme dans la réponse de Simone). Si vous commencez à avoir beaucoup de chaînes/traductions avec des liens, vous pouvez le rendre un peu plus sec.

J'ai créé un aide dans mon application_helper.rb :

# Convertit
# "chaîne avec __lien__ au milieu." à
# "chaîne avec #{link_to('lien', link_url, link_options)} au milieu."
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: Aucun emplacement pour __lien__ donné dans #{str}" if Rails.env.test?
    nil
  end
end

Dans mon en.yml :

log_in_message: "Déjà inscrit ? __Connectez-vous !__"

Et dans mes vues :

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

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

6 votes

Superbe solution. J'ai mis cela dans un Gem, qui vous permet de définir des choses comme Ceci est un %{link:lien vers Google}. Cela vous permet d'avoir plusieurs liens dans une seule chaîne, prend soin de XSS et permet des traductions imbriquées. Jetez un œil sur github.com/iGEL/i18n_link

0 votes

J'ai utilisé "str = t str" donc je donne juste la clé de traduction dans la fonction. plus confortable!

1 votes

Je voterai plus pour @iGEL si je pouvais. Le projet a été déplacé github.com/iGEL/it et si vous voulez l'utiliser dans un contrôleur pour un message flash dans Rails 3+, faites-le comme ceci view_context.it(clé, ...)

9voto

iGEL Points 2091

J'ai pris la solution de Hollis et j'ai fait un gem appelé it à partir de cela. Regardons un exemple:

log_in_message: "Déjà inscrit? %{login:Connectez-vous!}"

Et ensuite

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

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

3voto

emrass Points 3394

Merci beaucoup, holli, d'avoir partagé cette approche. Cela fonctionne à merveille pour moi. Je voterais pour vous si je le pouvais, mais c'est mon premier message donc je manque de la réputation nécessaire ... En complément : Le problème que j'ai constaté avec votre approche est qu'elle ne fonctionnera toujours pas depuis l'intérieur du contrôleur. J'ai donc fait des recherches et combiné votre approche avec celle de Glenn sur rubypond.

Voici ce que j'ai trouvé :

Helper de vue, 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

  # Convertit
  # "chaîne avec __lien__ au milieu." en
  # "chaîne avec #{link_to('lien', link_url, link_options)} au milieu."
  # --> voir 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: Aucun emplacement pour __lien__ donné dans #{str}" if Rails.env.test?
      nil
    end
  end

Dans le contrôleur :

flash.now[:alert] = t("chemin.vers.traduction")
flash.now[:alert_link] = ici_vient_le_chemin_du_lien # ou _url

Dans le fichier locale.yml :

chemin:
  vers:
    traduction: "chaîne avec __lien__ au milieu"

Dans la vue :

<%= render_flash_messages %>

J'espère que ce message me permettra d'obtenir la réputation pour vous voter, holli :) Tout retour d'information est le bienvenu.

2voto

Jaime Cham Points 510

Nous avions ce qui suit :

module I18nHelpers
  def translate key, options={}, &block
    s = super key, options  # Traduction par défaut
    if block_given?
      String.new(ERB::Util.html_escape(s)).gsub(/%\|([^\|]*)\|){
        capture($1, &block)  # Passer ce qui se trouve entre les marqueurs
      }.html_safe
    else
      s
    end
  end
  alias :t :translate
end

ou plus explicitement :

module I18nHelpers

  # Permet à un I18n d'inclure le marqueur spécial %|quelquechose|.
  # "quelquechose" sera ensuite transmis au block donné, qui
  # peut générer tout le HTML nécessaire.
  #
  # Les clés Normales et _html sont supportées.
  #
  # Les multiples sont permis
  #
  #     mykey:  "Cliquer %|ici| et %|là|"
  #
  # L'enchaînement devrait aussi fonctionner
  #
  def translate key, options={}, &block

    s = super key, options  # Traduction par défaut

    if block_given?

      # Échapper si ce n'est pas déjà du HTML brut (html_escape n'échappera pas si déjà html_safe)
      s = ERB::Util.html_escape(s)

      # ActiveSupport::SafeBuffer#gsub est cassé, donc convertir en String.
      # Voir https://github.com/rails/rails/issues/1555
      s = String.new(s)

      # Trouver le motif %|| à substituer, puis le remplacer par la capture du block
      s = s.gsub /%\|([^\|]*)\|/ do
        capture($1, &block)  # Passer ce qui se trouve entre les marqueurs
      end

      # Marquer comme html_safe en sortant
      s = s.html_safe
    end

    s
  end
  alias :t :translate

end

puis dans ApplicationController.rb simplement

class ApplicationController < ActionController::Base
  helper I18nHelpers

Etant donné une clé dans le fichier en.yml comme

mykey: "Cliquer %|ici|!"

peut être utilisé en ERB comme

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

devrait générer

Cliquer ici!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X