diff --git a/app/controllers/concerns/switch_locale.rb b/app/controllers/concerns/switch_locale.rb index 3013ff3cc..a8ea8ae05 100644 --- a/app/controllers/concerns/switch_locale.rb +++ b/app/controllers/concerns/switch_locale.rb @@ -5,10 +5,11 @@ module SwitchLocale def switch_locale(&) # priority is for locale set in query string (mostly for widget/from js sdk) - locale ||= locale_from_params + locale ||= params[:locale] + locale ||= locale_from_custom_domain # if locale is not set in account, let's use DEFAULT_LOCALE env variable - locale ||= locale_from_env_variable + locale ||= ENV.fetch('DEFAULT_LOCALE', nil) set_locale(locale, &) end @@ -32,26 +33,30 @@ module SwitchLocale end def set_locale(locale, &) - # if locale is empty, use default_locale - locale ||= I18n.default_locale + safe_locale = validate_and_get_locale(locale) # Ensure locale won't bleed into other requests # https://guides.rubyonrails.org/i18n.html#managing-the-locale-across-requests - I18n.with_locale(locale, &) + I18n.with_locale(safe_locale, &) end - def locale_from_params - I18n.available_locales.map(&:to_s).include?(params[:locale]) ? params[:locale] : nil + def validate_and_get_locale(locale) + return I18n.default_locale.to_s if locale.blank? + + available_locales = I18n.available_locales.map(&:to_s) + locale_without_variant = locale.split('_')[0] + + if available_locales.include?(locale) + locale + elsif available_locales.include?(locale_without_variant) + locale_without_variant + else + I18n.default_locale.to_s + end end def locale_from_account(account) return unless account - I18n.available_locales.map(&:to_s).include?(account.locale) ? account.locale : nil - end - - def locale_from_env_variable - return unless ENV.fetch('DEFAULT_LOCALE', nil) - - I18n.available_locales.map(&:to_s).include?(ENV.fetch('DEFAULT_LOCALE')) ? ENV.fetch('DEFAULT_LOCALE') : nil + account.locale end end diff --git a/app/controllers/public/api/v1/portals/base_controller.rb b/app/controllers/public/api/v1/portals/base_controller.rb index f6c10f7c4..66b052b1e 100644 --- a/app/controllers/public/api/v1/portals/base_controller.rb +++ b/app/controllers/public/api/v1/portals/base_controller.rb @@ -1,4 +1,6 @@ class Public::Api::V1::Portals::BaseController < PublicController + include SwitchLocale + before_action :show_plain_layout before_action :set_color_scheme before_action :set_global_config @@ -27,14 +29,7 @@ class Public::Api::V1::Portals::BaseController < PublicController end def switch_locale_with_portal(&) - locale_without_variant = params[:locale].split('_')[0] - is_locale_available = I18n.available_locales.map(&:to_s).include?(params[:locale]) - is_locale_variant_available = I18n.available_locales.map(&:to_s).include?(locale_without_variant) - if is_locale_available - @locale = params[:locale] - elsif is_locale_variant_available - @locale = locale_without_variant - end + @locale = validate_and_get_locale(params[:locale]) I18n.with_locale(@locale, &) end @@ -44,12 +39,12 @@ class Public::Api::V1::Portals::BaseController < PublicController Rails.logger.info "Article: not found for slug: #{params[:article_slug]}" render_404 && return if article.blank? - @locale = if article.category.present? - article.category.locale - else - article.portal.default_locale - end - + article_locale = if article.category.present? + article.category.locale + else + article.portal.default_locale + end + @locale = validate_and_get_locale(article_locale) I18n.with_locale(@locale, &) end diff --git a/spec/models/concerns/switch_locale_spec.rb b/spec/models/concerns/switch_locale_spec.rb new file mode 100644 index 000000000..98628b92c --- /dev/null +++ b/spec/models/concerns/switch_locale_spec.rb @@ -0,0 +1,74 @@ +require 'rails_helper' + +RSpec.describe 'SwitchLocale Concern', type: :controller do + controller(ApplicationController) do + include SwitchLocale + + def index + switch_locale { render plain: I18n.locale } + end + + def account_locale + switch_locale_using_account_locale { render plain: I18n.locale } + end + end + + let(:account) { create(:account, locale: 'es') } + let(:portal) { create(:portal, custom_domain: 'custom.example.com', config: { default_locale: 'fr_FR' }) } + + describe '#switch_locale' do + context 'when locale is provided in params' do + it 'sets locale from params' do + get :index, params: { locale: 'es' } + expect(response.body).to eq('es') + end + + it 'falls back to default locale if invalid' do + get :index, params: { locale: 'invalid' } + expect(response.body).to eq('en') + end + end + + context 'when request is from custom domain' do + before { request.host = portal.custom_domain } + + it 'sets locale from portal' do + get :index + expect(response.body).to eq('fr') + end + + it 'overrides portal locale with param' do + get :index, params: { locale: 'es' } + expect(response.body).to eq('es') + end + end + + context 'when locale is not provided anywhere' do + it 'sets locale from environment variable' do + with_modified_env(DEFAULT_LOCALE: 'de_DE') do + get :index + expect(response.body).to eq('de') + end + end + + it 'falls back to default locale if env locale invalid' do + with_modified_env(DEFAULT_LOCALE: 'invalid') do + get :index + expect(response.body).to eq('en') + end + end + end + end + + describe '#switch_locale_using_account_locale' do + before do + routes.draw { get 'account_locale' => 'anonymous#account_locale' } + end + + it 'sets locale from account' do + controller.instance_variable_set(:@current_account, account) + get :account_locale + expect(response.body).to eq('es') + end + end +end