Avoid I18n Fallback to default locale - ruby-on-rails

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.

Related

Why don't my locale settings in number_to_currency work?

Per the Rails 3.2 API Docs, to use different locales for number_to_currency, I need to do the following:
<%= number_to_currency(1234567890.506, :locale => :fr) %>
I was expecting the following output:
# => 1 234 567 890,51 €
Even though I literally use that exact thing within my app and it keeps outputting the following:
$1,234,567,890.51
When I check for the available_locales within my app I get the following:
> I18n.available_locales
=> [:en, :de, :es, :fr, :ja, :pl, :"pt-BR", :ru, :sv, :"zh-CN"]
So it SHOULD work, but it doesn't.
What am I missing?
Update 1
Per #s3tjan's comment, I did some digging in that linked Rails issue and that led me to my application.rb where I discovered I18n.enforce_available_locales = false. I changed that to true and restarted the server.
When I tried the above again, I am now getting this error:
ActionView::Template::Error (:fr is not a valid locale):
Not sure how to fix this.
Update 2
So I just realize that I never had a locale file in my config/locales. What I really want is to use the GBP Pounds for currency, so I added an en-GB.yml file in my config/locales, then I restarted my server and console.
In my application.rb, I have the following:
I18n.enforce_available_locales = true
Then I checked my console and got this:
[1] pry(main)> I18n.available_locales
=> [:en, :de, :es, :fr, :ja, :pl, :"pt-BR", :ru, :sv, :"zh-CN", :"en-GB"]
[2] pry(main)>
So the :"en-GB" was added successfully to my app's load path.
But when I do this in my view:
<%= number_to_currency(1234567890.506, :locale => :"en-GB") %>
This is the error I get:
:"en-GB" is not a valid locale excluded from capture due to environment or should_capture callback
ActionView::Template::Error (:"en-GB" is not a valid locale):
So still not working.
Update 3
My en-GB.yml file was taken directly from https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/en-GB.yml
So it looks exactly like that. Yet I am still getting the same error:
ActionView::Template::Error (:"en-GB" is not a valid locale):
Synopsis:
Remove custom language ymls and add the correct version of the i18n-rails gem. This resolved this special issue.
Original answer:
Ok my guess is that your en-GB.yml is empty. So it actually finds the file and adds the locale in I18n.available_locales BUT this does not include that all translations are available.
When you look at the format of such a yml file you will recognize they all start with
---
language-code
some_keys: ...
This is what actually is loaded into memory and therefore provides all the available translations. Available locale is just defined by found files in config/locales.
When you check the source of number_to_currency It takes the locale from the options and passes it along the key it looks for to I18n.
I18n.translate(:'number.format', :locale => options[:locale], :default => {})
Since you just say that en-GB is available but don't have the actual keys along the locale in memory you get the missing translation issue.
What I suggest is you either use the content of the linked yml file and paste it into your en-GB.yml or you remove your en-GB.yml and find a 3.2 working i18n-rails version and use it. i18n-rails provides plenty of default translations which are utilized all over default rails.
Addition:
Before you added the en-GB.yml file it actually worked like expected.
When no locale is found it defaults to dollar in here since the currency variable will be just and empty {}.
Somehow, I found the solution that worked for me.
First of all, you need to have a locale file with your requirements in it.
Here is the example of fr.yml file
For an instance, copy and paste this file in app/config/locales/
then restart your console,
then try, number_to_currency(1000.51, locale: :fr)
for sure, you will get '1 000,51 €'
Here is the full list of all supported countries' locale file.
Until and unless you don't have locale file with your format required in it, you won't get the desired result.
my rails version is 3.2.22.5, ruby is 2.4.2(I didnt install below 2.x.x)
I use locale yml from
https://github.com/svenfuchs/rails-i18n/blob/rails-3-x/rails/locale/en-GB.yml
This is work well
<%= number_to_currency(1234567890.506, :locale => :"en-GB") %>
<%= number_to_currency(1234567890.506, :locale => "en-GB") %>
to result
£1,234,567,890.51
and I add fr.yml too like
fr:
...
number:
currency:
format:
...
unit: €
And then this is work too
<%= number_to_currency(1234567890.506, :locale => :fr) %>
to result
€1,234,567,890.51
I didn't change or add configuration. I add only controller, view and locale file. And test it.
You could translate directly like, so test this
I18n.translate(:'number.currency.format', :locale => "en-GB", :default => {})
if it occur a same error, then check out you file's name, extension, path.
and you must restart server

