With regional locale, `pluralize` doesn't work but `translate` does - ruby-on-rails

I've got this behaviour that I can't explain:
$ rails console
Loading development environment (Rails 4.2.7.1)
irb(main):001:0> I18n.locale
=> :"en-GB"
irb(main):002:0> I18n.available_locales
=> [:en, :"en-GB"]
irb(main):003:0> 'bear'.pluralize
=> "bears"
irb(main):004:0> 'bear'.pluralize(2, :"en-GB")
=> "bear" # <- sadness here
irb(main):005:0> 'bear'.pluralize(2, :en)
=> "bears"
irb(main):006:0> I18n.translate("gst")
=> "VAT" # <- correct translation from 'config/locales/en-GB.yml'
irb(main):007:0> ActiveSupport::Inflector.pluralize('bear', :en)
=> "bears"
irb(main):008:0> ActiveSupport::Inflector.pluralize('bear', :'en-GB')
=> "bear"
My <rails_root>/uk/config/locales/en-GB.yml:
en-GB:
gst: VAT
How come the regionalised locale is available, in use, and works well with translations but not with pluralisation? Just in case, I've put
config.i18n.fallbacks = { :'en-GB' => :en } in my config/application.rb, but without effect. I've got no clue on where I should investigate further...
Any ideas?
Thank you.

Ok, this has been fixed in Rails 5 thanks to this patch

Related

rails console OR irb find out the RAILS_VERSION of the current environment

Somewhat related to:
Determine ruby version from within Rails
How to find out the RAILS_VERSION from within rails console?
Short info in Rails.gem_version:
Rails.gem_version
# => Gem::Version.new("6.0.1")
Long info in Rails::Info:
Rails::Info
=> About your application's environment
Rails version 6.0.1
Ruby version ruby 2....
RubyGems version 2.7...
Rack version 2.0...
Middleware UTF8Cleaner::Middleware, Rack::Cors, ActionDispatch::HostAuthorization, Rack::Sendfile, ActionDispatch::Static, Rack::Lock, ActionDispatch::Executor, ...
Application root /home/aaa/bb/app
Environment development
Database adapter mysql
Database schema version 20195201212345
One of possible ways is:
pry(main)> Rails.version
=> "5.1.7"
or like above was suggested:
pry(main)> Rails.gem_version
=> Gem::Version.new("5.1.7")
Then you can build some conditions using this technique of versions comparisons:
[20] pry(main)> Rails.version.starts_with?('5.1')
=> true
[21] pry(main)> Gem::Version.new(Rails.version) > Gem::Version.new('5.1.2')
=> true
[22] pry(main)> Gem::Version.new(Rails.version) > Gem::Version.new('5.2.3')
=> false
[23] pry(main)> Gem::Version.new(Rails.version) == Gem::Version.new('5.1')
=> false
[24] pry(main)> Gem::Version.new(Rails.version) == Gem::Version.new('5.1.7')
=> true
OR
[28] pry(main)> Rails.gem_version == Gem::Version.new('5.1.7')
=> true
[29] pry(main)> Rails.gem_version >= Gem::Version.new('5.1.2')
=> true
[30] pry(main)> Rails.gem_version < Gem::Version.new('5.2.2')
=> true

Avoid I18n Fallback to default locale

