fix: Extend the locale without variant check for article locales as well (#11021)

We allow users to select locale variants when creating the help center
(e.g., pt_BR or en_UK). However, the selected variant may not always be
available for translation in the app. In such cases, we need to fall
back to either the base language or the default locale.

While this fallback logic was implemented for the portal locale, it was
missing for article locales.

This PR fixes that issue.
This commit is contained in:
Pranav
2025-03-06 18:24:46 -08:00
committed by GitHub
parent a0cf25ccde
commit a513f152ed
3 changed files with 102 additions and 28 deletions

View File

@@ -5,10 +5,11 @@ module SwitchLocale
def switch_locale(&) def switch_locale(&)
# priority is for locale set in query string (mostly for widget/from js sdk) # 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 locale ||= locale_from_custom_domain
# if locale is not set in account, let's use DEFAULT_LOCALE env variable # 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, &) set_locale(locale, &)
end end
@@ -32,26 +33,30 @@ module SwitchLocale
end end
def set_locale(locale, &) def set_locale(locale, &)
# if locale is empty, use default_locale safe_locale = validate_and_get_locale(locale)
locale ||= I18n.default_locale
# Ensure locale won't bleed into other requests # Ensure locale won't bleed into other requests
# https://guides.rubyonrails.org/i18n.html#managing-the-locale-across-requests # https://guides.rubyonrails.org/i18n.html#managing-the-locale-across-requests
I18n.with_locale(locale, &) I18n.with_locale(safe_locale, &)
end end
def locale_from_params def validate_and_get_locale(locale)
I18n.available_locales.map(&:to_s).include?(params[:locale]) ? params[:locale] : nil 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 end
def locale_from_account(account) def locale_from_account(account)
return unless account return unless account
I18n.available_locales.map(&:to_s).include?(account.locale) ? account.locale : nil account.locale
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
end end
end end

View File

@@ -1,4 +1,6 @@
class Public::Api::V1::Portals::BaseController < PublicController class Public::Api::V1::Portals::BaseController < PublicController
include SwitchLocale
before_action :show_plain_layout before_action :show_plain_layout
before_action :set_color_scheme before_action :set_color_scheme
before_action :set_global_config before_action :set_global_config
@@ -27,14 +29,7 @@ class Public::Api::V1::Portals::BaseController < PublicController
end end
def switch_locale_with_portal(&) def switch_locale_with_portal(&)
locale_without_variant = params[:locale].split('_')[0] @locale = validate_and_get_locale(params[:locale])
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
I18n.with_locale(@locale, &) I18n.with_locale(@locale, &)
end end
@@ -44,12 +39,12 @@ class Public::Api::V1::Portals::BaseController < PublicController
Rails.logger.info "Article: not found for slug: #{params[:article_slug]}" Rails.logger.info "Article: not found for slug: #{params[:article_slug]}"
render_404 && return if article.blank? render_404 && return if article.blank?
@locale = if article.category.present? article_locale = if article.category.present?
article.category.locale article.category.locale
else else
article.portal.default_locale article.portal.default_locale
end end
@locale = validate_and_get_locale(article_locale)
I18n.with_locale(@locale, &) I18n.with_locale(@locale, &)
end end

View File

@@ -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