From 2b0a812543bc563589d235c73151df84a66107fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= <15726+Senen@users.noreply.github.com> Date: Wed, 24 May 2023 16:29:05 +0200 Subject: [PATCH] Add method to retrieve translations in one database query This methods performs much better when we need to load a lot of globalized models translations and returns the best fallback translation for the current language. --- app/models/concerns/globalizable.rb | 17 ++++++++ spec/models/concerns/globalizable.rb | 65 +++++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/app/models/concerns/globalizable.rb b/app/models/concerns/globalizable.rb index c2e04554f..0b995350d 100644 --- a/app/models/concerns/globalizable.rb +++ b/app/models/concerns/globalizable.rb @@ -100,5 +100,22 @@ module Globalizable def translation_class_delegate(method) translation_class.instance_eval { delegate method, to: :globalized_model } end + + def with_fallback_translation + translations_foreign_key = reflect_on_association(:translations).foreign_key + fallbacks = Globalize.fallbacks(Globalize.locale) + + fallbacks_with_order = fallbacks.map.with_index do |locale, order| + "('#{locale}', #{order})" + end.join(", ") + + translations_ids = translation_class + .select("DISTINCT ON (#{translations_foreign_key}) id") + .where(locale: fallbacks) + .joins("LEFT JOIN (VALUES #{fallbacks_with_order}) AS locales(name, ordering) ON locale = locales.name") + .order(translations_foreign_key, "locales.ordering") + + with_translations(fallbacks).where("#{translations_table_name}.id": translations_ids) + end end end diff --git a/spec/models/concerns/globalizable.rb b/spec/models/concerns/globalizable.rb index 0eca3b268..fd74d09c1 100644 --- a/spec/models/concerns/globalizable.rb +++ b/spec/models/concerns/globalizable.rb @@ -15,10 +15,7 @@ shared_examples_for "globalizable" do |factory_name| before do record.update!(attribute => "In English") - I18n.with_locale(:es) do - record.update!(required_fields.index_with("En español")) - record.update!(attribute => "En español") - end + add_translation(record, :es, "En español") record.reload end @@ -98,7 +95,7 @@ shared_examples_for "globalizable" do |factory_name| end it "Does not automatically add a translation for the current locale" do - record.translations.find_by(locale: :en).destroy! + destroy_translation(record, :en) record.reload record.update!(translations_attributes: [ @@ -188,4 +185,62 @@ shared_examples_for "globalizable" do |factory_name| expect(record.send(attribute)).to eq "Deutsche Sprache" end end + + describe ".with_fallback_translation" do + before do + fallbacks = { fr: %i[fr en es], es: %i[es fr en], en: %i[en es fr] } + allow(I18n).to receive(:fallbacks).and_return(I18n.fallbacks.merge(fallbacks)) + Globalize.set_fallbacks_to_all_available_locales + end + + it "returns records with the best fallback translation available for the current locale" do + record.update!(attribute => "Content in English") + add_translation(record, :es, "Contenido en español") + add_translation(record, :fr, "Contenu en français") + + I18n.with_locale(:es) do + expect(attribute_with_fallback_translation(record, attribute)).to eq "Contenido en español" + + destroy_translation(record, :es) + + expect(attribute_with_fallback_translation(record, attribute)).to eq "Contenu en français" + + destroy_translation(record, :fr) + + expect(attribute_with_fallback_translation(record, attribute)).to eq "Content in English" + end + + record.reload + + add_translation(record, :es, "Contenido en español") + add_translation(record, :fr, "Contenu en français") + + I18n.with_locale(:fr) do + expect(attribute_with_fallback_translation(record, attribute)).to eq "Contenu en français" + + destroy_translation(record, :fr) + + expect(attribute_with_fallback_translation(record, attribute)).to eq "Content in English" + + destroy_translation(record, :en) + + expect(attribute_with_fallback_translation(record, attribute)).to eq "Contenido en español" + end + end + end +end + +def add_translation(record, locale, text) + I18n.with_locale(locale) do + record.update!(required_fields.index_with(text)) + record.update!(attribute => text) + end +end + +def destroy_translation(record, locale) + record.translations.find_by(locale: locale).destroy! +end + +def attribute_with_fallback_translation(record, attribute) + record.class.with_fallback_translation.where(id: record.id).pick(attribute) end