From 076abe46fac95ed06a9d67b5b604df41db5e8401 Mon Sep 17 00:00:00 2001 From: Vinoth Kannan Date: Tue, 27 Sep 2022 14:26:06 +0530 Subject: [PATCH] FEATURE: new site setting to set locale from cookie for anonymous users. (#18377) This new hidden default-disabled site setting `set_locale_from_cookie` will set locale from anonymous user's cookie value. --- app/controllers/application_controller.rb | 11 ++---- config/site_settings.yml | 5 +++ lib/discourse.rb | 6 ++++ lib/middleware/anonymous_cache.rb | 4 +-- spec/lib/middleware/anonymous_cache_spec.rb | 5 ++- spec/requests/application_controller_spec.rb | 38 ++++++++++++++++++++ 6 files changed, 57 insertions(+), 12 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7e6857625b8..d7bd7429229 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -400,11 +400,8 @@ class ApplicationController < ActionController::Base if check_current_user && (user = current_user rescue nil) locale = user.effective_locale else - if SiteSetting.set_locale_from_accept_language_header - locale = locale_from_header - else - locale = SiteSetting.default_locale - end + locale = Discourse.anonymous_locale(request) + locale ||= SiteSetting.default_locale end if !I18n.locale_available?(locale) @@ -624,10 +621,6 @@ class ApplicationController < ActionController::Base private - def locale_from_header - HttpLanguageParser.parse(request.env["HTTP_ACCEPT_LANGUAGE"]) - end - def preload_anonymous_data store_preloaded("site", Site.json_for(guardian)) store_preloaded("siteSettings", SiteSetting.client_settings_json) diff --git a/config/site_settings.yml b/config/site_settings.yml index 2c6087a9752..93fb4a5f415 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -123,6 +123,11 @@ basic: default: false client: true validator: "AllowUserLocaleEnabledValidator" + set_locale_from_cookie: + default: false + hidden: true + client: true + validator: "AllowUserLocaleEnabledValidator" support_mixed_text_direction: client: true default: false diff --git a/lib/discourse.rb b/lib/discourse.rb index 9bd00a2e640..1773d4e3054 100644 --- a/lib/discourse.rb +++ b/lib/discourse.rb @@ -1085,4 +1085,10 @@ module Discourse Theme.all.each(&:update_javascript_cache!) Theme.expire_site_cache! end + + def self.anonymous_locale(request) + locale = HttpLanguageParser.parse(request.cookies["locale"]) if SiteSetting.set_locale_from_cookie + locale ||= HttpLanguageParser.parse(request.env["HTTP_ACCEPT_LANGUAGE"]) if SiteSetting.set_locale_from_accept_language_header + locale + end end diff --git a/lib/middleware/anonymous_cache.rb b/lib/middleware/anonymous_cache.rb index b9e0d0db8aa..ef6bc7e1dd2 100644 --- a/lib/middleware/anonymous_cache.rb +++ b/lib/middleware/anonymous_cache.rb @@ -100,8 +100,8 @@ module Middleware end def key_locale - if SiteSetting.set_locale_from_accept_language_header - HttpLanguageParser.parse(@env["HTTP_ACCEPT_LANGUAGE"]) + if locale = Discourse.anonymous_locale(@request) + locale else "" # No need to key, it is the same for all anon users end diff --git a/spec/lib/middleware/anonymous_cache_spec.rb b/spec/lib/middleware/anonymous_cache_spec.rb index 71f0428435d..34eff03ea22 100644 --- a/spec/lib/middleware/anonymous_cache_spec.rb +++ b/spec/lib/middleware/anonymous_cache_spec.rb @@ -53,7 +53,7 @@ RSpec.describe Middleware::AnonymousCache do end end - context "with header-based locale locale" do + context "with header or cookie based custom locale" do it "handles different languages" do # Normally does not check the language header french1 = new_helper("HTTP_ACCEPT_LANGUAGE" => "fr").cache_key @@ -76,6 +76,9 @@ RSpec.describe Middleware::AnonymousCache do expect(none).to eq(english) expect(french1).to eq(french2) expect(french1).not_to eq(none) + + SiteSetting.set_locale_from_cookie = true + expect(new_helper("HTTP_COOKIE" => "locale=es;").cache_key).to include("l=es") end end diff --git a/spec/requests/application_controller_spec.rb b/spec/requests/application_controller_spec.rb index adc80945adb..d25d1a983d6 100644 --- a/spec/requests/application_controller_spec.rb +++ b/spec/requests/application_controller_spec.rb @@ -895,6 +895,44 @@ RSpec.describe ApplicationController do end end end + + context "with set_locale_from_cookie enabled" do + context "when cookie locale differs from default locale" do + before do + SiteSetting.allow_user_locale = true + SiteSetting.set_locale_from_cookie = true + SiteSetting.default_locale = "en" + end + + context "with an anonymous user" do + it "uses the locale from the cookie" do + get "/bootstrap.json", headers: { Cookie: "locale=es" } + expect(response.status).to eq(200) + expect(response.parsed_body['bootstrap']['locale_script']).to end_with("es.js") + expect(I18n.locale.to_s).to eq(SiteSettings::DefaultsProvider::DEFAULT_LOCALE) # doesn't leak after requests + end + end + + context "when the preferred locale includes a region" do + it "returns the locale and region separated by an underscore" do + get "/bootstrap.json", headers: { Cookie: "locale=zh-CN" } + expect(response.status).to eq(200) + expect(response.parsed_body['bootstrap']['locale_script']).to end_with("zh_CN.js") + end + end + end + + context 'when locale cookie is not set' do + it 'uses the site default locale' do + SiteSetting.allow_user_locale = true + SiteSetting.default_locale = 'en' + + get "/bootstrap.json", headers: { Cookie: "" } + expect(response.status).to eq(200) + expect(response.parsed_body['bootstrap']['locale_script']).to end_with("en.js") + end + end + end end describe 'vary header' do