RAILS - How to fix I18n NoTranslationAvailable when using ISO3166::Country

I'm using https://rubygems.org/gems/countries in a project and trying to get all countries names using a locale string for translations.
The locale 'DE', 'NO' or others are coming from the Google OCR API. The gem also has a method: ISO3166::Country.translations, which will return all the locale strings and the matching country, like: "AL"=>"Albania".
In this list, there is also featured the translation "NO"=>"Norway", but when using ISO3166::Country.all_translated('NO'), an error is thrown: I18nData::NoTranslationAvailable: I18nData::NoTranslationAvailable -- countries-NO.
Is there any workaround or fix to use all languages and get all translations? Or at least how is Norway not in the locales? The gem I think uses I18n as it's mentioned in the error, and the :no locale is not valid into I18n:
I18n.locale = :no => I18n::InvalidLocale: :no is not a valid locale
I18n.locale = :de => :de
Norwegian i18n locales are :nn (Nynorsk) and :nb (Bokmål), not :no.
i18n uses countries-NN.txt and countries-NB.txt
So need to use ISO3166::Country.all_translated('NN') and ISO3166::Country.all_translated('NB')

How to use English text when corresponding language text doesn't exist

I have text like this
config/locales/en.yml
record_your_voice: 'Record your voice?'
When I'm seeing the page in Japanese local config, the text Record Your Voice is shown - the capitalized left side of code.
How can I use the text "Record your voice?" when corresponding Japanese text failed.
The answer is given here
in your application.rb file, you have the following options :
# rails will fallback to config.i18n.default_locale translation
config.i18n.fallbacks = true
# rails will fallback to en, no matter what is set as config.i18n.default_locale
config.i18n.fallbacks = [:en]
# fallbacks value can also be a hash - a map of fallbacks if you will
# missing translations of es and fr languages will fallback to english
# missing translations in german will fallback to french ('de' => 'fr')
config.i18n.fallbacks = {'es' => 'en', 'fr' => 'en', 'de' => 'fr'}

I18n locale disregarding fallbacks

I asked a previous question regarding locale-setting. I am trying to set up fallbacks of various Norwegian languages to Norwegian Bokmal (:nb). The desired behaviour is that if a browser passes nn or no as locale requests, the I18n.locale will be set to either :nn or :no, and then in the absence of translations for these locales, :nb will be served to the browser.
Based on the answer to my previous question, I have this line in my application initialiser:
config.i18n.default_locale = :en
config.i18n.fallbacks = {:nn => [:nb], :no => [:nb]}
In rails console, this gives me the following results:
> I18n.fallbacks
=> {:en=>[:en]}
> I18n.fallbacks[:nn]
=> [:nn, :nb, :en]
> I18n.fallbacks[:no]
=> [:no, :nb, :en]
Using a browser that has only nn & no in the language list, this does not work- it falls back to the default locale of :en instead. Here's the request headers:
Accept-Language: "nn,no;q=0.5"
If I add :nb to the browser language stack, I am correctly served Norwegian content.
Is there something I am missing in this process?
You need to set I18n.locale based on the browser setting.
def set_locale
I18n.locale = extract_locale_from_accept_language_header
end
private
def extract_locale_from_accept_language_header
request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
end
Taken from: http://guides.rubyonrails.org/i18n.html#setting-the-locale-from-the-client-supplied-information

Full url for an image-path in Rails 3

