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.
This commit is contained in:
@@ -56,7 +56,7 @@ class ApplicationController < ActionController::Base
|
|||||||
elsif Setting.enabled_locales.include?(session[:locale]&.to_sym)
|
elsif Setting.enabled_locales.include?(session[:locale]&.to_sym)
|
||||||
session[:locale]
|
session[:locale]
|
||||||
else
|
else
|
||||||
I18n.default_locale
|
Setting.default_locale
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class Management::BaseController < ActionController::Base
|
|||||||
session[:locale] = params[:locale].to_s
|
session[:locale] = params[:locale].to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
session[:locale] ||= I18n.default_locale.to_s
|
session[:locale] ||= Setting.default_locale.to_s
|
||||||
|
|
||||||
I18n.with_locale(session[:locale], &action)
|
I18n.with_locale(session[:locale], &action)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ module SearchDictionarySelector
|
|||||||
private
|
private
|
||||||
|
|
||||||
def find_from_i18n_default
|
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 = I18N_TO_DICTIONARY[key_to_lookup]
|
||||||
dictionary ||= "simple"
|
dictionary ||= "simple"
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class Mailer < ApplicationMailer
|
|||||||
def user_invite(email)
|
def user_invite(email)
|
||||||
@email_to = 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"]))
|
mail(to: @email_to, subject: t("mailers.user_invite.subject", org_name: Setting["org_name"]))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ module Globalizable
|
|||||||
translation_class.instance_eval do
|
translation_class.instance_eval do
|
||||||
validates method,
|
validates method,
|
||||||
length: options[:length],
|
length: options[:length],
|
||||||
if: lambda { |translation| translation.locale == I18n.default_locale }
|
if: lambda { |translation| translation.locale == Setting.default_locale }
|
||||||
end
|
end
|
||||||
if options.count > 1
|
if options.count > 1
|
||||||
translation_class.instance_eval do
|
translation_class.instance_eval do
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ class Setting < ApplicationRecord
|
|||||||
# Code to be included at the top (inside <head>) of every page (useful for tracking)
|
# Code to be included at the top (inside <head>) of every page (useful for tracking)
|
||||||
"html.per_page_code_head": "",
|
"html.per_page_code_head": "",
|
||||||
"locales.enabled": nil,
|
"locales.enabled": nil,
|
||||||
|
"locales.default": nil,
|
||||||
"map.latitude": 51.48,
|
"map.latitude": 51.48,
|
||||||
"map.longitude": 0.0,
|
"map.longitude": 0.0,
|
||||||
"map.zoom": 10,
|
"map.zoom": 10,
|
||||||
@@ -224,7 +225,16 @@ class Setting < ApplicationRecord
|
|||||||
def enabled_locales
|
def enabled_locales
|
||||||
locales = Setting["locales.enabled"].to_s.split.map(&:to_sym)
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ class User < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def locale
|
def locale
|
||||||
self[:locale] || I18n.default_locale.to_s
|
self[:locale] || Setting.default_locale.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirmation_required?
|
def confirmation_required?
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ end
|
|||||||
|
|
||||||
def random_locales
|
def random_locales
|
||||||
[
|
[
|
||||||
I18n.default_locale,
|
Setting.default_locale,
|
||||||
*(Setting.enabled_locales & %i[en es]),
|
*(Setting.enabled_locales & %i[en es]),
|
||||||
*Setting.enabled_locales.sample(4)
|
*Setting.enabled_locales.sample(4)
|
||||||
].uniq.take(5)
|
].uniq.take(5)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ describe Admin::SiteCustomization::ContentBlocks::FormContentBlockComponent do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "only includes enabled settings" do
|
it "only includes enabled settings" do
|
||||||
|
Setting["locales.default"] = "de"
|
||||||
Setting["locales.enabled"] = "de fr"
|
Setting["locales.enabled"] = "de fr"
|
||||||
|
|
||||||
render_inline component
|
render_inline component
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ describe Admin::SiteCustomization::ContentBlocks::FormHeadingContentBlockCompone
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "only includes enabled settings" do
|
it "only includes enabled settings" do
|
||||||
|
Setting["locales.default"] = "de"
|
||||||
Setting["locales.enabled"] = "de fr"
|
Setting["locales.enabled"] = "de fr"
|
||||||
|
|
||||||
render_inline component
|
render_inline component
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ describe Layout::LocaleSwitcherComponent do
|
|||||||
context "when not all available locales are enabled" do
|
context "when not all available locales are enabled" do
|
||||||
before do
|
before do
|
||||||
allow(I18n).to receive(:available_locales).and_return(%i[en es fr])
|
allow(I18n).to receive(:available_locales).and_return(%i[en es fr])
|
||||||
|
Setting["locales.default"] = "es"
|
||||||
Setting["locales.enabled"] = "es fr"
|
Setting["locales.enabled"] = "es fr"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -23,9 +23,11 @@ describe ApplicationController do
|
|||||||
|
|
||||||
describe "#switch_locale" do
|
describe "#switch_locale" do
|
||||||
it "uses the default locale by default" do
|
it "uses the default locale by default" do
|
||||||
|
Setting["locales.default"] = "pt-BR"
|
||||||
|
|
||||||
get :index
|
get :index
|
||||||
|
|
||||||
expect(response.body).to eq "en"
|
expect(response.body).to eq "pt-BR"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "uses the locale in the parameters when it's there" do
|
it "uses the locale in the parameters when it's there" do
|
||||||
|
|||||||
@@ -34,9 +34,11 @@ describe Management::BaseController do
|
|||||||
|
|
||||||
describe "#switch_locale" do
|
describe "#switch_locale" do
|
||||||
it "uses the default locale by default" do
|
it "uses the default locale by default" do
|
||||||
|
Setting["locales.default"] = "pt-BR"
|
||||||
|
|
||||||
get :index
|
get :index
|
||||||
|
|
||||||
expect(response.body).to eq "en"
|
expect(response.body).to eq "pt-BR"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "uses the locale in the parameters when it's there" do
|
it "uses the locale in the parameters when it's there" do
|
||||||
|
|||||||
@@ -29,13 +29,14 @@ describe SubscriptionsController do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "only accepts enabled locales" do
|
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")
|
create(:user, locale: "es", subscriptions_token: "mytoken")
|
||||||
|
|
||||||
get :edit, params: { token: "mytoken" }
|
get :edit, params: { token: "mytoken" }
|
||||||
|
|
||||||
expect(session[:locale]).to eq "en"
|
expect(session[:locale]).to eq "fr"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,27 +3,23 @@ require "rails_helper"
|
|||||||
describe SearchDictionarySelector do
|
describe SearchDictionarySelector do
|
||||||
context "from I18n default locale" do
|
context "from I18n default locale" do
|
||||||
before { allow(subject).to receive(:call).and_call_original }
|
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
|
it "returns correct dictionary for simple locale" do
|
||||||
I18n.default_locale = :es
|
Setting["locales.default"] = "es"
|
||||||
|
|
||||||
expect(subject.call).to eq("spanish")
|
expect(subject.call).to eq("spanish")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns correct dictionary for compound locale" do
|
it "returns correct dictionary for compound locale" do
|
||||||
I18n.default_locale = :"pt-BR"
|
Setting["locales.default"] = "pt-BR"
|
||||||
|
|
||||||
expect(subject.call).to eq("portuguese")
|
expect(subject.call).to eq("portuguese")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns simple for unsupported locale" do
|
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")
|
expect(subject.call).to eq("simple")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -30,6 +30,16 @@ describe Mailer do
|
|||||||
end
|
end
|
||||||
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 "<html lang=\"es\""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "#manage_subscriptions_token" do
|
describe "#manage_subscriptions_token" do
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
let(:proposal) { create(:proposal, author: user) }
|
let(:proposal) { create(:proposal, author: user) }
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ describe Budget::ContentBlock do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "is not valid with a disabled locale" do
|
it "is not valid with a disabled locale" do
|
||||||
|
Setting["locales.default"] = "pt-BR"
|
||||||
Setting["locales.enabled"] = "nl pt-BR"
|
Setting["locales.enabled"] = "nl pt-BR"
|
||||||
|
|
||||||
block.locale = "en"
|
block.locale = "en"
|
||||||
|
|||||||
47
spec/models/globalizable_spec.rb
Normal file
47
spec/models/globalizable_spec.rb
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe Globalizable do
|
||||||
|
before do
|
||||||
|
dummy_banner = Class.new(ApplicationRecord) do
|
||||||
|
def self.name
|
||||||
|
"DummyBanner"
|
||||||
|
end
|
||||||
|
self.table_name = "banners"
|
||||||
|
|
||||||
|
translates :title, touch: true
|
||||||
|
include Globalizable
|
||||||
|
has_many :translations, class_name: "DummyBanner::Translation", foreign_key: "banner_id"
|
||||||
|
|
||||||
|
validates_translation :title, length: { minimum: 7 }
|
||||||
|
end
|
||||||
|
|
||||||
|
stub_const("DummyBanner", dummy_banner)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".validates_translation" do
|
||||||
|
it "validates length for the default locale" do
|
||||||
|
Setting["locales.default"] = "es"
|
||||||
|
|
||||||
|
dummy = DummyBanner.new
|
||||||
|
dummy.translations.build(locale: "es", title: "Short")
|
||||||
|
dummy.translations.build(locale: "fr", title: "Long enough")
|
||||||
|
|
||||||
|
I18n.with_locale(:fr) do
|
||||||
|
expect(dummy).not_to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not validate length for other locales" do
|
||||||
|
Setting["locales.default"] = "es"
|
||||||
|
|
||||||
|
dummy = DummyBanner.new
|
||||||
|
dummy.translations.build(locale: "es", title: "Long enough")
|
||||||
|
dummy.translations.build(locale: "fr", title: "Long enough")
|
||||||
|
dummy.translations.build(locale: "en", title: "Short")
|
||||||
|
|
||||||
|
I18n.with_locale(:fr) do
|
||||||
|
expect(dummy).to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -160,6 +160,7 @@ RSpec.describe I18nContent do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "does not store new keys for disabled translations" do
|
it "does not store new keys for disabled translations" do
|
||||||
|
Setting["locales.default"] = "es"
|
||||||
Setting["locales.enabled"] = "es"
|
Setting["locales.enabled"] = "es"
|
||||||
|
|
||||||
I18nContent.update([{ id: "shared.yes", values: { "value_en" => "Oh, yeah" }}])
|
I18nContent.update([{ id: "shared.yes", values: { "value_en" => "Oh, yeah" }}])
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ describe Setting do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe ".available_locales" do
|
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
|
it "uses I18n available locales by default" do
|
||||||
Setting["locales.enabled"] = ""
|
Setting["locales.enabled"] = ""
|
||||||
@@ -280,6 +280,12 @@ describe Setting do
|
|||||||
expect(Setting.enabled_locales).to eq %i[de en pt-BR]
|
expect(Setting.enabled_locales).to eq %i[de en pt-BR]
|
||||||
end
|
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
|
it "ignores extra whitespace between locales" do
|
||||||
Setting["locales.enabled"] = " de en pt-BR "
|
Setting["locales.enabled"] = " de en pt-BR "
|
||||||
|
|
||||||
@@ -293,9 +299,9 @@ describe Setting do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "ignores words that don't make sense in this context" do
|
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
|
end
|
||||||
|
|
||||||
it "uses I18n available locales when no locale is available" do
|
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]
|
expect(Setting.enabled_locales).to eq %i[de en es pt-BR]
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ RSpec.describe SiteCustomization::ContentBlock do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "is not valid with a disabled locale" do
|
it "is not valid with a disabled locale" do
|
||||||
|
Setting["locales.default"] = "nl"
|
||||||
Setting["locales.enabled"] = "nl pt-BR"
|
Setting["locales.enabled"] = "nl pt-BR"
|
||||||
|
|
||||||
block.locale = "en"
|
block.locale = "en"
|
||||||
|
|||||||
@@ -81,6 +81,16 @@ describe User do
|
|||||||
end
|
end
|
||||||
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 "preferences" do
|
||||||
describe "email_on_comment" do
|
describe "email_on_comment" do
|
||||||
it "is false by default" do
|
it "is false by default" do
|
||||||
|
|||||||
Reference in New Issue
Block a user