I am using devise_invitable gem in rails and I have configured a number of locales in my rails app. What I would like to do is to invite a user with a locale I would specify myself. I t could look something like this:
User.invite!(:email => "test#example.com", :locale => 'fr')
This would send an email with the 'fr' as locale , even when I18n.locale would be en.
Is this possible, even with a complete different syntax than the one I am using above ?
Digging, I found that devise_invitable uses the devise mailer, see source. I'm not 100% sure how I18n.t exactly works, but I suppose you know that. So use alias_method_chain to monkey-patch the translate method. To pass the other language, you can choose one:
modify the whole call stack to pass down the variable
use a pseudo-global variable via Thread.current[].
I propose to use 2., make your choice. Then use that variable to return the correct translation.
Related
I work on Rails 5.2.3 and Ruby 2.5.1. At some point, I found an issue when I expected my array-of-string constant to contain some string but it didn't. Turned out the problem was related to German Umlaute characters (öäü).
So I have the constant defined like the following:
# coding: utf-8
# frozen_string_literal: true
class MyClass
module MyModule
MY_CONSTANT = [
'Breite in mm',
'Höhe in mm',
'Länge in mm'
].map(&:parameterize).freeze
end
end
I expect the constant to look like ["breite-in-mm", "hoehe-in-mm", "laenge-in-mm"]
But instead it's stored as ["breite-in-mm", "hohe-in-mm", "lange-in-mm"]. You see, "ö" has been converted to "o" instead of "oe". Same for "ä". Now it's "a", not "ae".
It works this way on production, in RSpec tests and even when I start Rails console and call this constant. But when I define a new constant from Rails console using the very same code, the strings are being successfully converted to what I expect, i.e. ["breite-in-mm", "hoehe-in-mm", "laenge-in-mm"]
I could easily get rid of this parameterize method and just type in the strings as I need them. Maybe I will have to do that. But I'm really curious about why all this is happening and couldn't find an answer by myself.
So thank you in advance for any ideas.
The parameterize method in Rails (through its use of ActiveSupport::Inflector#transliterate) is in locale aware. It thus uses locale-depending rules to transliterate characters such as Umlauts to ASCII characters.
When your app handles a request (or at least once after booting), you are usually setting a I18n locale, e.g. with I18n.locale = :de for a single request or with I18n.default_locale = :de for your whole app. After that, Rails (resp. the i18n gem) used this locale by default for its transliteration rules.
When initially setting your constant, this default locale was likely not yet set. The i18n gem is thus not aware of the German transliteration rules and uses only the basic Unicode normalization rules.
As a workaround, you can either pass the desired locale to use to the parameterize method as
MY_CONSTANT = [
'Breite in mm',
'Höhe in mm',
'Länge in mm'
].map { |const| const.parameterize(locale: :de).freeze }.freeze
or you can alternatively set the default i18n locale earlier than when your code is executed (e.g. in a file in config/initializers, depending on where exactly you initialize your constant):
I18n.default_locale = :de
Thanks, Holger Just for your great answer. It seems to be correct except it only works for Rails 6.0.0. So I'm going to post the one for Rails 5.2.3 which I'm using on my project.
Unfortunately in Rails 5 parameterize method does not accept the locale argument yet. This will be possible only in Rails 6.
But still, as mentioned in Holger Just's answer, parameterize method relies on transliterate method which does actually use current locale and converts strings according to it.
See Rails 5.2.3 docs and sources for those methods:
https://api.rubyonrails.org/v5.2.3/classes/ActiveSupport/Inflector.html#method-i-parameterize
https://api.rubyonrails.org/v5.2.3/classes/ActiveSupport/Inflector.html#method-i-transliterate
So I cannot pass the locale to parameterize method directly. Then I should set the locale before my constant is defined.
Setting I18n.default_locale = :de inside application.rb file did not help. I already had that and the strings have been transliterated regardless.
What eventually helped was setting I18n.locale = :de manually. Thanks to this, I got my strings parameterized correctly without any changes to MyConstant definition.
I use FastGettext in my Rails application.
I need a particular string translated into every single FastGettext.available_locales in my application.
How to do that?
PS: I couldn't find the solution with I18n either. (see answer below)
It seems to me that you can do something like that with I18n :
I18n.available_locales.each do |loc|
# do something with...
I18n.t(keypath, :locale => loc)
end
I am new to ruby & rails and have started building an application.
My goal is to build this in a way I can easily translate the contents of the rails app and display the website contents in a locale preferred by registered user.
Appreciate any inputs on some of the best practices or references to any documentation to read, to build a web application that can be easily translated?
Thanks,
Krish.
Check out the Rails Internationalization (I18n) API. It does everything you've described.
Also check out Globalize3, it became a standard for model translations. Very useful.
You can use ready_for_i18n plugin that convert your erb to desired form.It saves some time.
You'd definetely watch this talk: http://www.youtube.com/watch?v=CTu4iHWGDyE
Here are some tips or best practices I noted while working through Internationalization of a Rails app. (It's possible that some of them are now outdated).
Before I get into a list here's some clarification between localization and translation that was helpful to me:
Definitions:
App localization and model translations are separate concerns. Figure out which one of those (or both) it is, that you need.
The way I use them here:
App localization: Localization your app to a locale.
Model translation: Translation your model/data into a language.
Example: Give me the french translation (model translation) of the resource in my Spanish site (app localization)
I use Globalize for model translations.
Helpful Tips/ Best Practices:
Some of the following are clearly just helpful tips while working in the Context of Rails + Globalize, some of them might be more than that... possibly best practices.
I18n.locale refers to and sets the locale of the app. When using Globalize, Globalize.locale refers to and sets the locale for model/data translations.
Set both I18n.locale and Globalize.locale on every request. Since these variables are set in Thread, it will avoid some hard-to-replicate bugs.
Set Globalize.locale to I18n.locale right after setting I18n.locale. This allows for model translations to default to the locale of the app. (On my Spanish site, I expect data to be in Spanish, by default).
Change Globalize.locale (and notI18n.locale) to change model translation.
Reset I18n.locale and Globalize.locale after every test. In rspec
RSpec.configure do |config|
config.after(:each) do
I18n.locale = :en
Globalize.locale = :en
end
end
Either use subdomain, or subfolder in the url to refer to the locale of the app.
Use a language parameter to specify the language of the data.
When you work on your rails app, you will probably use default_url_options to use I18n.locale as the default locale parameter for your route / path helpers. However this doesn't work in tests. I picked up a solution from this github issue on rspec-rails.
I monkey patch ActionDispatch::Routing::RouteSet like so:
class ActionDispatch::Routing::RouteSet
def url_for_with_locale_fix(options={})
url_for_without_locale_fix(options.merge(:locale => I18n.locale))
end
alias_method_chain :url_for, :locale_fix
end
Set up a translate helper t as a wrapper around the I18n.t method
module I18nHelper
def t string, options = {}
I18n.t string, options
end
end
RSpec.configure do |config|
config.include I18nHelper
end
This is mini-pattern I use. You can check it out: http://developers-note.blogspot.com/2012/01/rails-i18n-good-practice.html
in my rails3 app, i'm using devise for authentication
now i'm trying to do i18n. for that i saw a devise.en.yml in config/locales, and thought all i got to do is make an devise.ro.yml to translate it in my other language. but if the devise.ro.yml is present, everything is in ro, no matter the language i use
for changing language, i use a locale param in my urls. for that i set up in the application controller something like this:
before_filter :set_locale
def set_locale
I18n.locale=params[:locale]
end
def default_url_options(options={})
{ :locale => I18n.locale }
end
everything in my app is translated ok, except the devise part
am I missing something here?
I can suppose you do in top of you devise.ro.yml ro instead of en ?
First you need to generate Devise views with: rails generate devise:views, then you can translate each of those to suite your locale needs.
devise.en.yml is only for flash messages.
Devise views do not use i18n by default, from now on you can use https://github.com/mcasimir/devise-i18n-views to add I18n support to Devise views and mails.
(see https://github.com/plataformatec/devise/pull/1989)
devise.ro.yml should work.
as a side-note:
I had similar problems, and used this trick to debug / find out where/what Rails is trying to lookup for translations:
http://unixgods.org/~tilo/Rails/which_l10n_strings_is_rails_trying_to_lookup.html
I would like to know if there is a way to insert, alter and remove i18n locale keys programatically (I guess I could use the DB, but I like Rails i18n and want to stay as close to it as possible).
Basically I want to know if there's a way (native, gem, plugin, whatever) to do things like:
I18n.add_locale_key("en", "application.messages.submit_message", "Submit message!")
I18n.add_locale_key("es", "application.messages.submit_message", "Enviar mensaje!")
I18n.remove_locale_key("en", "application.messages.submit_message")
I18n.remove_locale_key("es", "application.messages.submit_message")
As packaged, the Rails I18n API only support defining locale terms via the local .yaml or .rb files. Short of dynamically editing those files at runtime, your best bet is to use the DB functionality of a gem gem like FastGetText.
You could also roll your own solution, of course, but the DB method will likely work for your use case and will result in a smaller time investment.
Here's one way to do it:
>> I18n.backend.store_translations :en, :hello_world => "Hello, world."
=> {:hello_world=>"Hello, world."}
>> I18n.t :hello_world
=> "Hello, world."