Mixed locales in Rails i18n - ruby-on-rails

Rails somehow mixes my locales an I have absolutely no clue why. Most of my translated strings work as expected, but for some it mixes the locales.
Interestingly, this only happens on one of our systems. Specifically running Passenger with Apache.
When using Webrick, Thin, or Passenger Standalone on my development system everything is alright.
This is what I have in my application.rb:
config.i18n.default_locale = :de
This is in application_controller.rb:
before_filter :set_locale
def set_locale
I18n.locale = #current_client ? #current_client.locale : I18n.default_locale
end
(I experience the problems on pages where #current_client is nil and the else part gets executed).
So, I am basically using the :de locale. When showing a validation error on a form, I experience mixed up translations like this:
ist zu kurz (nicht weniger als 6 Zeichen) und translation missing: en.activerecord.errors.custom.password_format
As you can see, the error message from the first failing validation is translated as expected, for the second error message tries to access the English translation (which does not exist).
I suspect a problem with lazy loading of translated strings even before the before_filter gets executed.
Any clues why this might happen?
For the record: This is Rails 3
EDIT:
I just discovered that this depends on the environment used. When using the development environment, everything is fine. When using the production environment (or a production-like) environment, I experience the behavior described above.
EDIT 2:
I found out even more: It specifically depends on config.cache_classes. When set to true, I see the mixed translations. When set to false (as in the typical development environment), i18n works as expected.
EDIT 3:
Maybe this is related to the following bug?
https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/5522
Edit 4:
This IS related to the bug mentioned above, the problem is due to eagerly loaded model classes, which use I18n strings, but eager class loading happens before I18n initialization, hence the translations are not found. There even is another bug about this:
https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6353
Unfortunately, the Rails guys did not manage to include the fix in the recent 3.0.4 release (as far as I can tell). Hence I'm trying to figure out a workaround like this (in my application configuration):
config.before_eager_load do
I18n.load_path += Dir[Rails.root.join('config', 'locales', 'de.yml').to_s]
I18n::Railtie.reloader.paths.concat I18n.load_path
I18n::Railtie.reloader.execute_if_updated
I18n.reload!
end
Unlucky, this does not work. Any clues?

This problem may also occours in case you have a Gem that also uses I18n (I was having this problem with active_admin). Rails sets I18n to late for the Gem to be able to use that same load_paths.
What I have done was to add this to production.rb:
config.before_configuration do
I18n.load_path += Dir[Rails.root.join('config', 'locales', '*.{rb,yml}').to_s]
I18n.locale = 'pt-PT'
I18n.reload!
end

Here is my final workaround, which seems to work (put this in application.rb or one of your environment configuration files, as needed):
# THIS IS A WORKAROUND FOR A I18N BUG IN RAILS!
# Only required when cache_classes is set to true
# See https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6353
config.before_eager_load do
I18n.locale = :de
I18n.load_path += Dir[Rails.root.join('config', 'locales', 'de.yml').to_s]
I18n.reload!
end
Hope this is useful to anybody else...
EDIT:
If this does not work for you, try before_configuration instead of before_eager_load (see solution below). At least, works again as workaround for me in Rails 3.0.10

Have you tried fiddling with the Passenger spawn method settings? Try to set it to Conservative, this way Passenger should behave the same as Thin.

Upgrading to rails 3.0.5 should fix this and similar I18n problems.
See: https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6353

Related

Rails I18n validation deprecation warning

I just updated to rails 4.0.2 and I'm getting this warning:
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
Is there any security issue in setting it to false?
Important: Make sure your app is not using I18n 0.6.8, it has a bug that prevents the configuration to be set correctly.
Short answer
In order to silence the warning edit the application.rb file and include the following line inside the Rails::Application body
config.i18n.enforce_available_locales = true
The possible values are:
false: if you
want to skip the locale validation
don't care about locales
true: if you
want the application to raise an error if an invalid locale is passed (or)
want to default to the new Rails behaviors (or)
care about locale validation
Note:
The old default behavior corresponds to false, not true.
If you are setting the config.i18n.default_locale configuration or other i18n settings, make sure to do it after setting the config.i18n.enforce_available_locales setting.
If your use third party gems that include I18n features, setting the variable through the Application config object, may not have an effect. In this case, set it directly to I18n using I18n.config.enforce_available_locales.
Caveats
Example
require File.expand_path('../boot', __FILE__)
# ...
module YouApplication
class Application < Rails::Application
# ...
config.i18n.enforce_available_locales = true
# or if one of your gem compete for pre-loading, use
I18n.config.enforce_available_locales = true
# ...
end
end
Long answer
The deprecation warning is now displayed both in Rails 4 (>= 4.0.2) and Rails 3.2 (>= 3.2.14). The reason is explained in this commit.
Enforce available locales
When I18n.config.enforce_available_locales is true we'll raise an
I18n::InvalidLocale exception if the passed locale is unavailable.
The default is set to nil which will display a deprecation error.
If set to false we'll skip enforcing available locales altogether (old behaviour).
This has been implemented in the following methods :
I18n.config.default_locale=
I18n.config.locale=
I18n.translate
I18n.localize
I18n.transliterate
Before this change, if you passed an unsupported locale, Rails would silently switch to it if the locale is valid (i.e. if there is a corresponding locale file in the /config/locales folder), otherwise the locale would default to the config.i18n.default_locale configuration (which defaults to :en).
The new version of the I18n gem, forces developers to be a little bit more conscious of the locale management.
In the future, the behavior will change and if a locale is invalid, the Rails app will raise an error.
In preparation of such change (that may potentially break several applications that until today were relying on silent defaults), the warning is forcing you to explicitly declare which validation you want to perform, during the current transition period.
To restore the previous behavior, simply set the following configuration to false
config.i18n.enforce_available_locales = false
otherwise, set it to true to match the new Rails defaults or if you want to be more rigid on domain validation and avoid switching to the default in case of invalid locale.
config.i18n.enforce_available_locales = true
Caveat
If you are setting the config.i18n.default_locale configuration or using any of the previously mentioned methods (default_locale=, locale=, translate, etc), make sure to do it after setting the config.i18n.enforce_available_locales setting. Otherwise, the deprecation warning will keep on popping up. (Thanks Fábio Batista).
If you use third party gems that include I18n features, setting the variable through may not have effect. In fact, the issue is the same as described in the previous point, just a little bit harder to debug.
This issue is a matter of precedence. When you set the config in your Rails app, the value is not immediately assigned to the I18n gem. Rails stores each config in an internal object, loads the dependencies (Railties and third party gems) and then it passes the configuration to the target classes. If you use a gem (or Rails plugin) that calls any of the I18n methods before the config is assigned to I18n, then you'll get the warning.
In this case, you need to skip the Rails stack and set the config immediately to the I18n gem by calling
I18n.config.enforce_available_locales = true
instead of
config.i18n.enforce_available_locales = true
The issue is easy to prove. Try to generate a new empty Rails app and you will see that setting config.i18n in the application.rb works fine.
If in your app it does not, there is an easy way to debug the culprit. Locate the i18n gem in your system, open the i18n.rb file and edit the method enforce_available_locales! to include the statement puts caller.inspect.
This will cause the method to print the stacktrace whenever invoked. You will be able to determine which gem is calling it by inspecting the stacktrace (in my case it was Authlogic).
["/Users/weppos/.rvm/gems/ruby-2.0.0-p247#application/gems/i18n-0.6.9/lib/i18n.rb:150:in `translate'",
"/Users/weppos/.rvm/gems/ruby-2.0.0-p247#application/gems/authlogic-3.1.0/lib/authlogic/i18n/translator.rb:8:in `translate'",
"/Users/weppos/.rvm/gems/ruby-2.0.0-p247#application/gems/authlogic-3.1.0/lib/authlogic/i18n.rb:79:in `translate'",
"/Users/weppos/.rvm/gems/ruby-2.0.0-p247#application/gems/authlogic-3.1.0/lib/authlogic/acts_as_authentic/email.rb:68:in `validates_format_of_email_field_options'",
"/Users/weppos/.rvm/gems/ruby-2.0.0-p247#application/gems/authlogic-3.1.0/lib/authlogic/acts_as_authentic/email.rb:102:in `block in included'",
"/Users/weppos/.rvm/gems/ruby-2.0.0-p247#application/gems/authlogic-3.1.0/lib/authlogic/acts_as_authentic/email.rb:99:in `class_eval'",
"/Users/weppos/.rvm/gems/ruby-2.0.0-p247#application/gems/authlogic-3.1.0/lib/authlogic/acts_as_authentic/email.rb:99:in `included'",
"/Users/weppos/.rvm/gems/ruby-2.0.0-p247#application/gems/authlogic-3.1.0/lib/authlogic/acts_as_authentic/base.rb:37:in `include'",
"/Users/weppos/.rvm/gems/ruby-2.0.0-p247#application/gems/authlogic-3.1.0/lib/authlogic/acts_as_authentic/base.rb:37:in `block in acts_as_authentic'",
"/Users/weppos/.rvm/gems/ruby-2.0.0-p247#application/gems/authlogic-3.1.0/lib/authlogic/acts_as_authentic/base.rb:37:in `each'",
"/Users/weppos/.rvm/gems/ruby-2.0.0-p247#application/gems/authlogic-3.1.0/lib/authlogic/acts_as_authentic/base.rb:37:in `acts_as_authentic'",
"/Users/weppos/Projects/application/app/models/user.rb:8:in `<class:User>'",
"/Users/weppos/Projects/application/app/models/user.rb:1:in `<top (required)>'",
Just for completeness, note that you can also get rid of the warning by setting I18n.enforce_available_locales to true (or false) in config/application.rb:
require File.expand_path('../boot', __FILE__)
.
.
.
module SampleApp
class Application < Rails::Application
.
.
.
I18n.enforce_available_locales = true
.
.
.
end
end
I18n.config.enforce_available_locales = true worked for me in Rails 3.2.16 (I put it in config/application.rb)
Doesn't seem that way - that'd be previous behavior of the way i18n works - new behavior (true) will raise an error when you ask for a locale not implemented/available.
See the commit that added this warning: https://github.com/svenfuchs/i18n/commit/3b6e56e06fd70f6e4507996b017238505e66608c
If you want to care about locales write into appilcation.rb file.
config.i18n.enforce_available_locales = true
You can write false if locale validation and do not care about that.

Devise looking for wrong locale on Rails 4.0.2

I've just updated to Rails 4.0.2 and I got the deprecation warning:
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
To get rid of the warning, I decided to set:
I18n.config.enforce_available_locales = true
in my config/application.rb file.
It worked pretty well on Development, all tests passed, and I was happy... Then I deployed my work in a staging server and it crashed!
Devise is looking for a locale :pt. All I have is a locale :pt-BR. I set it in my application.rb:
config.i18n.default_locale = 'pt-BR'
I don't know why devise is looking for :pt only. My locales in the config folder are: pt-BR.yml and devise.pt-BR.yml.
I tried to change the file name from devise.pt-BR.yml to devise.pt.yml (and inside from pt-BR to pt) and it worked.
I had to go back and set:
I18n.config.enforce_available_locales = false
to make it work.
I have two questions:
1 - Why this is not happening on the tests and on development?
2 - Why Devise is asking for :pt, if I'm setting the default locale as :pt-BR in my config?
Thanks in advance
Add devise-i18n to your Gemfile to get the pt-BR locale file for the Devise strings. This fixes the production problem.
As for why you're not seeing the issue in development, it may be because you're not triggering the specific I18n.t lookup - you need to narrow it down. Some Devise string is triggering this, and I suspect it won't cause an error until that string gets looked up. You need to narrow down the exact set of actions that causes the issue. If it's failing on startup, there might be some production eager loading going on that triggers the issue.

Why does Rails not refresh classes on every request (despite configuration)?

I recently started having to restart my development server every time I change my code. My development.rb file still has this line:
config.cache_classes = false
I tried using the debugger verify that this value has stuck around. To do this I set my configuration to a global variable in environment.rb:
$my_initializer = Rails::Initializer.run do |config|
...
end
then I put a debugger line in one of my controllers so I could do this:
(rdb:2) $my_initializer.configuration.cache_classes
false
So that eliminated the possibility that the value of cache_classes was getting set to true somewhere else. I've tried using both Mongrel and WEBrick and it still happens.
What else might be causing Rails not to reload my code with every request?
I am running:
Mongrel 1.1.5
WEBrick 1.3.1
Rails 2.3.8
Ruby 1.8.7 p253
EDIT:
at #Daemin 's suggestion I checked that the mtime of my files are are actually getting updated when I save them in my text editor (Textmate)
merced:controllers lance$ ls -l people_controller.rb
-rwxr-xr-x 1 lance staff 2153 Act 10 18:01 people_controller.rb
Then I made a change and saved the file:
merced:controllers lance$ ls -l people_controller.rb
-rwxr-xr-x# 1 lance staff 2163 Oct 11 12:03 people_controller.rb
So it's not a problem with the mtimes.
So it turns out that config.threadsafe! overwrites the effect of config.cache_classes = false, even though it doesn't actually overwrite the value of cache_classes (see my question for proof). Digging around a bit more in the Rails source code might illuminate why this might be, but I don't actually need threadsafe behavior in my development environment. Instead, I replaced my call to config.threadsafe! in environment.rb to
config.threadsafe! unless RAILS_ENV == "development"
and everything works fine now.
If anyone else has this problem the solution was the order: config.threadsafe! has to come before config.cache_classes. Reorder it like this to fix it:
...
config.threadsafe!
config.cache_classes = false
...
answer from: Rails: cache_classes => false still caches
I suspect that the classes you are expecting to refresh have been 'required' somewhere in your configuration. Note that Rails' dependency loading happens after Ruby's requires have happened. If a particular module or class has already been required, it will not be handled by Rails' dependency loader, and thus it will not be reloaded. For a detailed explanation, check out this article: http://spacevatican.org/2008/9/28/required-or-not
Despite the fact that the threadsafe! solution works, I also wanted to point out for your benefit and the others that may come in after the following...
If you're editing engine code that is directly in your vendor/engines directory, those files will not be updated without a restart. There may be a configuration option to enable such functionality. However, this is very important to remember if you have used engines to separate large bits of functionality from your application.
My guess would be that it's not reloading the classes for each request because they haven't changed between requests. So the system would note down the last modified time when the classes are loaded, and not reload them until that changed.

application_controller.rb not being loaded

In my Rails app (running on rails 2.3.5, ruby 1.8.7), my application_controller.rb file is not being loaded automatically when config.cache_classes = false in environment.rb.
It's in the load path. If I add require 'application_controller' to the end of my environment.rb or set cache_classes = true, then the app works fine.
Why wouldn't it load when classes are not being cacehed?
This sounds like it for some reason your app is still using 2.3.2 gems for ActiveSupport. It is probably still looking for application.rb, and the undefined pretty_inspect also lends itself to a versioning problem.
First, make sure that you don't have something like this at the top of your environment.rb:
RAILS_GEM_VERSION = '2.3.2'
If you don't, then at the bottom of the your environment.rb find out if something else is setting it wrong by adding this:
puts RAILS_GEM_VERSION
The application code is loaded as part of the Rails::Initializer.run method in environment.rb. It's almost the last step. I know of nothing that would prevent the application controller from loading -- my only suggestion is to make sure there is not a typo in the filename /app/controllers/application_controller.rb and to make sure there is not a typo in the class definition class ApplicationController < ActionController::Base.
I'd like to add that the first part my last comment applies to production mode, where the classes are eager-loaded in Rails::Initializer#load_application_classes, but in development mode it does not cache classes, so loads them as part of a const_missing catcher each request. See ActiveSupport::Dependencies#load_missing_constant.
I have another idea. You mentioned that it is in the load path, but I would confirm later on that it stays in the load path and that a plugin doesn't mess it up or something. At the very bottom of environment.rb (last line) add this line:
puts ActiveSupport::Dependencies.load_paths.pretty_inspect
Then run a script/server from the command line and take a look at the load paths, making sure /path_to_your_rails_app/app/controllers shows up.
The problem is definitely related to config.cache_classes = false; if I switch this to true then the problem disappears.
(Thanks #Ben Lee for leading me towards this)

How does class cacheing work in rails 2.2+

I have a rails application that patches ActiveRecord with a hand-coded validator.
The patch is made by adding the following lines in config/environment.rb
Rails::Initializer.run do |config|
...
end
class ActiveRecord::Base
include MtLib::DBValidations
end
This works fine in production mode i.e. with
config.cache_classes = true
however it does not work in development with cache_classes set to false.
The error thrown is
ArgumentError (A copy of MtLib::DBValidations has been removed from
the module tree but is still active!):
My question is what is the process that is followed when cache_class is set to false. Does Rails re-run any of the initialization methods? If not then where is the best place for me to put my patch to ensure that it is in all models and survives a classes reload?
I have tried adding the patch to config/initializers/active_record_patch, however this is not re-run when the classes are reloaded.
The solution to this, provided by Frederick Cheung on the Ruby On Rails google group add the directory containing the loaded class into the load_once_path array.
I edited environment.rb to look like this
config.load_paths +=
%W( #{RAILS_ROOT}/lib/soap_clients/carefone #{RAILS_ROOT}/lib/mt_lib)
# Make sure load_once_paths is a subset of load_paths
config.load_once_paths += %W( #{RAILS_ROOT}/lib/mt_lib)
And now this works in development mode without having to reload the server on every request

Resources