I have an Image, which contains carrierwave uploads:
Image.find(:first).image.url #=> "/uploads/image/4d90/display_foo.jpg"
In my view, I want to find the absolute url for this. Appending the root_url results in a double /.
root_url + image.url #=> http://localhost:3000//uploads/image/4d90/display_foo.jpg
I cannot use url_for (that I know of), because that either allows passing a path, or a list of options to identify the resource and the :only_path option. Since I do't have a resource that can be identified trough "controller"+"action" I cannot use the :only_path option.
url_for(image.url, :only_path => true) #=> wrong amount of parameters, 2 for 1
What would be the cleanest and best way to create a path into a full url in Rails3?
You can also set CarrierWave's asset_host config setting like this:
# config/initializers/carrierwave.rb
CarrierWave.configure do |config|
config.storage = :file
config.asset_host = ActionController::Base.asset_host
end
This ^ tells CarrierWave to use your app's config.action_controller.asset_host setting, which can be defined in one of your config/envrionments/[environment].rb files. See here for more info.
Or set it explicitly:
config.asset_host = 'http://example.com'
Restart your app, and you're good to go - no helper methods required.
* I'm using Rails 3.2 and CarrierWave 0.7.1
try path method
Image.find(:first).image.path
UPD
request.host + Image.find(:first).image.url
and you can wrap it as a helper to DRY it forever
request.protocol + request.host_with_port + Image.find(:first).image.url
Another simple method to use is URI.parse, in your case would be
require 'uri'
(URI.parse(root_url) + image.url).to_s
and some examples:
1.9.2p320 :001 > require 'uri'
=> true
1.9.2p320 :002 > a = "http://asdf.com/hello"
=> "http://asdf.com/hello"
1.9.2p320 :003 > b = "/world/hello"
=> "/world/hello"
1.9.2p320 :004 > c = "world"
=> "world"
1.9.2p320 :005 > d = "http://asdf.com/ccc/bbb"
=> "http://asdf.com/ccc/bbb"
1.9.2p320 :006 > e = "http://newurl.com"
=> "http://newurl.com"
1.9.2p320 :007 > (URI.parse(a)+b).to_s
=> "http://asdf.com/world/hello"
1.9.2p320 :008 > (URI.parse(a)+c).to_s
=> "http://asdf.com/world"
1.9.2p320 :009 > (URI.parse(a)+d).to_s
=> "http://asdf.com/ccc/bbb"
1.9.2p320 :010 > (URI.parse(a)+e).to_s
=> "http://newurl.com"
Just taking floor's answer and providing the helper:
# Use with the same arguments as image_tag. Returns the same, except including
# a full path in the src URL. Useful for templates that will be rendered into
# emails etc.
def absolute_image_tag(*args)
raw(image_tag(*args).sub /src="(.*?)"/, "src=\"#{request.protocol}#{request.host_with_port}" + '\1"')
end
There's quite a bunch of answers here. However, I didn't like any of them since all of them rely on me to remember to explicitly add the port, protocol etc. I find this to be the most elegant way of doing this:
full_url = URI( root_url )
full_url.path = Image.first.image.url
# Or maybe you want a link to some asset, like I did:
# full_url.path = image_path("whatevar.jpg")
full_url.to_s
And what is the best thing about it is that we can easily change just one thing and no matter what thing that might be you always do it the same way. Say if you wanted to drop the protocol and and use the The Protocol-relative URL, do this before the final conversion to string.
full_url.scheme = nil
Yay, now I have a way of converting my asset image urls to protocol relative urls that I can use on a code snippet that others might want to add on their site and they'll work regardless of the protocol they use on their site (providing that your site supports either protocol).
I used default_url_options, because request is not available in mailer and avoided duplicating hostname in config.action_controller.asset_host if haven't specified it before.
config.asset_host = ActionDispatch::Http::URL.url_for(ActionMailer::Base.default_url_options)
You can't refer to request object in an email, so how about:
def image_url(*args)
raw(image_tag(*args).sub /src="(.*?)"/, "src=\"//#{ActionMailer::Base.default_url_options[:protocol]}#{ActionMailer::Base.default_url_options[:host]}" + '\1"')
end
You can actually easily get this done by
root_url[0..-2] + image.url
I agree it doesn't look too good, but gets the job done.. :)
I found this trick to avoid double slash:
URI.join(root_url, image.url)

Resources