I have the following configured in my application.rb
config.i18n.available_locales = [:at, :de, :ch_de, :ch_fr, :fr, :int_en, :int_fr]
config.i18n.default_locale = :at
My default locale is set to :at (Austria). Which I require for Route Translation. Rails server won't start without it and to be fair it makes sense.
I now created a fallback map, which works just fine:
config.i18n.fallbacks = {'de' => 'at', 'ch_de' => 'at', 'ch_fr' => 'fr', 'int_fr' => 'fr', 'fr' => 'fr', 'int_en' => 'int_en'}
So basically I want all German speaking countries to fallback on :at, whilst all French speaking countries fall back to :fr.
However, I do NOT under any circumstances want :fr to fallback on :at. This is for SEO purposes as some french pages do not have metadata configured. So now the french pages would display the Austrian :at metadata entry. Which is wrong.
If I turn of fallbacks completely:
config.i18n.fallbacks = false
the following works fine in my views:
t('.metatitle', :default => "")
In that case if there is no translation available then nothing is displayed. However, the rest of the site that already exists relies on fallbacks - so this is not an option, considering the effort to implement the change.
Is there a way turn off fallbacks for individual translations?
Or can I implement the fallback map and make sure that the map doesn't fallback to it's default locale if for example no french :fr translation exists?
PS: The route translating gem that requires the default locale is this one here.
Thank you for your help !
Figured it out - and thought of sharing it with you:
If you wish to avoid fallback to the default locale on individual translation you simply have to send a empty fallback array like this:
t('.metatitle', :default => "", :fallback => [])
Et Voila !
This is tricky in Rails up to 6.1 because you need to beat the logic in the Railtie initializer which desperately wants to fallback to the default_locale.
To set the default fallback locale to nil you need to use the following code:
config.i18n.default_locale = "de-AT"
config.i18n.fallbacks.defaults = [[]] # If you just write [], then default_locale is used
config.i18n.fallbacks.map = {
:de => "de-AT",
"de-CH" => "de-AT",
}
Let's check:
$ rails console
2.7.2 :001 > I18n.fallbacks["de"]
=> [:de, :"de-AT"]
2.7.2 :002 > I18n.fallbacks["fr"]
=> [:fr]
2.7.2 :003 > I18n.fallbacks["de-CH"]
=> [:"de-CH", :de, :"de-AT"]
2.7.2 :004 > I18n.fallbacks["de-AT"]
=> [:"de-AT", :de]
2.7.2 :005 >
Not 100% what you want, but there just seems to be no way to prevent the fallbacks to go from country specific locale to generic language locale, when fallbacks are enabled.
Note #1: Your locales are a bit non-standard. AFAIK there is no 'at' locale, but only "de-AT".
Note #2: Some more subtleties and notes in this answer.

Globalize Fallback does not work in production environment

I have the following I18n-Setup
config.i18n.available_locales = [:de, :en]
config.i18n.fallbacks = true
config.i18n.enforce_available_locales = true
config.i18n.default_locale = :de
I have a URL-based locale-switch:
# routes.rb
scope '(:locale)/', locale: /en|de/, defaults: {locale: 'de'} do
resources :programmes
root 'programmes#portal'
end
In my ApplicationController I set the locale as a before_filter
# application_controller.rb
before_filter :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
In my application.rb
config.i18n.available_locales = [:de, :en]
config.i18n.fallbacks = true
config.i18n.enforce_available_locales = true
config.i18n.default_locale = :de
I have a fallback for both of my languages (/app/initializers/globalize.rb):
Globalize.fallbacks = {:en => [:en, :de], :de => [:de, :en]}
My development.rb and production.rb don't differ in terms of I18n setup.
Now I run into the following problem:
I have a record which is only tranlsated in EN
In my development environment using locale = DE the fallback to the english title works
However, in production environment this fallback does not work.
It is not clear to my why this does not work in production environment.
Update: In the production console on my production environment the fallback seems to work as well:
irb(main):005:0* I18n.locale
=> :de
irb(main):006:0> p=Programme.find(1289)
=> #<Programme id: 1289, created_at: "2014-07-08 09:58:21", ....>
irb(main):007:0> p.title
=> "English Title"
irb(main):008:0> I18n.locale = :en
=> :en
irb(main):009:0> p.title
=> "English Title"
irb(main):010:0> I18n.locale = :something-undefined
=> :something-undefined
irb(main):011:0> p.title
=> nil
irb(main):012:0> I18n.default_locale
=> :de
TL;DR
# config/application.rb
require 'i18n/backend/fallbacks'
Answer
I recently had a similar error in development environment.
Here it is my relevant code setup:
# Gemfile
gem 'globalize', '5.0.1'
gem 'rails', '4.2.2'
gem 'rails-i18n', '4.0.4'
and:
# config/application.rb
config.i18n.available_locales = [:it, :en]
config.i18n.enforce_available_locales = true
config.i18n.default_locale = :it
config.i18n.fallbacks = true
With this setup i got these results:
2.2.2 :001 > Product.find_by_name('Fun Farm').translations.inspect
=> "#<ActiveRecord::Associations::CollectionProxy [#<Product::Translation id: 16, product_id: 16, locale: \"it\", created_at: \"2015-06-20 08:33:51\", updated_at: \"2015-06-20 08:33:51\", awards: \"Vincitore della <strong>Ludoteca Ideale</strong>, ...\", spot: \"Il party game per chi ha i riflessi pronti.\", synopsis: \"<p>\\n Gli animali stanno scappando dalla fattoria ...\">]>"
2.2.2 :002 > Product.find_by_name('Fun Farm').spot
=> "Il party game per chi ha i riflessi pronti."
2.2.2 :003 > I18n::locale = :en
=> :en
2.2.2 :004 > Product.find_by_name('Fun Farm').spot
=> nil
Adding:
# config/application.rb
require 'i18n/backend/fallbacks'
it works:
2.2.2 :001 > Product.find_by_name('Fun Farm').translations.inspect
=> "#<ActiveRecord::Associations::CollectionProxy [#<Product::Translation id: 16, product_id: 16, locale: \"it\", created_at: \"2015-06-20 08:33:51\", updated_at: \"2015-06-20 08:33:51\", awards: \"Vincitore della <strong>Ludoteca Ideale</strong>, ...\", spot: \"Il party game per chi ha i riflessi pronti.\", synopsis: \"<p>\\n Gli animali stanno scappando dalla fattoria ...\">]>"
2.2.2 :002 > Product.find_by_name('Fun Farm').spot
=> "Il party game per chi ha i riflessi pronti."
2.2.2 :003 > I18n::locale = :en
=> :en
2.2.2 :004 > Product.find_by_name('Fun Farm').spot
=> "Il party game per chi ha i riflessi pronti."

