Je voudrais i18n un texte qui ressemble à ceci:
Déjà inscrit? Connectez-vous!
Notez qu'il y a un lien sur le texte. Sur cet exemple, il pointe vers google - en réalité, il pointera vers log_in_path
.
J'ai trouvé deux façons de procéder, mais aucune ne semble "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 ayant le <a href=...</a>
partie sur le en.yml
ne me semble pas très propre.
L'autre option que je connais utilise vues localisées - login.en.html.erb
, et login.es.html.erb
.
Cela ne semble pas non plus juste, car la seule ligne différente serait celle susmentionnée; 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 lourd; Je pense que je préfère la première option à avoir autant de petits fichiers d'affichage.
Ma question est donc la suivante: existe-t-il une manière "appropriée" de mettre cela en œuvre?
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>
La séparation du texte et du lien dans le fichier locale.yml fonctionne pendant un certain temps, mais avec du texte plus long, il est difficile de les traduire et de les conserver car le lien se trouve dans un élément de traduction distinct (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éé un assistant 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 selon moi:
<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.
J'ai pris la solution de Hollis et en ai fait n bijou appelé it
. Regardons un exemple:
log_in_message: "Already signed up? %{login:Log in!}"
Et alors
<p><%=t_link "log_in_message", :login => login_path %></p>
Pour plus de détails, voir https://github.com/iGEL/it .
Dans en.yml
registration:
terms:
text: "I do agree with the terms and conditions: %{gtc} / %{stc}"
gtc: "GTC"
stc: "STC"
Dans 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 %>
Merci beaucoup, holli, d'avoir partagé cette approche. Cela fonctionne comme un charme pour moi. Je voterais pour vous si je le pouvais, mais c'est mon premier post, donc je n'ai pas la bonne réputation ... Comme pièce supplémentaire au puzzle: Le problème que j'ai réalisé avec votre approche est qu'elle ne fonctionnera toujours pas de l'intérieur le controlle. J'ai fait quelques recherches et combiné votre approche avec celle de Glenn sur rubypond .
Voici ce que j'ai trouvé:
Voir l'aide, par ex. 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 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.
Nous avions 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.yml
fichier 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>!
Je voulais un peu plus de flexibilité que d'ajouter simplement des liens vers des messages flash à partir de fichiers YAML (par exemple, le nom d'utilisateur connecté, etc.), donc je voulais plutôt utiliser la notation ERB dans la chaîne.
Comme j'utilise bootstrap_flash
j'ai donc modifié le code d'aide comme suit pour décoder les chaînes ERB avant d'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("×"), :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 celles-ci (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 dans 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 devrait être adaptable pour 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