RubyOnRails - Does not keep the selected language when I refresh the page - ruby-on-rails

In development mode all work fine. But not in production mode on heroku.
When I select a locale and I refresh the page, it displays language by default half the time and the language I selected otherwise. I do not know what to do. I tried to clear the cache with Rails.cache.clear command, but it does not work. I guess the problem is with the cache system. I'm new to ruby on rails. Someone would have an idea how to fix this?
to understand my problem, you can go to my website, select the French language and refresh the page several times. Once the page is in French. Another time in English.
https://releaseit.herokuapp.com/
my application_controller:
before_action :set_locale
def set_locale
if params[:locale].in? %W(en fr)
I18n.locale = params[:locale]
end
end
The config files are same as here: https://github.com/starterkits/rails4-starterkit/tree/master/config
Sorry for my english (I am french and i use google translator)

I don't know (or see) where do you pass locale in URL. As far as I know in order to use params[:locale] your URL should look like:
https://releaseit.herokuapp.com/fr/something
https://releaseit.herokuapp.com/en/something
https://releaseit.herokuapp.com/en
https://releaseit.herokuapp.com?locale=fr
http://guides.rubyonrails.org/i18n.html#setting-the-locale-from-the-url-params
What is more try my set_locale method:
def set_locale
if params[:locale]
I18n.locale = params[:locale] || I18n.default_locale
else
I18n.locale = http_accept_language.compatible_language_from(
I18n.available_locales
)
end
end
set_locale method should be executed on every page reload to set proper locale each time.
It requires http_accept_language gem from: https://github.com/iain/http_accept_language
Check out also route_translator gem for creating routes with locale.

Related

I18n.locale getting reset to :en in between controller and the view