Rails 3.2 Time ago in words Method returns html content

I wanted to display datetime in words, like "1 hour ago" and i tried the following method but it is returning returning html in the string format as shown below.
method
helper.time_ago_in_words(Time.now)
result
=> "<span class=\"translation_missing\" title=\"translation missing: en.less_than_x_minutes\">Less Than X Minutes</span>"
rails console output:
>> helper.time_ago_in_words(Time.now)
=> "<span class=\"translation_missing\" title=\"translation missing: en.less_than_x_minutes\">Less Than X Minutes</span>"
>> I18n.locale = :de
=> :de
>> helper.time_ago_in_words(Time.now)
=> "<span class=\"translation_missing\" title=\"translation missing: de.less_than_x_minutes\">Less Than X Minutes</span>"
>> I18n.locale = :en
=> :en
>> helper.time_ago_in_words(Time.now)
=> "<span class=\"translation_missing\" title=\"translation missing: en.less_than_x_minutes\">Less Than X Minutes</span>"
Do you use any locales in config? English is for sure the default one and it should work correctly.
2.0.0p0 :001 > helper.time_ago_in_words(Time.now)
=> "less than a minute"
2.0.0p0 :002 > I18n.locale = :de
=> :de
2.0.0p0 :003 > helper.time_ago_in_words(Time.now)
=> "translation missing: de.datetime.distance_in_words.less_than_x_minutes"
2.0.0p0 :004 > I18n.locale = :en
=> :en
2.0.0p0 :005 > helper.time_ago_in_words(Time.now)
=> "less than a minute"
Could you try to invoke this method on helper object, like I did in console (rails c)? What is the output?
In a rails console, if I just use time_ago_in_words from ActionView::Helpers, I'll get the translation_missing error message. But if I include from ActionView::Helpers::DateHelper, it'll work as expected.
works:
[2] pry(main)> include ActionView::Helpers::DateHelper
=> Object
[3] pry(main)> time_ago_in_words(Time.now)
=> "less than a minute"
translation missing:
[1] pry(main)> include ActionView::Helpers
=> Object [2]
pry(main)> time_ago_in_words(Time.now)
=> "<span class=\"translation_missing\" title=\"translation missing: en.less_than_x_minutes\">Less Than X Minutes</span>"
Try this:
time_ago_in_words(Time.now)

How to test for a time span parameter?

I'm struggling with the following issue:
$ rails c
Loading development environment (Rails 3.2.1)
irb(main):001:0> 2.class
=> Fixnum
irb(main):002:0> Fixnum === 2
=> true
irb(main):003:0> 2.hours.class
=> Fixnum
irb(main):004:0> Fixnum === 2.hours
=> false
irb(main):005:0>
I'd like to test whether some specified parameter is a symbol or a time span. The way I thought would be the natural way in Ruby/Rails is:
case param
when :today then
# do this...
when Fixnum then
# do that...
else
raise ArgumentError ...
end
As far as I can tell from ActiveSupport's source code === is not overridden for Fixnum. So, what am I doing wrong?
You can see from the console, that 2.hours can't be Fixnum, because it's #inspect method is overridden. It just pretends to be Fixnum, but it is ActiveSupport::Duration (see the source)
> 2.hours
=> 7200 seconds
> 2.hours.to_i
=> 7200
Better check if it responds to some methods. If it responds to to_i, it is not a symbol.
> :today.respond_to?(:to_i)
=> false
> 2.hours.respond_to?(:to_i)
=> true

Resources