diff --git a/app/assets/javascripts/discourse/tests/index.html b/app/assets/javascripts/discourse/tests/index.html
index 3582ff67394..c4fb561923e 100644
--- a/app/assets/javascripts/discourse/tests/index.html
+++ b/app/assets/javascripts/discourse/tests/index.html
@@ -54,9 +54,9 @@
-
-
-
+
+
+
diff --git a/app/controllers/extra_locales_controller.rb b/app/controllers/extra_locales_controller.rb
index d120fe3f3da..f42aca971fd 100644
--- a/app/controllers/extra_locales_controller.rb
+++ b/app/controllers/extra_locales_controller.rb
@@ -23,50 +23,54 @@ class ExtraLocalesController < ApplicationController
@js_digests ||= { site_specific: {}, shared: {} }
end
- def bundle_js_hash(bundle)
- bundle_key = "#{bundle}_#{I18n.locale}"
+ def bundle_js_hash(bundle, locale:)
+ bundle_key = "#{bundle}_#{locale}"
if bundle.in?(SITE_SPECIFIC_BUNDLES)
site = RailsMultisite::ConnectionManagement.current_db
js_digests[:site_specific][site] ||= {}
js_digests[:site_specific][site][bundle_key] ||= begin
- js = bundle_js(bundle)
+ js = bundle_js(bundle, locale: locale)
js.present? ? digest_for_content(js) : nil
end
elsif bundle.in?(SHARED_BUNDLES)
- js_digests[:shared][bundle_key] ||= digest_for_content(bundle_js(bundle))
+ js_digests[:shared][bundle_key] ||= digest_for_content(bundle_js(bundle, locale: locale))
else
raise "Unknown bundle: #{bundle}"
end
end
- def url(bundle)
+ def url(bundle, locale: I18n.locale)
+ hash = bundle_js_hash(bundle, locale:)
+
base = "#{GlobalSetting.cdn_url}#{Discourse.base_path}"
- path = "/extra-locales/#{bundle_js_hash(bundle)}/#{bundle}"
+ path = "/extra-locales/#{hash}/#{locale}/#{bundle}.js"
query = SITE_SPECIFIC_BUNDLES.include?(bundle) ? "?__ws=#{Discourse.current_hostname}" : ""
"#{base}#{path}#{query}"
end
- def client_overrides_exist?
- bundle_js_hash(OVERRIDES_BUNDLE).present?
+ def client_overrides_exist?(locale: I18n.locale)
+ bundle_js_hash(OVERRIDES_BUNDLE, locale: locale).present?
end
- def bundle_js(bundle)
- locale_str = I18n.locale.to_s
+ def bundle_js(bundle, locale:)
+ locale_str = locale.to_s
bundle_str = "#{bundle}_js"
- case bundle
- when OVERRIDES_BUNDLE
- JsLocaleHelper.output_client_overrides(locale_str)
- when MF_BUNDLE
- JsLocaleHelper.output_MF(locale_str)
- else
- JsLocaleHelper.output_extra_locales(bundle_str, locale_str)
+ I18n.with_locale(locale) do
+ case bundle
+ when OVERRIDES_BUNDLE
+ JsLocaleHelper.output_client_overrides(locale_str)
+ when MF_BUNDLE
+ JsLocaleHelper.output_MF(locale_str)
+ else
+ JsLocaleHelper.output_extra_locales(bundle_str, locale_str)
+ end
end
end
- def bundle_js_with_hash(bundle)
- js = bundle_js(bundle)
+ def bundle_js_with_hash(bundle, locale:)
+ js = bundle_js(bundle, locale: locale)
[js, digest_for_content(js)]
end
@@ -82,14 +86,17 @@ class ExtraLocalesController < ApplicationController
def show
bundle = params[:bundle]
- raise Discourse::InvalidAccess.new if !valid_bundle?(bundle)
+ raise Discourse::NotFound if !valid_bundle?(bundle)
+
+ locale = params[:locale]
+ raise Discourse::NotFound if !I18n.available_locales.include?(locale.to_sym)
digest = params[:digest]
if digest.present?
raise Discourse::InvalidParameters.new(:digest) unless digest.to_s.size == SHA1_HASH_LENGTH
end
- content, hash = ExtraLocalesController.bundle_js_with_hash(bundle)
+ content, hash = ExtraLocalesController.bundle_js_with_hash(bundle, locale:)
immutable_for(1.year) if hash == digest
render plain: content, content_type: "application/javascript"
diff --git a/config/routes.rb b/config/routes.rb
index a426697a5dd..82e03681297 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -460,7 +460,10 @@ Discourse::Application.routes.draw do
get "email/unsubscribed" => "email#unsubscribed", :as => "email_unsubscribed"
post "email/unsubscribe/:key" => "email#perform_unsubscribe", :as => "email_perform_unsubscribe"
- get "extra-locales/:digest/:bundle" => "extra_locales#show"
+ get "extra-locales/:digest/:locale/:bundle" => "extra_locales#show",
+ :constraints => {
+ format: :js,
+ }
resources :session, id: RouteFormat.username, only: %i[create destroy become] do
get "become" if !Rails.env.production?
diff --git a/spec/requests/extra_locales_controller_spec.rb b/spec/requests/extra_locales_controller_spec.rb
index 4a531586130..e0e6c16ddfe 100644
--- a/spec/requests/extra_locales_controller_spec.rb
+++ b/spec/requests/extra_locales_controller_spec.rb
@@ -6,35 +6,35 @@ RSpec.describe ExtraLocalesController do
describe "#show" do
it "won't work with a weird parameter" do
- get "/extra-locales/#{fake_digest}/-invalid..character!!"
+ get "/extra-locales/#{fake_digest}/en/-invalid..character!!.js"
expect(response.status).to eq(404)
end
it "needs a valid bundle" do
- get "/extra-locales/#{fake_digest}/made-up-bundle"
- expect(response.status).to eq(403)
+ get "/extra-locales/#{fake_digest}/en/made-up-bundle.js"
+ expect(response.status).to eq(404)
end
it "requires a valid digest" do
- get "/extra-locales/ZZZ/overrides"
+ get "/extra-locales/ZZZ/en/overrides.js"
expect(response.status).to eq(400)
end
it "caches for 1 year if version is provided and it matches current hash" do
- get "/extra-locales/#{ExtraLocalesController.bundle_js_hash("admin")}/admin"
+ get "/extra-locales/#{ExtraLocalesController.bundle_js_hash("admin", locale: :en)}/en/admin.js"
expect(response.status).to eq(200)
expect(response.headers["Cache-Control"]).to eq("max-age=31556952, public, immutable")
end
it "does not cache at all if version is invalid" do
- get "/extra-locales/#{fake_digest}/admin"
+ get "/extra-locales/#{fake_digest}/en/admin.js"
expect(response.status).to eq(200)
expect(response.headers["Cache-Control"]).not_to include("max-age", "public", "immutable")
end
it "doesn’t generate the bundle twice" do
described_class.expects(:bundle_js).returns("JS").once
- get "/extra-locales/#{fake_digest}/admin"
+ get "/extra-locales/#{fake_digest}/en/admin.js"
end
context "with plugin" do
@@ -60,7 +60,7 @@ RSpec.describe ExtraLocalesController do
after { JsLocaleHelper.clear_cache! }
it "includes plugin translations" do
- get "/extra-locales/#{fake_digest}/admin"
+ get "/extra-locales/#{fake_digest}/en/admin.js"
expect(response.status).to eq(200)
expect(response.body.include?("github_badges")).to eq(true)
end
@@ -72,13 +72,13 @@ RSpec.describe ExtraLocalesController do
it "works for anonymous users" do
TranslationOverride.upsert!(I18n.locale, "js.some_key", "client-side translation")
- get "/extra-locales/#{ExtraLocalesController.bundle_js_hash("overrides")}/overrides"
+ get "/extra-locales/#{ExtraLocalesController.bundle_js_hash("overrides", locale: :en)}/en/overrides.js"
expect(response.status).to eq(200)
expect(response.headers["Cache-Control"]).to eq("max-age=31556952, public, immutable")
end
it "returns nothing when there are not overridden translations" do
- get "/extra-locales/#{fake_digest}/overrides"
+ get "/extra-locales/#{fake_digest}/en/overrides.js"
expect(response.status).to eq(200)
expect(response.body).to be_empty
end
@@ -99,7 +99,7 @@ RSpec.describe ExtraLocalesController do
"{NUM_RESULTS, plural, one {1 result} other {many} }",
)
- get "/extra-locales/#{fake_digest}/overrides"
+ get "/extra-locales/#{fake_digest}/en/overrides.js"
expect(response.status).to eq(200)
expect(response.body).to_not include("server.some_key", "server.some_MF")
@@ -144,12 +144,7 @@ RSpec.describe ExtraLocalesController do
"{NUM_RESULTS, plural, one {1 result} other {many} }",
)
- SiteSetting.allow_user_locale = true
- user = Fabricate(:user, locale: :fr)
- sign_in(user)
-
- get "/extra-locales/#{fake_digest}/overrides"
- expect(response.status).to eq(200)
+ get ExtraLocalesController.url("overrides", locale: :fr)
ctx = MiniRacer::Context.new
ctx.eval("I18n = {};")
@@ -169,7 +164,7 @@ RSpec.describe ExtraLocalesController do
before { JsLocaleHelper.stubs(:output_MF).with("en").returns("MF_TRANSLATIONS") }
it "returns the translations properly" do
- get "/extra-locales/#{fake_digest}/mf"
+ get ExtraLocalesController.url("mf")
expect(response.body).to eq("MF_TRANSLATIONS")
end
end
@@ -177,28 +172,32 @@ RSpec.describe ExtraLocalesController do
describe ".bundle_js_hash" do
it "doesn't call bundle_js more than once for the same locale and bundle" do
- I18n.locale = :de
- ExtraLocalesController.expects(:bundle_js).with("admin").returns("admin_js DE").once
+ locale = :de
+ ExtraLocalesController.expects(:bundle_js).with("admin", locale:).returns("admin_js DE").once
expected_hash_de = Digest::SHA1.hexdigest("admin_js DE")
- expect(ExtraLocalesController.bundle_js_hash("admin")).to eq(expected_hash_de)
- expect(ExtraLocalesController.bundle_js_hash("admin")).to eq(expected_hash_de)
+ expect(ExtraLocalesController.bundle_js_hash("admin", locale:)).to eq(expected_hash_de)
+ expect(ExtraLocalesController.bundle_js_hash("admin", locale:)).to eq(expected_hash_de)
- I18n.locale = :fr
- ExtraLocalesController.expects(:bundle_js).with("admin").returns("admin_js FR").once
+ locale = :fr
+ ExtraLocalesController.expects(:bundle_js).with("admin", locale:).returns("admin_js FR").once
expected_hash_fr = Digest::SHA1.hexdigest("admin_js FR")
- expect(ExtraLocalesController.bundle_js_hash("admin")).to eq(expected_hash_fr)
- expect(ExtraLocalesController.bundle_js_hash("admin")).to eq(expected_hash_fr)
+ expect(ExtraLocalesController.bundle_js_hash("admin", locale:)).to eq(expected_hash_fr)
+ expect(ExtraLocalesController.bundle_js_hash("admin", locale:)).to eq(expected_hash_fr)
- I18n.locale = :de
- expect(ExtraLocalesController.bundle_js_hash("admin")).to eq(expected_hash_de)
+ locale = :de
+ expect(ExtraLocalesController.bundle_js_hash("admin", locale:)).to eq(expected_hash_de)
- ExtraLocalesController.expects(:bundle_js).with("wizard").returns("wizard_js DE").once
+ ExtraLocalesController
+ .expects(:bundle_js)
+ .with("wizard", locale:)
+ .returns("wizard_js DE")
+ .once
expected_hash_de = Digest::SHA1.hexdigest("wizard_js DE")
- expect(ExtraLocalesController.bundle_js_hash("wizard")).to eq(expected_hash_de)
- expect(ExtraLocalesController.bundle_js_hash("wizard")).to eq(expected_hash_de)
+ expect(ExtraLocalesController.bundle_js_hash("wizard", locale:)).to eq(expected_hash_de)
+ expect(ExtraLocalesController.bundle_js_hash("wizard", locale:)).to eq(expected_hash_de)
end
end
@@ -224,10 +223,10 @@ RSpec.describe ExtraLocalesController do
end
describe ".bundle_js_with_hash" do
- before { described_class.stubs(:bundle_js).with("admin").returns("JS") }
+ before { described_class.stubs(:bundle_js).with("admin", locale: :en).returns("JS") }
it "returns both JS and its hash for a given bundle" do
- expect(described_class.bundle_js_with_hash("admin")).to eq(
+ expect(described_class.bundle_js_with_hash("admin", locale: :en)).to eq(
["JS", Digest::SHA1.hexdigest("JS")],
)
end
@@ -235,7 +234,9 @@ RSpec.describe ExtraLocalesController do
describe ".url" do
it "works" do
- expect(ExtraLocalesController.url("admin")).to match(%r{\A/extra-locales/\h{40}\/admin\z})
+ expect(ExtraLocalesController.url("admin")).to match(
+ %r{\A/extra-locales/\h{40}/en/admin\.js\z},
+ )
end
it "includes subfolder path" do
@@ -256,5 +257,12 @@ RSpec.describe ExtraLocalesController do
"https://cdn.example.com/extra-locales/",
)
end
+
+ it "includes locale correctly" do
+ expect(ExtraLocalesController.url("admin")).to include("/en/admin.js")
+ I18n.with_locale(:fr) do
+ expect(ExtraLocalesController.url("admin")).to include("/fr/admin.js")
+ end
+ end
end
end