diff --git a/.rubocop_todo/lint/redundant_cop_disable_directive.yml b/.rubocop_todo/lint/redundant_cop_disable_directive.yml
index 2c7bad0b3ca9181f1d971f00b59fa2b39ee78e62..0f0905bfeeeb89ac4c70dd493066a9d5e09d2d07 100644
--- a/.rubocop_todo/lint/redundant_cop_disable_directive.yml
+++ b/.rubocop_todo/lint/redundant_cop_disable_directive.yml
@@ -364,7 +364,6 @@ Lint/RedundantCopDisableDirective:
     - 'sidekiq_cluster/cli.rb'
     - 'sidekiq_cluster/sidekiq_cluster.rb'
     - 'spec/components/previews/pajamas/banner_component_preview.rb'
-    - 'spec/controllers/concerns/preferred_language_switcher_spec.rb'
     - 'spec/controllers/groups/milestones_controller_spec.rb'
     - 'spec/controllers/profiles/two_factor_auths_controller_spec.rb'
     - 'spec/controllers/projects/milestones_controller_spec.rb'
diff --git a/.rubocop_todo/rspec/feature_category.yml b/.rubocop_todo/rspec/feature_category.yml
index 064767d68b18e8a71fd12d27e7b7e710ad940fac..e6f6154acc238f7c8b2be41db09f67dc688f5ade 100644
--- a/.rubocop_todo/rspec/feature_category.yml
+++ b/.rubocop_todo/rspec/feature_category.yml
@@ -1229,7 +1229,6 @@ RSpec/FeatureCategory:
     - 'spec/controllers/concerns/issuable_actions_spec.rb'
     - 'spec/controllers/concerns/issuable_collections_spec.rb'
     - 'spec/controllers/concerns/page_limiter_spec.rb'
-    - 'spec/controllers/concerns/preferred_language_switcher_spec.rb'
     - 'spec/controllers/concerns/project_unauthorized_spec.rb'
     - 'spec/controllers/concerns/redirects_for_missing_path_on_tree_spec.rb'
     - 'spec/controllers/concerns/renders_commits_spec.rb'
diff --git a/.rubocop_todo/rspec/named_subject.yml b/.rubocop_todo/rspec/named_subject.yml
index 536591ff27f99959863f532f53b051e6cd8f3241..a72c233962c52d181a5ab78e1982dc88ee2291ba 100644
--- a/.rubocop_todo/rspec/named_subject.yml
+++ b/.rubocop_todo/rspec/named_subject.yml
@@ -1218,7 +1218,6 @@ RSpec/NamedSubject:
     - 'spec/controllers/concerns/check_rate_limit_spec.rb'
     - 'spec/controllers/concerns/graceful_timeout_handling_spec.rb'
     - 'spec/controllers/concerns/page_limiter_spec.rb'
-    - 'spec/controllers/concerns/preferred_language_switcher_spec.rb'
     - 'spec/controllers/concerns/product_analytics_tracking_spec.rb'
     - 'spec/controllers/concerns/renders_commits_spec.rb'
     - 'spec/controllers/concerns/routable_actions_spec.rb'
diff --git a/.rubocop_todo/style/inline_disable_annotation.yml b/.rubocop_todo/style/inline_disable_annotation.yml
index 872ecd87635eede138e527bb199f53f19e8565fe..fd15c534ebe46acd8dea5421937a166e97e04b7b 100644
--- a/.rubocop_todo/style/inline_disable_annotation.yml
+++ b/.rubocop_todo/style/inline_disable_annotation.yml
@@ -2488,7 +2488,6 @@ Style/InlineDisableAnnotation:
     - 'spec/config/application_spec.rb'
     - 'spec/controllers/concerns/content_security_policy_patch_spec.rb'
     - 'spec/controllers/concerns/continue_params_spec.rb'
-    - 'spec/controllers/concerns/preferred_language_switcher_spec.rb'
     - 'spec/controllers/groups/milestones_controller_spec.rb'
     - 'spec/controllers/omniauth_callbacks_controller_spec.rb'
     - 'spec/controllers/profiles/two_factor_auths_controller_spec.rb'
