From 6de4737b7059e2620d727e2b8ce2e017bb405659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 4 Mar 2024 20:03:25 +0100 Subject: [PATCH] Allow different default locales per tenant Note that, for everything to work consistently, we need to make sure that the default locale is one of the available locales. Also note that we aren't overwriting the `#save ` method set by globalize. I didn't feel too comfortable changing a monkey-patch which ideally shouldn't be there in the first place, I haven't found a case where `Globalize.locale` is `nil` (since it defaults to `I18n.locale`, which should never be `nil`), so using `I18n.default_locale` probably doesn't affect us. --- app/controllers/application_controller.rb | 2 +- app/controllers/management/base_controller.rb | 2 +- app/lib/search_dictionary_selector.rb | 2 +- app/mailers/mailer.rb | 2 +- app/models/concerns/globalizable.rb | 2 +- app/models/setting.rb | 12 ++++- app/models/user.rb | 2 +- db/dev_seeds.rb | 2 +- .../form_content_block_component_spec.rb | 1 + ...rm_heading_content_block_component_spec.rb | 1 + .../layout/locale_switcher_component_spec.rb | 1 + .../application_controller_spec.rb | 4 +- .../management/base_controller_spec.rb | 4 +- .../subscriptions_controller_spec.rb | 5 +- spec/lib/search_dictionary_selector_spec.rb | 18 +++---- spec/mailers/mailer_spec.rb | 10 ++++ spec/models/budget/content_block_spec.rb | 1 + spec/models/globalizable_spec.rb | 47 +++++++++++++++++ spec/models/i18n_content_spec.rb | 1 + spec/models/setting_spec.rb | 52 +++++++++++++++++-- .../site_customization/content_block_spec.rb | 1 + spec/models/user_spec.rb | 10 ++++ 22 files changed, 156 insertions(+), 26 deletions(-) create mode 100644 spec/models/globalizable_spec.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 28493bd02..6a70a9d5e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -56,7 +56,7 @@ class ApplicationController < ActionController::Base elsif Setting.enabled_locales.include?(session[:locale]&.to_sym) session[:locale] else - I18n.default_locale + Setting.default_locale end end diff --git a/app/controllers/management/base_controller.rb b/app/controllers/management/base_controller.rb index 2307d5ea3..745688743 100644 --- a/app/controllers/management/base_controller.rb +++ b/app/controllers/management/base_controller.rb @@ -44,7 +44,7 @@ class Management::BaseController < ActionController::Base session[:locale] = params[:locale].to_s end - session[:locale] ||= I18n.default_locale.to_s + session[:locale] ||= Setting.default_locale.to_s I18n.with_locale(session[:locale], &action) end diff --git a/app/lib/search_dictionary_selector.rb b/app/lib/search_dictionary_selector.rb index cc6168317..358204489 100644 --- a/app/lib/search_dictionary_selector.rb +++ b/app/lib/search_dictionary_selector.rb @@ -37,7 +37,7 @@ module SearchDictionarySelector private def find_from_i18n_default - key_to_lookup = I18n.default_locale.to_s.split("-").first.to_sym + key_to_lookup = Setting.default_locale.to_s.split("-").first.to_sym dictionary = I18N_TO_DICTIONARY[key_to_lookup] dictionary ||= "simple" diff --git a/app/mailers/mailer.rb b/app/mailers/mailer.rb index 8398bbf6b..7fcc3d98c 100644 --- a/app/mailers/mailer.rb +++ b/app/mailers/mailer.rb @@ -76,7 +76,7 @@ class Mailer < ApplicationMailer def user_invite(email) @email_to = email - I18n.with_locale(I18n.default_locale) do + I18n.with_locale(Setting.default_locale) do mail(to: @email_to, subject: t("mailers.user_invite.subject", org_name: Setting["org_name"])) end end diff --git a/app/models/concerns/globalizable.rb b/app/models/concerns/globalizable.rb index d4bb45edb..5578c37db 100644 --- a/app/models/concerns/globalizable.rb +++ b/app/models/concerns/globalizable.rb @@ -82,7 +82,7 @@ module Globalizable translation_class.instance_eval do validates method, length: options[:length], - if: lambda { |translation| translation.locale == I18n.default_locale } + if: lambda { |translation| translation.locale == Setting.default_locale } end if options.count > 1 translation_class.instance_eval do diff --git a/app/models/setting.rb b/app/models/setting.rb index 0dee92c84..7a69b569d 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -96,6 +96,7 @@ class Setting < ApplicationRecord # Code to be included at the top (inside ) of every page (useful for tracking) "html.per_page_code_head": "", "locales.enabled": nil, + "locales.default": nil, "map.latitude": 51.48, "map.longitude": 0.0, "map.zoom": 10, @@ -224,7 +225,16 @@ class Setting < ApplicationRecord def enabled_locales locales = Setting["locales.enabled"].to_s.split.map(&:to_sym) - (locales & I18n.available_locales).presence || I18n.available_locales + [ + default_locale, + *((locales & I18n.available_locales).presence || I18n.available_locales) + ].uniq + end + + def default_locale + locale = Setting["locales.default"].to_s.strip.to_sym + + ([locale] & I18n.available_locales).first || I18n.default_locale end end end diff --git a/app/models/user.rb b/app/models/user.rb index d13c27b2f..2fbfd9fed 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -339,7 +339,7 @@ class User < ApplicationRecord end def locale - self[:locale] || I18n.default_locale.to_s + self[:locale] || Setting.default_locale.to_s end def confirmation_required? diff --git a/db/dev_seeds.rb b/db/dev_seeds.rb index 5c67ac52f..4623cdac1 100644 --- a/db/dev_seeds.rb +++ b/db/dev_seeds.rb @@ -24,7 +24,7 @@ end def random_locales [ - I18n.default_locale, + Setting.default_locale, *(Setting.enabled_locales & %i[en es]), *Setting.enabled_locales.sample(4) ].uniq.take(5) diff --git a/spec/components/admin/site_customization/content_blocks/form_content_block_component_spec.rb b/spec/components/admin/site_customization/content_blocks/form_content_block_component_spec.rb index 6e44168b6..f025bd3a5 100644 --- a/spec/components/admin/site_customization/content_blocks/form_content_block_component_spec.rb +++ b/spec/components/admin/site_customization/content_blocks/form_content_block_component_spec.rb @@ -8,6 +8,7 @@ describe Admin::SiteCustomization::ContentBlocks::FormContentBlockComponent do end it "only includes enabled settings" do + Setting["locales.default"] = "de" Setting["locales.enabled"] = "de fr" render_inline component diff --git a/spec/components/admin/site_customization/content_blocks/form_heading_content_block_component_spec.rb b/spec/components/admin/site_customization/content_blocks/form_heading_content_block_component_spec.rb index b04c8a64d..09fe4469d 100644 --- a/spec/components/admin/site_customization/content_blocks/form_heading_content_block_component_spec.rb +++ b/spec/components/admin/site_customization/content_blocks/form_heading_content_block_component_spec.rb @@ -8,6 +8,7 @@ describe Admin::SiteCustomization::ContentBlocks::FormHeadingContentBlockCompone end it "only includes enabled settings" do + Setting["locales.default"] = "de" Setting["locales.enabled"] = "de fr" render_inline component diff --git a/spec/components/layout/locale_switcher_component_spec.rb b/spec/components/layout/locale_switcher_component_spec.rb index f043f3550..8678ef566 100644 --- a/spec/components/layout/locale_switcher_component_spec.rb +++ b/spec/components/layout/locale_switcher_component_spec.rb @@ -101,6 +101,7 @@ describe Layout::LocaleSwitcherComponent do context "when not all available locales are enabled" do before do allow(I18n).to receive(:available_locales).and_return(%i[en es fr]) + Setting["locales.default"] = "es" Setting["locales.enabled"] = "es fr" end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index f9bc09b65..921a3256e 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -23,9 +23,11 @@ describe ApplicationController do describe "#switch_locale" do it "uses the default locale by default" do + Setting["locales.default"] = "pt-BR" + get :index - expect(response.body).to eq "en" + expect(response.body).to eq "pt-BR" end it "uses the locale in the parameters when it's there" do diff --git a/spec/controllers/management/base_controller_spec.rb b/spec/controllers/management/base_controller_spec.rb index 9abe44fbb..35385f73d 100644 --- a/spec/controllers/management/base_controller_spec.rb +++ b/spec/controllers/management/base_controller_spec.rb @@ -34,9 +34,11 @@ describe Management::BaseController do describe "#switch_locale" do it "uses the default locale by default" do + Setting["locales.default"] = "pt-BR" + get :index - expect(response.body).to eq "en" + expect(response.body).to eq "pt-BR" end it "uses the locale in the parameters when it's there" do diff --git a/spec/controllers/subscriptions_controller_spec.rb b/spec/controllers/subscriptions_controller_spec.rb index e675136de..11a96fd8d 100644 --- a/spec/controllers/subscriptions_controller_spec.rb +++ b/spec/controllers/subscriptions_controller_spec.rb @@ -29,13 +29,14 @@ describe SubscriptionsController do end it "only accepts enabled locales" do - Setting["locales.enabled"] = "en nl" + Setting["locales.default"] = "fr" + Setting["locales.enabled"] = "fr nl" create(:user, locale: "es", subscriptions_token: "mytoken") get :edit, params: { token: "mytoken" } - expect(session[:locale]).to eq "en" + expect(session[:locale]).to eq "fr" end end end diff --git a/spec/lib/search_dictionary_selector_spec.rb b/spec/lib/search_dictionary_selector_spec.rb index d9a618b25..b0f2cbc29 100644 --- a/spec/lib/search_dictionary_selector_spec.rb +++ b/spec/lib/search_dictionary_selector_spec.rb @@ -3,27 +3,23 @@ require "rails_helper" describe SearchDictionarySelector do context "from I18n default locale" do before { allow(subject).to receive(:call).and_call_original } - around do |example| - original_i18n_default = I18n.default_locale - begin - example.run - ensure - I18n.default_locale = original_i18n_default - end - end it "returns correct dictionary for simple locale" do - I18n.default_locale = :es + Setting["locales.default"] = "es" + expect(subject.call).to eq("spanish") end it "returns correct dictionary for compound locale" do - I18n.default_locale = :"pt-BR" + Setting["locales.default"] = "pt-BR" + expect(subject.call).to eq("portuguese") end it "returns simple for unsupported locale" do - expect(I18n).to receive(:default_locale).and_return(:pl) # avoiding I18n::InvalidLocale + allow(I18n).to receive(:available_locales).and_return(%i[en pl]) + Setting["locales.default"] = "pl" + expect(subject.call).to eq("simple") end end diff --git a/spec/mailers/mailer_spec.rb b/spec/mailers/mailer_spec.rb index af991075c..aed829ae2 100644 --- a/spec/mailers/mailer_spec.rb +++ b/spec/mailers/mailer_spec.rb @@ -30,6 +30,16 @@ describe Mailer do end end + describe "#user_invite" do + it "uses the default locale setting" do + Setting["locales.default"] = "es" + + Mailer.user_invite("invited@consul.dev").deliver_now + + expect(ActionMailer::Base.deliveries.last.body.to_s).to match " "Oh, yeah" }}]) diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb index 5df0fd8bc..f3a87ce22 100644 --- a/spec/models/setting_spec.rb +++ b/spec/models/setting_spec.rb @@ -260,7 +260,7 @@ describe Setting do end describe ".available_locales" do - before { allow(I18n).to receive(:available_locales).and_return(%i[de en es pt-BR]) } + before { allow(I18n).to receive_messages(default_locale: :de, available_locales: %i[de en es pt-BR]) } it "uses I18n available locales by default" do Setting["locales.enabled"] = "" @@ -280,6 +280,12 @@ describe Setting do expect(Setting.enabled_locales).to eq %i[de en pt-BR] end + it "adds the default locale to the list of available locales" do + Setting["locales.enabled"] = "en es" + + expect(Setting.enabled_locales).to eq %i[de en es] + end + it "ignores extra whitespace between locales" do Setting["locales.enabled"] = " de en pt-BR " @@ -293,9 +299,9 @@ describe Setting do end it "ignores words that don't make sense in this context" do - Setting["locales.enabled"] = "yes es 1234 en SuperCool" + Setting["locales.enabled"] = "yes de 1234 en SuperCool" - expect(Setting.enabled_locales).to eq %i[es en] + expect(Setting.enabled_locales).to eq %i[de en] end it "uses I18n available locales when no locale is available" do @@ -304,4 +310,44 @@ describe Setting do expect(Setting.enabled_locales).to eq %i[de en es pt-BR] end end + + describe ".default_locale" do + before { allow(I18n).to receive_messages(default_locale: :en, available_locales: %i[de en es pt-BR]) } + + it "uses I18n default locale by default" do + Setting["locales.default"] = "" + + expect(Setting.default_locale).to eq :en + end + + it "allows defining the default locale" do + Setting["locales.default"] = "de" + + expect(Setting.default_locale).to eq :de + end + + it "handles locales which include a dash" do + Setting["locales.default"] = "pt-BR" + + expect(Setting.default_locale).to eq :"pt-BR" + end + + it "ignores extra whitespace in the locale name" do + Setting["locales.default"] = " es " + + expect(Setting.default_locale).to eq :es + end + + it "ignores locales which aren't available" do + Setting["locales.default"] = "fr" + + expect(Setting.default_locale).to eq :en + end + + it "ignores an array of several locales" do + Setting["locales.default"] = "de es" + + expect(Setting.default_locale).to eq :en + end + end end diff --git a/spec/models/site_customization/content_block_spec.rb b/spec/models/site_customization/content_block_spec.rb index 3be09e812..d0b5d276b 100644 --- a/spec/models/site_customization/content_block_spec.rb +++ b/spec/models/site_customization/content_block_spec.rb @@ -29,6 +29,7 @@ RSpec.describe SiteCustomization::ContentBlock do end it "is not valid with a disabled locale" do + Setting["locales.default"] = "nl" Setting["locales.enabled"] = "nl pt-BR" block.locale = "en" diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 62a0663be..0e9d4fe22 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -81,6 +81,16 @@ describe User do end end + describe "#locale" do + it "defaults to the default locale setting" do + Setting["locales.default"] = "nl" + + user = build(:user, locale: nil) + + expect(user.locale).to eq "nl" + end + end + describe "preferences" do describe "email_on_comment" do it "is false by default" do