I'm trying to internationalize a Rails/Spree app using spree's own spree_i18n gem, but I can't get it to work.
I made a minimal app which recreates the problem here.
To cut a long story short, I have the following code in my ApplicationController:
before_action :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
puts I18n.locale
end
And code in my view which should be translated (<%= t("whatever") %>). But no matter what I do, the text is always output in English.
With some additional code for debugging, I can see that once set_locale is called but while execution is still within the controller, the locale is correct (e.g. if I visit the url /?locale=es, then the puts statement in the above controller code outputs es).
But by the time execution has reached the view, the locale has somehow been reset to en. (E.g. adding <% raise I18n.locale.to_s %> within the view raises "en" as an error message.)
I've opened an issue on Spree's Github because as far as I can tell I've followed their instructions exactly and it's still not working, but I may still be missing something. Why isn't the locale getting set properly?
(Note: I should add that the Spree.t doesn't work either, not just t.)
EDIT: If you look at the comment on my Github issue, you'll see that I got it working. However, I'm 99% sure that my solution is a hack and there's a better method I should be using. Bounty goes to whoever can tell me what I'm dong wrong.
Spree I18n gives a way to set default language: on config/application.rb with
config.i18n.default_locale = :es
And the possibility of setting the languages to be changed. Perhaps on config/initializers/spree_i18n.rb
SpreeI18n::Config.available_locales = [:en, :es, :de]
SpreeI18n::Config.supported_locales = [:en, :es, :de]
After that, you can remove set_locale on ApplicationController, because it has no effect.
With this at place, it works like a charm.
Edited:
I change the message of error because I want to be sure that it works:
<%= product_description(#product) rescue Spree.t(:product_has_no_description) +
' ' + Spree.t(:action) %>
And I add a new product without description. Running the server at localhost
In english I see: "This product has no description Action"
In spanish I see: "Este producto no tiene descripción Acción"
In deutsch I see: "Produkt hat keine Beschreibung Aktion"
Exactly the expected.
You can see the source with changes at github
It is unclear to me how Spree handles localization and in your routes.rb you only mounts engine.
Basically you should start localize your app within routes.rb by adding:
scope "(:locale)", locale: /#{I18n.available_locales.join("|")}/ do
# routing and engines go here
end
Now, you need to keep your params[:locale] across requests, so add to app controller:
def default_url_options(options={})
logger.debug "default_url_options is passed options: #{options.inspect}\n"
{ locale: I18n.locale }
end
Last, detect and set locale for current request, depending on your input data:
before_filter :set_locale
def set_locale
if defined?(params) && params[:locale]
I18n.locale = params[:locale]
elsif current_user && current_user.language_id.present?
I18n.locale = current_user.language.code
elsif defined?(request)
I18n.locale = extract_locale_from_accept_language_header
end
I18n.locale ||= I18n.default_locale
I18n.locale = :en unless valid_languages.include?(I18n.locale.to_sym)
end
You may be using/setting the following in app/controllers/application_controller.rb, which doesn't work:
before_action :set_locale
def set_locale
I18n.locale =
Spree::Frontend::Config[:locale] =
Spree::Backend::Config[:locale] = :LOCALE
end
In core/lib/spree/core/controller_helpers/common.rb there is another before_filter called set_user_language. That filter gets called and re-sets the locale to the value of session[:locale], or if that's not defined it uses default locale.
To solve the issue, set session[:locale] = :LOCALE in your before_filter.

Rails and locales for a beginner

I'm trying to understand how to set locales.
Right now, my application controller has:
def set_locale
I18n.locale = user_signed_in? ? current_user.local.to_sym : :en
end
This works well, but if I want to add a quick switch button on the log in page (which won't have session info because there's no current_user...how can I do this?
You don't have to have a current_user to have a session. Every request that hits your app will have a session object available to it where you can persist data.
Your set_locale can use the locale stored in sessionas the default (falling back to :en) if the user isn't currently logged in:
def set_locale
I18n.locale = user_signed_in? ? current_user.local.to_sym : (session[:locale] || :en)
end
You just need a method which sets the locale based on the user's submitted locale.
You can make a chain that figures out the current locale from many sources with priorities. The sources are (in order):
The current GET variable lang. I like to use it when a user wants to see only the current page in another locale. It neither changes his preference locale nor session locale.
The lang variable in the session which is applied to a whole session. So a user might like to browse multiple pages in the website in a locale different from his preference one, but without having to set the GET lang variable on every request and without having to change the locale in his preferences, and this is where session comes in. It applies only to the current... session :)
The locale in his preferences.
The default locale of the application (I18n.defaul_locale) which defaults to :en if you don't set it manually.
My code looks like:
before_filter :set_locale
def set_locale
if user_signed_in?
I18n.locale = params[:lang] || session[:lang] || current_user.locale || I18n.default_locale
else
I18n.locale = params[:lang] || session[:lang] || I18n.default_locale
end
end

Launch the existing website with different Logo, name and URL

I'm having an existing website. Now I want to launch this same website with a new name, URL & logo in Canada. The difference would be only URL, name and logo. Rest everything (App, DB, layout, server etc.) will remain same.
I developed the website in ruby on rails 3.2. What is the BEST way to get this done ?
if a user comes at www.existing.come will be able to see the existing layout
if a user comes at www.forcanada.com. it will load the same website but different logo n name
You could use the I18n mechanic for it. Do something like the following code in the controller:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_locale
private
def set_locale
case request.host.split('.').last
when 'de'
I18n.locale = :de
when 'com'
I18n.locale = :en
else
I18n.locale = I18n.default_locale
end
end
end
Than use index.de.html.erb or index.en.html.erb for the different views. You can create your own "language" for each site. en and de are just examples.
Have a look at
http://xyzpub.com/en/ruby-on-rails/3.2/i18n_mehrsprachige_rails_applikation.html

Why locale setting in Rails acts as global (when using Thin)?

I just realized that the recommended Rails way to set locale in your controller
before_filter :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
sets the locale globally. The code above works, but I wonder is default_locale really default if you have to type it explicitly?
What I'd expect is to have a locale per request (like we have session per request) and doing something like:
def set_locale
locale = params[:locale] if params[:locale]
end
And having I18n.default_locale used by default otherwise. This would match ideally the optional locale in path:
# config/routes.rb
scope "(:locale)", :locale => /en|nl/ do
resources :books
end
For now if for some reason I skip locale setting in some action it uses the locale set in the previous request which could be from another user!
And isn't there a potential race condition as one request can change global I18n.locale while another request (having set another locale beforehande) is in the middle of rendering?
UPDATE: Some details I found for now, from the I18n documentstion:
Sets the current locale pseudo-globally, i.e. in the Thread.current hash
def locale=(locale)
Now I want to understand if every request is a separate thread.
UPDATE 2: See my answer for explanation.
So now the final answer. TL;DR Setting locale acts as global only when you use threaded web servers, like Thin and Puma.
As I mentioned, I18n.locale=
Sets the current locale pseudo-globally, i.e. in the Thread.current hash
So it is supposed to be per-request, and it works this way in Webrick and Unicorn.
But if you use threaded web server like Thin or Puma, seems that the thread lives longer, and the value is preserved for future requests, until it is changed explicitly. Where I learned it is from the new Steve Klabnik's gem request_store:
If you need global state, you've probably reached for Thread.current.
<...>
So people are using those fancy threaded web servers, like Thin or Puma. But if you use Thread.current, and you use one of those servers, watch out! Values can stick around longer than you'd expect, and this can cause bugs.
Recommended code from above does not set locale globally it sets it by request.
before_filter :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
Code is usually place in BaseController so before each page is render it is triggered and set. There is no race conditions since every page will trigger this code and I18n locale will be calculated there. You can expand this to let's say looks for users locale, than session locale, than request params, than uses English.
def set_locale
I18n.locale = #user.locale || session[:locale] || params[:locale] || :en
end
In other words if you set local on one page let's say in home controller to german and got to dashboard controller you will see default language (english). Since change is not global. That is why code is placed in base controller. Hope it makes sense.

Rails i18n: Translation missing problem, locale not defined

I have a problem with a Rails 2.3.8 application. I'm using rails i18n to make the site in different languages. Everything works perfect, everywhere, except one place.
After successful signup, I do:
flash[:notice] = t 'path.to.locale.key'
Just as I do everywhere else.
But that renders the following:
translation missing: 'locale.path.to.locale.key' not found
It seems it's not loading the current locale (or else it will say 'en', or 'es', or whatever instead of 'locale').
Any idea that could be causing this?
Thanks
Maybe you overwrite it somewhere down that yml file. Maybe you did too many nesting. Maybe that key has subkeys.
Delete everything from that locale.yml and place only that message and see if it works.
The problem you are having happens to me every now and then, and it's always something I messed up in yml file.
Try setting a default locale in your ApplicationController, for example with a before_filter:
I18n.locale = params[:locale] || 'en'
Well, this happened to me in mailer classes after I upgraded to Rails 4.1. It was working correctly on Rails 3 and there was no change on yml files. Somehow i18n did not see the default locale.
So I've added this line on mailer class to fix out.
I18n.locale = I18n.default_locale
class ProviderMailer < ActionMailer::Base
include Resque::Mailer
default from: APP_CONFIG.mailer.from
def registration_email(provider)
#provider = provider
I18n.locale = I18n.default_locale
#provider_url = "#{APP_CONFIG.base_url}/hizmetsgl/#{provider['_id']}"
#howto_url = "#{APP_CONFIG.base_url}/hizmetverenler"
mail(to: provider["business_email"], subject: t('provider_mailer.registration_email.subject'))
end
end

Resources