diff --git a/app/controllers/concerns/preferred_language_switcher.rb b/app/controllers/concerns/preferred_language_switcher.rb
index 011bdbbca1053662dd192888194d6225b0821834..664c1717f7065c97b9f3dc89318995e717a381c1 100644
--- a/app/controllers/concerns/preferred_language_switcher.rb
+++ b/app/controllers/concerns/preferred_language_switcher.rb
@@ -15,7 +15,7 @@ def init_preferred_language
 
   def preferred_language
     cookies[:preferred_language].presence_in(Gitlab::I18n.available_locales) ||
-      selectable_language(marketing_site_language) ||
+      selectable_language(language_from_params) ||
       selectable_language(browser_languages) ||
       Gitlab::CurrentSettings.default_preferred_language
   end
@@ -37,14 +37,9 @@ def browser_languages
   end
   strong_memoize_attr :browser_languages
 
-  def marketing_site_language
-    # Our marketing site will be the only thing we are sure of the language placement in the url for.
-    locale = params[:glm_source]&.match(%r{\A#{ApplicationHelper.promo_host}/([a-z]{2})-([a-z]{2})}i)&.captures
-
-    return [] if locale.blank?
-
-    # This is local and then locale_region - the marketing site will always send locale-region pairs like fr-fr.
-    [locale[0], "#{locale[0]}_#{locale[1]}"]
+  def language_from_params
+    # overridden in ee
+    []
   end
 end
 
diff --git a/ee/app/controllers/concerns/ee/preferred_language_switcher.rb b/ee/app/controllers/concerns/ee/preferred_language_switcher.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3a3121541c6a05c7c186ac01c3f48c536269d66a
--- /dev/null
+++ b/ee/app/controllers/concerns/ee/preferred_language_switcher.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module EE
+  module PreferredLanguageSwitcher
+    extend ::Gitlab::Utils::Override
+
+    private
+
+    def language_from_params
+      return super unless ::Gitlab::Saas.feature_available?(:marketing_site_language)
+
+      # Our marketing site will be the only thing we are sure of the language placement in the url for.
+      glm_source = params.permit(:glm_source)[:glm_source]
+      locale = glm_source&.match(%r{\A#{::ApplicationHelper.promo_host}/([a-z]{2})-([a-z]{2})}i)&.captures
+
+      return [] if locale.blank?
+
+      # This is local and then locale_region - the marketing site will always send locale-region pairs like fr-fr.
+      [locale[0], "#{locale[0]}_#{locale[1]}"]
+    end
+  end
+end
diff --git a/ee/config/saas_features/marketing_site_language.yml b/ee/config/saas_features/marketing_site_language.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7a30ffd985aae0045d39aceea377aa419bc6f019
--- /dev/null
+++ b/ee/config/saas_features/marketing_site_language.yml
@@ -0,0 +1,5 @@
+---
+name: marketing_site_language
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/165093
+milestone: '17.5'
+group: group::acquisition
diff --git a/ee/lib/ee/gitlab/saas.rb b/ee/lib/ee/gitlab/saas.rb
index b177f5b48a8d66f854cc1bdbc7dbcd8a036a6256..d86fcbf795ce89521cb84788be9cff56d9fe297c 100644
--- a/ee/lib/ee/gitlab/saas.rb
+++ b/ee/lib/ee/gitlab/saas.rb
@@ -12,6 +12,7 @@ module Saas
           ai_vertex_embeddings
           experimentation
           marketing_google_tag_manager
+          marketing_site_language
           namespaces_storage_limit
           onboarding
           purchases_additional_minutes
diff --git a/ee/spec/controllers/concerns/ee/preferred_language_switcher_spec.rb b/ee/spec/controllers/concerns/ee/preferred_language_switcher_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..949e86c15c45ba3f3e834349191624e6fe1a3a92
--- /dev/null
+++ b/ee/spec/controllers/concerns/ee/preferred_language_switcher_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe PreferredLanguageSwitcher, type: :controller, feature_category: :acquisition do
+  controller(ActionController::Base) do
+    include PreferredLanguageSwitcher
+
+    before_action :init_preferred_language, only: :new
+
+    def new
+      render html: 'new page'
+    end
+  end
+
+  subject { cookies[:preferred_language] }
+
+  before do
+    stub_feature_flags(disable_preferred_language_cookie: false)
+  end
+
+  context 'when the marketing_site_language SaaS feature is available' do
+    before do
+      stub_saas_features(marketing_site_language: true)
+    end
+
+    context 'when first visit' do
+      let(:glm_source) { 'about.gitlab.com' }
+      let(:accept_language_header) { nil }
+
+      before do
+        request.env['HTTP_ACCEPT_LANGUAGE'] = accept_language_header
+
+        get :new, params: { glm_source: glm_source }
+      end
+
+      it { is_expected.to eq Gitlab::CurrentSettings.default_preferred_language }
+
+      context 'when language param is valid' do
+        let(:glm_source) { 'about.gitlab.com/fr-fr/' }
+
+        it { is_expected.to eq 'fr' }
+
+        context 'for case insensitivity on language' do
+          let(:glm_source) { 'about.gitlab.com/fr-FR/' }
+
+          it { is_expected.to eq 'fr' }
+        end
+
+        context 'for case insensitivity on marketing site URL' do
+          let(:glm_source) { 'ABOUT.gitlab.com/fr-fr/' }
+
+          it { is_expected.to eq 'fr' }
+        end
+
+        context 'when language param is invalid' do
+          let(:glm_source) { 'about.gitlab.com/ko-ko/' }
+
+          it { is_expected.to eq Gitlab::CurrentSettings.default_preferred_language }
+        end
+
+        context 'when the glm_source is not the marketing site' do
+          let(:glm_source) { 'some.othersite.com/fr-fr/' }
+
+          it { is_expected.to eq Gitlab::CurrentSettings.default_preferred_language }
+        end
+      end
+
+      context 'when language params and language header are both valid' do
+        let(:accept_language_header) { 'zh-CN,zh;q=0.8,zh-TW;q=0.7' }
+        let(:glm_source) { 'about.gitlab.com/fr-fr/' }
+
+        it { is_expected.to eq 'fr' }
+      end
+    end
+  end
+
+  context 'when the marketing_site_language SaaS feature is not available' do
+    before do
+      stub_saas_features(marketing_site_language: false)
+    end
+
+    context 'for first visit' do
+      let(:glm_source) { 'about.gitlab.com/fr-fr/' }
+      let(:accept_language_header) { nil }
+
+      before do
+        request.env['HTTP_ACCEPT_LANGUAGE'] = accept_language_header
+
+        get :new, params: { glm_source: glm_source }
+      end
+
+      it { is_expected.to eq Gitlab::CurrentSettings.default_preferred_language }
+    end
+  end
+end
diff --git a/spec/controllers/concerns/preferred_language_switcher_spec.rb b/spec/controllers/concerns/preferred_language_switcher_spec.rb
index 9f897b14a21e6c3b0617f791b8f467e91d3db8fa..23219388850a142503e32cab9d15c523b3778298 100644
--- a/spec/controllers/concerns/preferred_language_switcher_spec.rb
+++ b/spec/controllers/concerns/preferred_language_switcher_spec.rb
@@ -2,9 +2,9 @@
 
 require 'spec_helper'
 
-RSpec.describe PreferredLanguageSwitcher, type: :controller do
+RSpec.describe PreferredLanguageSwitcher, type: :controller, feature_category: :acquisition do
   controller(ActionController::Base) do
-    include PreferredLanguageSwitcher # rubocop:disable RSpec/DescribedClass
+    include PreferredLanguageSwitcher
 
     before_action :init_preferred_language, only: :new
 
@@ -19,100 +19,40 @@ def new
     stub_feature_flags(disable_preferred_language_cookie: false)
   end
 
-  context 'when first visit' do
-    let(:glm_source) { 'about.gitlab.com' }
+  context 'for first visit' do
     let(:accept_language_header) { nil }
 
     before do
       request.env['HTTP_ACCEPT_LANGUAGE'] = accept_language_header
 
-      get :new, params: { glm_source: glm_source }
-    end
-
-    it 'sets preferred_language to default' do
-      expect(subject).to eq Gitlab::CurrentSettings.default_preferred_language
+      get :new
     end
 
-    context 'when language param is valid' do
-      let(:glm_source) { 'about.gitlab.com/fr-fr/' }
-
-      it 'sets preferred_language accordingly' do
-        expect(subject).to eq 'fr'
-      end
-
-      context 'for case insensitivity on language' do
-        let(:glm_source) { 'about.gitlab.com/fr-FR/' }
-
-        it 'sets preferred_language accordingly' do
-          expect(subject).to eq 'fr'
-        end
-      end
-
-      context 'for case insensitivity on marketing site URL' do
-        let(:glm_source) { 'ABOUT.gitlab.com/fr-fr/' }
-
-        it 'sets preferred_language accordingly' do
-          expect(subject).to eq 'fr'
-        end
-      end
-
-      context 'when language param is invalid' do
-        let(:glm_source) { 'about.gitlab.com/ko-ko/' }
-
-        it 'sets preferred_language to default' do
-          expect(subject).to eq Gitlab::CurrentSettings.default_preferred_language
-        end
-      end
-
-      context 'when the glm_source is not the marketing site' do
-        let(:glm_source) { 'some.othersite.com/fr-fr/' }
-
-        it 'sets preferred_language to default' do
-          expect(subject).to eq Gitlab::CurrentSettings.default_preferred_language
-        end
-      end
-    end
+    it { is_expected.to eq Gitlab::CurrentSettings.default_preferred_language }
 
     context 'when browser preferred language is not english' do
       context 'with selectable language' do
         let(:accept_language_header) { 'zh-CN,zh;q=0.8,zh-TW;q=0.7' }
 
-        it 'sets preferred_language accordingly' do
-          expect(subject).to eq 'zh_CN'
-        end
+        it { is_expected.to eq 'zh_CN' }
       end
 
       context 'with unselectable language' do
         let(:accept_language_header) { 'nl-NL;q=0.8' }
 
-        it 'sets preferred_language to default' do
-          expect(subject).to eq Gitlab::CurrentSettings.default_preferred_language
-        end
+        it { is_expected.to eq Gitlab::CurrentSettings.default_preferred_language }
       end
 
       context 'with empty string in language header' do
         let(:accept_language_header) { '' }
 
-        it 'sets preferred_language to default' do
-          expect(subject).to eq Gitlab::CurrentSettings.default_preferred_language
-        end
+        it { is_expected.to eq Gitlab::CurrentSettings.default_preferred_language }
       end
 
       context 'with language header without dashes' do
         let(:accept_language_header) { 'fr;q=8' }
 
-        it 'sets preferred_language accordingly' do
-          expect(subject).to eq 'fr'
-        end
-      end
-    end
-
-    context 'when language params and language header are both valid' do
-      let(:accept_language_header) { 'zh-CN,zh;q=0.8,zh-TW;q=0.7' }
-      let(:glm_source) { 'about.gitlab.com/fr-fr/' }
-
-      it 'sets preferred_language according to language params' do
-        expect(subject).to eq 'fr'
+        it { is_expected.to eq 'fr' }
       end
     end
   end
@@ -129,17 +69,13 @@ def new
     context 'with a valid value' do
       let(:user_preferred_language) { 'zh_CN' }
 
-      it 'keeps preferred language unchanged' do
-        expect(subject).to eq user_preferred_language
-      end
+      it { is_expected.to eq user_preferred_language }
     end
 
     context 'with an invalid value' do
       let(:user_preferred_language) { 'xxx' }
 
-      it 'sets preferred_language to default' do
-        expect(subject).to eq Gitlab::CurrentSettings.default_preferred_language
-      end
+      it { is_expected.to eq Gitlab::CurrentSettings.default_preferred_language }
     end
   end
 
@@ -149,8 +85,6 @@ def new
       get :new
     end
 
-    it 'does not set the cookie' do
-      expect(subject).to be_nil
-    end
+    it { is_expected.to be_nil }
   end
 end