Access Translation File (I18N) from Inside Rails Model

Access translation file (i18n) from inside rails model

Call:

I18n.t 

instead of simple t. t is a helper method only available in the views, delegating the whole logic to I18n module.

UPDATE:

As mentioned in the comments, view helper is not only delegating to the I18n module, it makes sure that you can use a default scopes as well.

Rails model using only default locale

Solved by adding the following method in application controller:

  def default_url_options(options={})
{ locale: I18n.locale }
end

Rails I18n: How to add a error translation key which is common for all model base errors?

Simply don't use a relative translation key -

@user.errors.add(:base, t(:common_error) )

If you pass a symbol to errors.add the translation uses the scope of the model.

Should I prevent making I18n translation calls in models

  1. Yes. Translations belong to the view layer and only to the "display" part of it (that is, not to the API, which is still view)
  2. Error messsages should be standard enough so you get to translate them correctly by iterating them in the view and getting the piecewise translations. Unfortunately that's not always possible because of different grammatical order of the sentences in languages. Still it's not useful to move those translations to the model. ActiveRecord's Errors class has hooks for translations just like ActiveModel, so the view can rely on them without injecting any i18n in the model itself.
  3. Select helpers can often take a lambda as a label generator, you can use and abuse view paths and relative i18n keys and whatever you need without having to inject anything in the model. In case of doubt, use a presenter and inject I18n in it.

Rails I18n translations scoping

Okay I was actuall still not happy with this. This default 'lazy lookup' scope is totally krap when you want to translate the same thing at different places. Say I have two different partials that contain information dealing with the same model. Using lazy lookup, I would need to have the same translation twice in my yml file.

Here's a little piece of code that you can put in your application helper. It's basically an override of the default I18n.t that will set the scope to @t_scope when it is defined, and you don't need to worry about the scope anymore

My code addition

helpers/application_helper.rb

def t(*args)
# If there is just one param and we have defined a translation scope in the view
# only using symbols right now, need extended version to handle strings
if args.size == 1 and args.first.is_a?(Symbol) and @t_scope
super(args.shift, @t_scope)
else
super(*args)
end
end

def set_t_scope(scope)
push_t_scope(@t_scope ||= {})
replace_t_scope(scope)
end
alias :t_scope :set_t_scope

def replace_t_scope(scope)
@t_scope = {scope: scope}
end

def push_t_scope(scope)
(@tscope_stack ||= []) << scope
end

def pop_t_scope
@t_scope = @tscope_stack.pop
end

What you can do with it

projects/show.html.erb

<%= t_scope([:projects, :deadlines]) %>
<fieldset>
<legend>Deadlines</legend>
<% if Time.now > @project.deadline.expected_finish_date %>
<p><%= t(:hurry) %></p>
<% else %>
<p><%= t(:you_have_time) %>
</fieldset>
<fieldset>
<legend>Deadlines</legend>
<%= render 'tasks', tasks: @project.tasks %>
...

views/projects/_tasks.html.erb

<%= t_scope([:projects, :tasks]) %>
<% tasks.each do | task| %>
<h2><%= t(:person_in_charge) %></h2>
...
<% pop_t_scope %>

en.yml

en:
projects:
deadlines:
hurry: "Hurry man !"
you_have_time: "Relax, there's still time"
tasks:
person_in_charge: 'The Boss is %{name}'

Now the only problem that I see, is that when rendering multiple partials from a view, the @t_scope will be transferred and could potentiall cause problems. However wouldn't be a problem is @t_scope is set to nil at the beginning of each file

rails i18n - translating text with multiple links inside

Have you tried it like this?

<div class="terms">
<%= t('view.user.account.terms_html',
gtc_link: link_to(t('pages.imprint.gtc'),"/pdf/agb_# {I18n.locale}.pdf", { target: '_blank' }),
data_privacy: link_to(t('view.account.data_privacy'),"https://www.wirecardbank.de/privacy-documents/datenschutzhinweis-fur-die-wirecard-zahlarten/", { target: '_blank' }),
additional_terms: link_to(t('view.account.additional_terms'),"https://www.wirecardbank.de/privacy-documents/datenschutzhinweis-fur-die-wirecard-zahlarten/", { target: '_blank' })
) %>
</div>

How to use I18n from controller in Rails

In controller you use it like this

I18n.t 'controllers.admin.pet.treated'

Using t() directly enables lazy loading:

t(".treated") #loads from key: controllers.admin.pet.treated

Translating model values in the view

It looks like in your view, you're calling I18N.translate(@firm.status) (okay, you're probably just calling t @firm.status, because it looks prettier, right?)

In this case, since the status is "trial", it's the same as calling t "trial", which is why it's looking for a top-level translation "en.trial". If you want to namespace this, the simplest way would be to just call t "activemodel.attributes.firm.status.#{@firm.status}", although this will quickly get annoying and you'll want some helper methods to do this for you. I'm also not convinced that you should have "activemodel.attributes" as part of the scope.

Consider reading up on http://guides.rubyonrails.org/i18n.html, particularly section 4.1.1 about scopes and 3.5 about organizing your locale files.



Related Topics



Leave a reply



Submit