discourse/spec/requests/extra_locales_controller_spec.rb
Loïc Guitaut 301713ef96 DEV: Upgrade the MessageFormat library (JS)
This patch upgrades the MessageFormat library to version 3.3.0 from
0.1.5.

Our `I18n.messageFormat` method signature is unchanged, and now uses the
new API under the hood.

We don’t need dedicated locale files for handling pluralization rules
anymore as everything is now included by the library itself.

The compilation of the messages now happens through our
`messageformat-wrapper` gem. It then outputs an ES module that includes
all its needed dependencies.

Most of the changes happen in `JsLocaleHelper` and in the `ExtraLocales`
controller.

A new method called `.output_MF` has been introduced in
`JsLocaleHelper`. It handles all the fetching, compiling and
transpiling to generate the proper MF messages in JS. Overrides and
fallbacks are also handled directly in this method.

The other main change is that now the MF translations are served through
the `ExtraLocales` controller instead of being statically compiled in a
JS file, then having to patch the messages using overrides and
fallbacks. Now the MF translations are just another bundle that is
created on the fly and cached by the client.
2024-07-10 09:51:25 +02:00

254 lines
8.8 KiB
Ruby
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: true
RSpec.describe ExtraLocalesController do
around { |example| allow_missing_translations(&example) }
describe "#show" do
it "won't work with a weird parameter" do
get "/extra-locales/-invalid..character!!"
expect(response.status).to eq(404)
end
it "needs a valid bundle" do
get "/extra-locales/made-up-bundle"
expect(response.status).to eq(403)
end
it "requires staff access" do
get "/extra-locales/admin"
expect(response.status).to eq(403)
get "/extra-locales/wizard"
expect(response.status).to eq(403)
end
it "requires a valid version" do
get "/extra-locales/overrides", params: { v: "a" }
expect(response.status).to eq(400)
get "/extra-locales/overrides?v[foo]=1"
expect(response.status).to eq(400)
end
context "when logged in as a moderator" do
let(:moderator) { Fabricate(:moderator) }
before { sign_in(moderator) }
it "caches for 1 year if version is provided and it matches current hash" do
get "/extra-locales/admin", params: { v: ExtraLocalesController.bundle_js_hash("admin") }
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/admin", params: { v: "a" * 32 }
expect(response.status).to eq(200)
expect(response.headers["Cache-Control"]).not_to include("max-age", "public", "immutable")
end
it "doesnt generate the bundle twice" do
described_class.expects(:bundle_js).returns("JS").once
get "/extra-locales/admin", params: { v: "a" * 32 }
end
context "with plugin" do
before do
JsLocaleHelper.clear_cache!
JsLocaleHelper
.expects(:plugin_translations)
.with(any_of("en", "en_GB"))
.returns(
"admin_js" => {
"admin" => {
"site_settings" => {
"categories" => {
"github_badges" => "GitHub Badges",
},
},
},
},
)
.at_least_once
end
after { JsLocaleHelper.clear_cache! }
it "includes plugin translations" do
get "/extra-locales/admin"
expect(response.status).to eq(200)
expect(response.body.include?("github_badges")).to eq(true)
end
end
end
context "with overridden translations" do
after { I18n.reload! }
it "works for anonymous users" do
TranslationOverride.upsert!(I18n.locale, "js.some_key", "client-side translation")
get "/extra-locales/overrides",
params: {
v: ExtraLocalesController.bundle_js_hash("overrides"),
}
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/overrides"
expect(response.status).to eq(200)
expect(response.body).to be_empty
end
context "with translations" do
it "returns the correct translations" do
TranslationOverride.upsert!(I18n.locale, "js.some_key", "client-side translation")
TranslationOverride.upsert!(
I18n.locale,
"js.client_MF",
"{NUM_RESULTS, plural, one {1 result} other {many} }",
)
TranslationOverride.upsert!(I18n.locale, "admin_js.another_key", "admin client js")
TranslationOverride.upsert!(I18n.locale, "server.some_key", "server-side translation")
TranslationOverride.upsert!(
I18n.locale,
"server.some_MF",
"{NUM_RESULTS, plural, one {1 result} other {many} }",
)
get "/extra-locales/overrides"
expect(response.status).to eq(200)
expect(response.body).to_not include("server.some_key", "server.some_MF")
ctx = MiniRacer::Context.new
ctx.eval("I18n = {};")
ctx.eval(response.body)
expect(ctx.eval("I18n._overrides['#{I18n.locale}']['js.some_key']")).to eq(
"client-side translation",
)
expect(ctx.eval("I18n._overrides['#{I18n.locale}']['js.client_MF'] === undefined")).to eq(
true,
)
expect(ctx.eval("I18n._overrides['#{I18n.locale}']['admin_js.another_key']")).to eq(
"admin client js",
)
end
it "returns overrides from fallback locale" do
TranslationOverride.upsert!(:en, "js.some_key", "some key (en)")
TranslationOverride.upsert!(:fr, "js.some_key", "some key (fr)")
TranslationOverride.upsert!(:en, "js.only_en", "only English")
TranslationOverride.upsert!(:fr, "js.only_fr", "only French")
TranslationOverride.upsert!(
:en,
"js.some_client_MF",
"{NUM_RESULTS, plural, one {1 result} other {many} }",
)
TranslationOverride.upsert!(
:fr,
"js.some_client_MF",
"{NUM_RESULTS, plural, one {1 result} other {many} }",
)
TranslationOverride.upsert!(
:en,
"js.only_en_MF",
"{NUM_RESULTS, plural, one {1 result} other {many} }",
)
TranslationOverride.upsert!(
:fr,
"js.only_fr_MF",
"{NUM_RESULTS, plural, one {1 result} other {many} }",
)
SiteSetting.allow_user_locale = true
user = Fabricate(:user, locale: :fr)
sign_in(user)
get "/extra-locales/overrides"
expect(response.status).to eq(200)
ctx = MiniRacer::Context.new
ctx.eval("I18n = {};")
ctx.eval(response.body)
overrides = ctx.eval("I18n._overrides")
expect(overrides.keys).to contain_exactly("en", "fr")
expect(overrides["en"]).to eq({ "js.only_en" => "only English" })
expect(overrides["fr"]).to eq(
{ "js.some_key" => "some key (fr)", "js.only_fr" => "only French" },
)
end
end
end
context "when requesting MessageFormat translations" do
before { JsLocaleHelper.stubs(:output_MF).with("en").returns("MF_TRANSLATIONS") }
it "returns the translations properly" do
get "/extra-locales/mf"
expect(response.body).to eq("MF_TRANSLATIONS")
end
end
end
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
expected_hash_de = Digest::MD5.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)
I18n.locale = :fr
ExtraLocalesController.expects(:bundle_js).with("admin").returns("admin_js FR").once
expected_hash_fr = Digest::MD5.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)
I18n.locale = :de
expect(ExtraLocalesController.bundle_js_hash("admin")).to eq(expected_hash_de)
ExtraLocalesController.expects(:bundle_js).with("wizard").returns("wizard_js DE").once
expected_hash_de = Digest::MD5.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)
end
end
describe ".client_overrides_exist?" do
after do
I18n.reload!
ExtraLocalesController.clear_cache!
end
it "returns false if there are no client-side translation overrides" do
expect(ExtraLocalesController.client_overrides_exist?).to eq(false)
TranslationOverride.upsert!(I18n.locale, "server.some_key", "server-side translation")
expect(ExtraLocalesController.client_overrides_exist?).to eq(false)
end
it "returns true if there are client-side translation overrides" do
expect(ExtraLocalesController.client_overrides_exist?).to eq(false)
TranslationOverride.upsert!(I18n.locale, "js.some_key", "client-side translation")
expect(ExtraLocalesController.client_overrides_exist?).to eq(true)
end
end
describe ".bundle_js_with_hash" do
before { described_class.stubs(:bundle_js).with("admin").returns("JS") }
it "returns both JS and its hash for a given bundle" do
expect(described_class.bundle_js_with_hash("admin")).to eq(
["JS", Digest::MD5.hexdigest("JS")],
)
end
end
end