From 2e6644d513ff835261bfb7cebd41c840c59cb18c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Wed, 17 Oct 2018 01:07:23 +0200 Subject: [PATCH] Fix crash with no translation for default locale When we were visiting a page showing the content of a record which uses globalize and our locale was the default one and there was no translation for the default locale, the application was crashing in some places because there are no fallbacks for the default locale. For example, when visiting a legislation process, the line with `CGI.escape(title)` was crashing because `title` was `nil` for the default locale. We've decided to solve this issue by using any available translations as globalize fallbacks instead of showing a 404 error or a translation missing error because these solutions would (we thinkg) either require modifying many places in the application or making the translatable logic even more complex. Initially we tried to add this solution to an initializer, but it must be called after initializing the application so I18n.fallbacks[locale] gets the value defined in config.i18n.fallbacks. Also note the line: fallbacks[locale] = I18n.fallbacks[locale] + I18n.available_locales Doesn't mention `I18n.default_locale` because the method `I18n.fallbacks[locale]` automatically adds the default locale. --- config/application.rb | 2 + config/initializers/globalize.rb | 6 +++ spec/features/legislation/processes_spec.rb | 8 +++ spec/models/admin_notification_spec.rb | 2 + spec/models/concerns/globalizable.rb | 56 +++++++++++++++++++++ 5 files changed, 74 insertions(+) create mode 100644 spec/models/concerns/globalizable.rb diff --git a/config/application.rb b/config/application.rb index ad39ae88f..77e52538f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -48,6 +48,8 @@ module Consul config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')] config.i18n.load_path += Dir[Rails.root.join('config', 'locales', 'custom', '**', '*.{rb,yml}')] + config.after_initialize { Globalize.set_fallbacks_to_all_available_locales } + config.assets.paths << Rails.root.join("app", "assets", "fonts") # Do not swallow errors in after_commit/after_rollback callbacks. diff --git a/config/initializers/globalize.rb b/config/initializers/globalize.rb index d0836d0cc..d5834f217 100644 --- a/config/initializers/globalize.rb +++ b/config/initializers/globalize.rb @@ -11,3 +11,9 @@ module Globalize end end end + +def Globalize.set_fallbacks_to_all_available_locales + Globalize.fallbacks = I18n.available_locales.each_with_object({}) do |locale, fallbacks| + fallbacks[locale] = (I18n.fallbacks[locale] + I18n.available_locales).uniq + end +end diff --git a/spec/features/legislation/processes_spec.rb b/spec/features/legislation/processes_spec.rb index 9989c5ee8..17c750d81 100644 --- a/spec/features/legislation/processes_spec.rb +++ b/spec/features/legislation/processes_spec.rb @@ -141,6 +141,14 @@ feature 'Legislation' do expect(page).to_not have_content("Additional information") end + + scenario "Shows another translation when the default locale isn't available" do + process = create(:legislation_process, title_fr: "Français") + process.translations.where(locale: :en).first.destroy + + visit legislation_process_path(process) + expect(page).to have_content("Français") + end end context 'debate phase' do diff --git a/spec/models/admin_notification_spec.rb b/spec/models/admin_notification_spec.rb index eeb974e83..daaae0c32 100644 --- a/spec/models/admin_notification_spec.rb +++ b/spec/models/admin_notification_spec.rb @@ -3,6 +3,8 @@ require 'rails_helper' describe AdminNotification do let(:admin_notification) { build(:admin_notification) } + it_behaves_like "globalizable", :admin_notification + it "is valid" do expect(admin_notification).to be_valid end diff --git a/spec/models/concerns/globalizable.rb b/spec/models/concerns/globalizable.rb new file mode 100644 index 000000000..b63734117 --- /dev/null +++ b/spec/models/concerns/globalizable.rb @@ -0,0 +1,56 @@ +require "spec_helper" + +shared_examples_for "globalizable" do |factory_name| + let(:record) { create(factory_name) } + let(:field) { record.translated_attribute_names.first } + + describe "Fallbacks" do + before do + allow(I18n).to receive(:available_locales).and_return(%i[ar de en es fr]) + + record.update_attribute(field, "In English") + + { es: "En español", de: "Deutsch" }.each do |locale, text| + Globalize.with_locale(locale) do + record.translated_attribute_names.each do |attribute| + record.update_attribute(attribute, record.send(attribute)) + end + + record.update_attribute(field, text) + end + end + end + + after do + allow(I18n).to receive(:available_locales).and_call_original + allow(I18n.fallbacks).to receive(:[]).and_call_original + Globalize.set_fallbacks_to_all_available_locales + end + + context "With a defined fallback" do + before do + allow(I18n.fallbacks).to receive(:[]).and_return([:fr, :es]) + Globalize.set_fallbacks_to_all_available_locales + end + + it "Falls back to the defined fallback" do + Globalize.with_locale(:fr) do + expect(record.send(field)).to eq "En español" + end + end + end + + context "Without a defined fallback" do + before do + allow(I18n.fallbacks).to receive(:[]).and_return([:fr]) + Globalize.set_fallbacks_to_all_available_locales + end + + it "Falls back to the first available locale" do + Globalize.with_locale(:fr) do + expect(record.send(field)).to eq "Deutsch" + end + end + end + end +end