We user I18n gem for translations in our application.
With the translations, it is expected to fallback to default_locale which is :en, if the translation is not available in corresponding locale.
Class Article
...
translates :title
...
end
While accessing as a french user,
article.title => title in english
but
article.attributes(:title) => nil
I guess attributes picks directly from the active_record object (french translation) and since it is not available, it returns nil. Is there a way to make attributes as well fallback to default locale if translation is not available in corresponding locale.
You can use I18n.fallbacks:
I18n.default_locale = :"en-US"
I18n.fallbacks[:fr] # => [:fr, :"en-US", :en]
Take a look how to use Fallbacks on I18n Wiki
Related
I know I can change the ActiveRecord names in a locale file, but is there a way to change them based on context rather than locale (while still leveraging locale files in the application)?
For a simple example (I have multiple circumstances where I need to accomplish this), if I have an address form in a wizard and a user selects a country, how could I change the label/error messages for a :zipcode attribute to display "Zipcode" to those who have selected United States, and "Postcode" to those who have selected United Kingdom?
Edit: What I mean is when a model attribute (country) changes, how to change the human readable attributes for (zipcode) based on the country selection. The users locale won't change (I am already making use of locale files for translations).
Best way to localize is to use I18n, check this post: https://guides.rubyonrails.org/i18n.html#setup-the-rails-application-for-internationalization
Basic I18n
First add locales in you application.rb controller
config.i18n.available_locales = ["en-GB", "en-US"]
config.i18n.default_locale = "en-US"
Then create 2 files en-US.yml and en-GB.yml under config/locales
# en-GB.yml
en-Gb:
zipcode: "PostCode"
# en-US.yml
en-US:
zipcode: "ZipCode"
Then in your controller, you will need to set dictionary that will be used for translation. It is defined with the I18n.locale variable.
# example when locale is passing through params
before_action :set_locale
def set_locale
I18n.locale = I18n.available_locales.include?(params[:locale]) ? params[:locale] : I18n.default_locale
end
And finaly in your views:
<%= t('zipcode') %>
Or if you need in you ruby files:
I18n.t('zipcode')
Localize ActiveRecord Attributes
Same as above, you can create a active_record.en-US.yml under config/locales
# active_record.en-US.yml
en-US:
activerecord:
attributes:
your_model_name:
zipcode: 'ZipCode'
I accepted the other answer as I did end up using locale files as part of my solution.
What I did was a bit hacky, however:
Set locale to {language}-{selected_country} before the necessary request
Have specific overrides in those YAML files where necessary, fall back to standard attributes in base language file whenever possible.
Make use of alias_attributes in the FormObject to minimise the need for the above hack.
I have a model with custom validations. I return the validation
errors to the view as JSON using AJAX.
I need to show the error
messages with the right locale but the messages
show in English (default) for each locale.
When I return I18n.locale
itself instead of the message (for troubleshooting) it shows "en" no matter what is the set locale.
I18n in controllers or views works as expected, this problem
occurs only in the model.
The only configuration that I've made regarding locale is setting the default locale in the application config and setting the locale before each action in the application controller.
Another thing that I've noticed is that when I use root_path without providing the locale, it's using the default locale instead keeping the current locale like it should. I think those issues are connected
Edit:
When I print the locale parameter in controller#create, I get nil. For some reason I don't have the locale parameter at all.
model file:
validate :relevant_date_time
def relevant_date_time
errors.add(:date_error, I18n.t("validations.date_error")) unless date_time_is_relevant?
end
application_controller:
before_action :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
Am I forgetting something?
Information I reviewed:
http://guides.rubyonrails.org/i18n.html
Access translation file (i18n) from inside rails model
Rails 3 translations within models in production
http://www.eq8.eu/blogs/10-translating-locales-for-rails-model-errors
https://lingohub.com/frameworks-file-formats/rails5-i18n-ruby-on-rails/
Change locale at runtime in Rails 3
If you set locale at application_controller.rb, it will only have scope in all controllers and views NOT in models.
So you have to set locale inside model too. Same as you did at controller.
At controller
Model.new(params.merge(locale: I18n.locale))
Inside model
attr_accessible :locale
before_validation() do
I18n.local = locale
end
def relevant_date_time
errors.add(:date_error, I18n.t("validations.date_error")) unless date_time_is_relevant?
end
I18n locale in controllers/views only applies to the action's context, it does not change your app's default locale setting, which is where your model is getting its locale from (as you have discovered).
The step you are missing is to pass the locale into your model from your action, which you could do by adding an attr_accessor :locale to your model, and then passing the locale through when creating/updating it in your controller:
# e.g. app/controllers/users_controller.rb
# user_params = { email: test#mail.com, password: 12345678 }
User.new(user_params.merge(locale: I18n.locale))
Then in your validation method you would be able to access the current locale with locale, so all you need to do is pass it to I18n.t:
errors.add(:date_error, I18n.t("validations.date_error", locale: locale)) unless date_time_is_relevant?
Solved by adding the following method in application controller:
def default_url_options(options={})
{ locale: I18n.locale }
end
For anyone who is struggling with I18n in model validation, the solution could be not including any custom message keys in the models but
en:
activerecord:
errors:
models:
model_name:
attributes:
attr_name:
taken: << or whatever error message like blank, too_long, too_short etc.
Credit t:
https://stackoverflow.com/a/4453350/267693
I am using Ruby on Rails 3.1.1 and I am trying to translate email messages body. I created/stated all necessary "things" (YAML files, key/value pairs, ...) to make the I18n gem to work: email messages are sent without problems using the default language (:en).
Then I added a new language and made all that had to be done to make the I18n gem to work with another language and to get always a locale=de parameter in URLs.
class ApplicationController < ActionController::Base
before_filter :set_locale
def set_locale
if params[:locale] && I18n.available_locales.include?(params[:locale].to_sym)
I18n.locale = params[:locale]
end
end
...
end
However when I sent an email, even if the locale is properly set (eg: locale=de), sent email message are not translated (those still use the default :en language).
How can I make the I18n to translate email messages body?
I read the Localized ActionMailer Templates for Rails blog post
but it is old and I have not tested that...
I read the Rails I18n and emails blog post and I tested that. It works, but how can handle translation in my case (I am using the params method...)? Is there a better solution?
Solution
In your railsproject make a mailer (read http://guides.rubyonrails.org/action_mailer_basics.html how to make one). For example UserMailer.
rails g mailer UserMailer
Define a method for example mail_user.
def mail_user(user)
#user = user
mail(:to => "test example <testuser#testuser.com>", :subject => "hello")
end
Now define views. For example: mail_user.de.html.erb and mail_user.en.html.erb. Put your translations in there. If you want to translate variables seperatly use:
<%= I18n.t("foo.bar") %>
When you do this, ensure you have a en.yml and de.yml translation! Define a translation like the following example:
foo:
bar: hello
You should be ready to go.
How this works
ActionMailer works the following way. You can create mailer models which inherit from ActionMailer::Base. Like ActionController the models have associated views (templates) in the /app/views/ directory.
Now here is the technical part and why this all magicly works. ActionController and ActionMailer default include AbstractController::Rendering directly or indirectly (ActionController::Metal::Rendering). AbstractController::Rendering uses ActionView as default library for its template rendering engine and includes AbstractController::ViewPaths and an instance of I18n proxy to find localized views. To learn more i'd like to refer to the ActionPack source code on github.
To get to the point. ActionView allows you to use localisation in your templates: See Rails guide: Action View Overview , Chapter Localized views.
I have a few tables that contain translations. In order to simplify access to all translations and have them cached, I managed, after the initialization of Rails to read the table and inject their content in the translation store.
#store translations in the I18n store
I18n.available_locales.each do |lang|
storage_hash = {:text => {}, :permalink => {}}
Translation.where(:translatable_type => self.name, :language=> lang).each do |c|
storage_hash[:text][c.translatable_id.to_s] = c.text
storage_hash[:permalink][c.translatable_id.to_s] = c.permalink
end
I18n.backend.store_translations(lang, self.name.downcase => storage_hash)
end
It works great, but in dev, sometimes the translation store is reset and I loose the translations I previously added.
Does it happen in production? Is there a callback I could use to repopulate my translation? Or is there a better way to do what I want?
there is a better way of doing this. it's called I18n backends and you can read about them in the guides: http://guides.rubyonrails.org/i18n.html#using-different-backends
it's also possible to chain backends, so that you can establish fallbacks for your translations. I18n is a pretty mighty library, have a closer look at the docs https://github.com/svenfuchs/rails-i18n
Add your translation in a seed.rb script or only in a config/locale file.
For example:
I18n.t('something')
should output only
something
if the translation missing.
It is possible:
See section 4.1.2 Defaults at Rails Internationalization (I18n) API.
I18n.t :missing, :default => 'Not here'
# => 'Not here'
On rails 4 you can change the exception handler.
Add the following to config/initializers/i18n.rb:
module I18n
class MissingTranslationExceptionHandler < ExceptionHandler
def call(exception, locale, key, options)
if exception.is_a?(MissingTranslation)
key
else
super
end
end
end
end
I18n.exception_handler = I18n::MissingTranslationExceptionHandler.new
Now on you views you can just do:
<p><%= t "Not translated!" %></p>
Guide on the subject: http://guides.rubyonrails.org/i18n.html#using-different-exception-handlers
side-note:
this might help figuring out what Rails thinks the current scope is (e.g. when using ".something")
http://unixgods.org/~tilo/Rails/which_l10n_strings_is_rails_trying_to_lookup.html
this way you can better avoid having missing translations because of incorrect placing of translations strings in the L10n-file / incorrect keys
David's answer is the right solution to the question, another (more verbose) way to do it is to rescue and return the key:
def translate_nicely(key)
begin
I18n.translate!(key)
rescue
key
end
end
nope, not possible. If you use I18 you need to have a file that corresponds to the language otherwise I18n will complain.
Of course you can set the default language in your environment.rb file. Should be near the bottom and you can set this for whatever language you want but in your locales/ folder you will need to have a corresponding yml translation.