PERF: Compile main locale bundles just-in-time (#32335)

Previously all locale bundles would be built & compressed during
assets:precompile. For most sites, only one of these languages was
actually used, so this is fairly wasteful.

This commit moves the main locale bundle into the
ExtraLocalesController, which has recently undergone many improvements
to make it more efficient. This allows locale files to be bundled "just
in time" when they're first accessed.

Now that brotli level=6 is enabled for these assets in our nginx config,
this change should have no impact on the locale bundle size.
This commit is contained in:
David Taylor 2025-04-28 10:31:27 +01:00 committed by GitHub
parent 3b9d9f5893
commit c62a4a4759
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
68 changed files with 37 additions and 317 deletions

View File

@ -117,12 +117,6 @@ gem "net-imap", require: false
gem "net-pop", require: false
gem "digest", require: false
# Gems used only for assets and not required in production environments by default.
# Allow everywhere for now cause we are allowing asset debugging in production
group :assets do
gem "uglifier"
end
group :test do
gem "capybara", require: false
gem "webmock", require: false

View File

@ -610,8 +610,6 @@ GEM
concurrent-ruby (~> 1.0)
tzinfo-data (1.2025.2)
tzinfo (>= 1.0.0)
uglifier (4.2.1)
execjs (>= 0.3.0, < 3)
unf (0.2.0)
unicode-display_width (3.1.4)
unicode-emoji (~> 4.0, >= 4.0.4)
@ -791,7 +789,6 @@ DEPENDENCIES
thor
trilogy
tzinfo-data
uglifier
unf
unicorn
web-push
@ -1085,7 +1082,6 @@ CHECKSUMS
trilogy (2.9.0) sha256=a2d63b663ba68a4758e15d1f9afb228f5d16efc7fe7cea68699e1c106ef6067f
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
tzinfo-data (1.2025.2) sha256=a92375a1fbb47d38fe88fd514c40a38cc8f97d168da2a6479f15185e86470939
uglifier (4.2.1) sha256=75d42b81b10bfd21e7a427fabb1d49ff5ea7bda3c4a5039ddb2a78d194c6f5aa
unf (0.2.0) sha256=e6bcc2e101d80e3f9459753db747d5926aada1aaaf61e629e93359da9a5b04ab
unicode-display_width (3.1.4) sha256=8caf2af1c0f2f07ec89ef9e18c7d88c2790e217c482bfc78aaa65eadd5415ac1
unicode-emoji (4.0.4) sha256=2c2c4ef7f353e5809497126285a50b23056cc6e61b64433764a35eff6c36532a

View File

@ -216,9 +216,6 @@ if (themeTestPages) {
} else {
// Running with ember cli, but we want to pass through plugin request to Rails
module.exports.proxies = {
"/assets/locales/*.js": {
target,
},
"/assets/plugins/*_extra.js": {
target,
},

View File

@ -53,7 +53,7 @@
<script src="{{rootURL}}assets/discourse.js"></script>
<script src="{{rootURL}}assets/locales/en.js" data-embroider-ignore></script>
<script src="{{rootURL}}extra-locales/0000000000000000000000000000000000000000/en/main.js" data-embroider-ignore></script>
<script src="{{rootURL}}extra-locales/0000000000000000000000000000000000000000/en/mf.js" data-embroider-ignore></script>
<script src="{{rootURL}}extra-locales/0000000000000000000000000000000000000000/en/admin.js" data-embroider-ignore></script>
<script src="{{rootURL}}extra-locales/0000000000000000000000000000000000000000/en/wizard.js" data-embroider-ignore></script>

View File

@ -1,11 +0,0 @@
//= depend_on 'client.ar.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:ar) %>
// original moment.js implementation can be found here:
// https://github.com/moment/moment/blob/b7ec8e2ec068e03de4f832f28362675bb9e02261/locale/ar.js#L185-L191
moment.updateLocale("ar", {
postformat(string) {
return string;
}
});

View File

@ -1,3 +0,0 @@
//= depend_on 'client.be.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:be) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.bg.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:bg) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.bs_BA.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:bs_BA) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.ca.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:ca) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.cs.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:cs) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.da.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:da) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.de.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:de) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.el.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:el) %>

View File

@ -1,5 +0,0 @@
//= depend_on 'client.en.yml'
//= require locales/i18n
<% JsLocaleHelper.reloadable_plugins(:en, self) %>
<%= JsLocaleHelper.output_locale(:en) %>

View File

@ -1,6 +0,0 @@
//= depend_on 'client.en_GB.yml'
//= depend_on 'client.en.yml'
//= require locales/i18n
<% JsLocaleHelper.reloadable_plugins(:en_GB, self) %>
<%= JsLocaleHelper.output_locale(:en_GB) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.es.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:es) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.et.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:et) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.fa_IR.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:fa_IR) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.fi.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:fi) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.fr.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:fr) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.gl.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:gl) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.he.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:he) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.hr.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:hr) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.hu.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:hu) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.hy.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:hy) %>

View File

@ -1,2 +0,0 @@
require("discourse/loader-shims");
require("discourse-i18n");

View File

@ -1,3 +0,0 @@
//= depend_on 'client.id.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:id) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.it.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:it) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.ja.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:ja) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.ko.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:ko) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.lt.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:lt) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.lv.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:lv) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.nb_NO.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:nb_NO) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.nl.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:nl) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.pl_PL.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:pl_PL) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.pt.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:pt) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.pt_BR.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:pt_BR) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.ro.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:ro) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.ru.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:ru) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.sk.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:sk) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.sl.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:sl) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.sq.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:sq) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.sr.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:sr) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.sv.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:sv) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.sw.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:sw) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.te.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:te) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.th.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:th) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.tr_TR.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:tr_TR) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.ug.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:ug) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.uk.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:uk) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.ur.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:ur) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.vi.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:vi) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.zh_CN.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:zh_CN) %>

View File

@ -1,3 +0,0 @@
//= depend_on 'client.zh_TW.yml'
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:zh_TW) %>

View File

@ -11,12 +11,13 @@ class ExtraLocalesController < ApplicationController
OVERRIDES_BUNDLE = "overrides"
SHA1_HASH_LENGTH = 40
MAIN_BUNDLE = "main"
MF_BUNDLE = "mf"
ADMIN_BUNDLE = "admin"
WIZARD_BUNDLE = "wizard"
SITE_SPECIFIC_BUNDLES = [OVERRIDES_BUNDLE, MF_BUNDLE]
SHARED_BUNDLES = [ADMIN_BUNDLE, WIZARD_BUNDLE]
SHARED_BUNDLES = [MAIN_BUNDLE, ADMIN_BUNDLE, WIZARD_BUNDLE]
class << self
def js_digests
@ -63,6 +64,8 @@ class ExtraLocalesController < ApplicationController
JsLocaleHelper.output_client_overrides(locale_str)
when MF_BUNDLE
JsLocaleHelper.output_MF(locale_str)
when MAIN_BUNDLE
JsLocaleHelper.output_locale(locale_str)
else
JsLocaleHelper.output_extra_locales(bundle_str, locale_str)
end

View File

@ -36,7 +36,7 @@
<%= preload_script file %>
<%- end %>
<%= preload_script "locales/#{I18n.locale}" %>
<%= preload_script_url ExtraLocalesController.url("main") %>
<%= preload_script_url ExtraLocalesController.url("mf") %>
<%- if ExtraLocalesController.client_overrides_exist? %>
<%= preload_script_url ExtraLocalesController.url("overrides") %>

View File

@ -10,7 +10,7 @@
<%= preload_script "test-support" %>
<%= preload_script "discourse" %>
<%= preload_script "test" %>
<%= preload_script "locales/#{I18n.locale}" %>
<%= preload_script_url ExtraLocalesController.url("main") %>
<%= preload_script_url ExtraLocalesController.url("mf") %>
<%= preload_script "admin" %>
<%- Discourse.find_plugin_js_assets(include_disabled: true).each do |file| %>

View File

@ -117,9 +117,6 @@ module Discourse
# :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Allows us to skip minification on some files
config.assets.skip_minification = []
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
config.time_zone = "UTC"

View File

@ -9,7 +9,6 @@ Rails.application.config.assets.enabled = true
Rails.application.config.assets.version = "2-#{GlobalSetting.asset_url_salt}"
# Add additional assets to the asset load path.
Rails.application.config.assets.paths << "#{Rails.root}/config/locales"
Rails.application.config.assets.paths << "#{Rails.root}/public/javascripts"
# Precompile additional assets.
@ -23,29 +22,12 @@ Rails.application.config.assets.precompile += [
end,
]
Rails.application.config.assets.precompile += %w[
break_string.js
locales/i18n.js
scripts/discourse-test-listen-boot
]
Rails.application.config.assets.precompile += %w[break_string.js scripts/discourse-test-listen-boot]
Rails.application.config.assets.precompile << lambda do |logical_path, filename|
filename.start_with?(EmberCli.dist_dir) && EmberCli.assets.include?(logical_path)
end
# Precompile all available locales
unless GlobalSetting.try(:omit_base_locales)
Dir
.glob("#{Rails.root}/app/assets/javascripts/locales/*.js.erb")
.each do |file|
Rails
.application
.config
.assets
.precompile << "locales/#{file.match(/([a-z_A-Z]+\.js)\.erb$/)[1]}"
end
end
# out of the box sprockets 3 grabs loose files that are hanging in assets,
# the exclusion list does not include hbs so you double compile all this stuff
Rails.application.config.assets.precompile.delete(Sprockets::Railtie::LOOSE_APP_ASSETS)

View File

@ -187,7 +187,10 @@ module JsLocaleHelper
translations = translations_for(locale_str)
remove_message_formats!(translations, locale)
result = +""
result = +<<~JS
require("discourse/loader-shims");
require("discourse-i18n");
JS
translations.keys.each do |l|
translations[l].keys.each { |k| translations[l].delete(k) unless k == "js" }

View File

@ -1429,7 +1429,6 @@ class Plugin::Instance
""
opts[:server_locale_file] = Dir["#{root_path}/config/locales/server*.#{locale}.yml"].first ||
""
opts[:js_locale_file] = File.join(root_path, "assets/locales/#{locale}.js.erb")
locale_chain = opts[:fallbackLocale] ? [locale, opts[:fallbackLocale]] : [locale]
lib_locale_path = File.join(root_path, "lib/javascripts/locale")
@ -1522,8 +1521,7 @@ class Plugin::Instance
def valid_locale?(custom_locale)
File.exist?(custom_locale[:client_locale_file]) &&
File.exist?(custom_locale[:server_locale_file]) &&
File.exist?(custom_locale[:js_locale_file]) && custom_locale[:moment_js]
File.exist?(custom_locale[:server_locale_file]) && custom_locale[:moment_js]
end
def find_locale_file(locale_chain, path)

View File

@ -34,7 +34,6 @@ task "assets:precompile:before": %w[
assets:precompile:prereqs
assets:precompile:build
] do
require "uglifier"
require "open3"
# Ensure we ALWAYS do a clean build
@ -42,13 +41,8 @@ task "assets:precompile:before": %w[
STDERR.puts "Purging temp files"
`rm -fr #{Rails.root}/tmp/cache`
$node_compress = !ENV["SKIP_NODE_UGLIFY"]
unless ENV["USE_SPROCKETS_UGLIFY"]
$bypass_sprockets_uglify = true
Rails.configuration.assets.js_compressor = nil
Rails.configuration.assets.gzip = false
end
Rails.configuration.assets.js_compressor = nil
Rails.configuration.assets.gzip = false
STDERR.puts "Bundling assets"
@ -127,54 +121,12 @@ def cdn_relative_path(p)
global_path_klass.cdn_relative_path(p)
end
def compress_node(from, to)
to_path = "#{assets_path}/#{to}"
assets = cdn_relative_path("/assets")
assets_additional_path = (d = File.dirname(from)) == "." ? "" : "/#{d}"
source_map_root = assets + assets_additional_path
source_map_url = "#{File.basename(to)}.map"
base_source_map = assets_path + assets_additional_path
cmd = <<~SH
pnpm terser '#{assets_path}/#{from}' -m -c -o '#{to_path}' --source-map "base='#{base_source_map}',root='#{source_map_root}',url='#{source_map_url}',includeSources=true"
SH
STDERR.puts cmd
result = `#{cmd} 2>&1`
unless $?.success?
STDERR.puts result
exit 1
end
result
end
def compress_ruby(from, to)
data = File.read("#{assets_path}/#{from}")
uglified, map =
Uglifier.new(
comments: :none,
source_map: {
filename: File.basename(from),
output_filename: File.basename(to),
},
).compile_with_map(data)
dest = "#{assets_path}/#{to}"
File.write(dest, uglified << "\n//# sourceMappingURL=#{cdn_path "/assets/#{to}.map"}")
File.write(dest + ".map", map)
GC.start
end
def gzip(path)
STDERR.puts "gzip -f -c -9 #{path} > #{path}.gz"
STDERR.puts `gzip -f -c -9 #{path} > #{path}.gz`.strip
raise "gzip compression failed: exit code #{$?.exitstatus}" if $?.exitstatus != 0
end
# different brotli versions use different parameters
def brotli_command(path)
compression_quality = ENV["DISCOURSE_ASSETS_PRECOMPILE_DEFAULT_BROTLI_QUALITY"] || "6"
"brotli -f --quality=#{compression_quality} #{path} --output=#{path}.br"
@ -188,21 +140,6 @@ def brotli(path)
raise "chmod failed: exit code #{$?.exitstatus}" if $?.exitstatus != 0
end
def max_compress?(path, locales)
return false if Rails.configuration.assets.skip_minification.include? path
return false if EmberCli.is_ember_cli_asset?(path)
return true if path.exclude? "locales/"
path_locale = path.delete_prefix("locales/").delete_suffix(".js")
return true if locales.include? path_locale
false
end
def compress(from, to)
$node_compress ? compress_node(from, to) : compress_ruby(from, to)
end
def concurrent?
if ENV["SPROCKETS_CONCURRENT"] == "1"
concurrent_compressors = []
@ -249,30 +186,13 @@ task "assets:precompile:compress_js": "environment" do
.select { |k, v| k =~ /\.js\z/ }
.each do |file, info|
path = "#{assets_path}/#{file}"
_file =
(
if (d = File.dirname(file)) == "."
"_#{file}"
else
"#{d}/_#{File.basename(file)}"
end
)
_path = "#{assets_path}/#{_file}"
max_compress = max_compress?(info["logical_path"], locales)
if File.exist?(_path)
STDERR.puts "Skipping: #{file} already compressed"
elsif file.include? "discourse/tests"
if file.include? "discourse/tests"
STDERR.puts "Skipping: #{file}"
else
proc.call do
log_task_duration(file) do
STDERR.puts "Compressing: #{file}"
if max_compress
FileUtils.mv(path, _path)
compress(_file, file)
end
info["size"] = File.size(path)
info["mtime"] = File.mtime(path).iso8601
gzip(path)

View File

@ -1,2 +0,0 @@
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:foo_BAR) %>

View File

@ -1,2 +0,0 @@
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:tlh) %>

View File

@ -1,2 +0,0 @@
//= require locales/i18n
<%= JsLocaleHelper.output_locale(:tup) %>

View File

@ -22,12 +22,9 @@ RSpec.describe JsLocaleHelper do
ctx.eval <<~JS
define("discourse/loader-shims", () => {})
define("discourse/lib/load-moment", () => {})
moment = { defineLocale: () => {}, fn: {}, tz: {} }
require("discourse-i18n");
globalThis.moment = { defineLocale: () => {}, fn: {}, tz: {} }
JS
# As there are circular references in the return value, this raises an
# error if we let MiniRacer try to convert the value to JSON. Forcing
# returning `null` from `#eval` will prevent that.
ctx.eval("#{File.read("#{Rails.root}/app/assets/javascripts/locales/i18n.js")};null")
ctx
end

View File

@ -607,7 +607,6 @@ TEXT
config/locales/client.foo_BAR.yml
config/locales/server.foo_BAR.yml
lib/javascripts/locale/moment_js/foo_BAR.js
assets/locales/foo_BAR.js.erb
].each do |path|
it "does not register a new locale when #{path} is missing" do
path = "#{plugin_path}/#{path}"

View File

@ -1074,11 +1074,13 @@ RSpec.describe ApplicationController do
{ HTTP_ACCEPT_LANGUAGE: locale }
end
def locale_scripts(body)
def main_locale_scripts(body)
Nokogiri::HTML5
.parse(body)
.css('script[src*="assets/locales/"]')
.map { |script| script.attributes["src"].value }
.css('script[src*="extra-locales/"]')
.filter_map do |script|
script.attributes["src"].to_s[%r{extra-locales/[^/]+/([^/]+)/main.js}, 1]
end
end
context "with allow_user_locale disabled" do
@ -1092,7 +1094,7 @@ RSpec.describe ApplicationController do
it "uses the default locale" do
get "/latest", headers: headers("fr")
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/en.js")
expect(main_locale_scripts(response.body)).to contain_exactly("en")
end
end
@ -1103,7 +1105,7 @@ RSpec.describe ApplicationController do
get "/latest", headers: headers("fr")
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/en.js")
expect(main_locale_scripts(response.body)).to contain_exactly("en")
end
end
end
@ -1121,13 +1123,13 @@ RSpec.describe ApplicationController do
it "uses the locale from the headers" do
get "/latest", headers: headers("fr")
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/fr.js")
expect(main_locale_scripts(response.body)).to contain_exactly("fr")
end
it "doesn't leak after requests" do
get "/latest", headers: headers("fr")
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/fr.js")
expect(main_locale_scripts(response.body)).to contain_exactly("fr")
expect(I18n.locale.to_s).to eq(SiteSettings::DefaultsProvider::DEFAULT_LOCALE)
end
end
@ -1140,7 +1142,7 @@ RSpec.describe ApplicationController do
it "uses the user's preferred locale" do
get "/latest", headers: headers("fr")
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/fr.js")
expect(main_locale_scripts(response.body)).to contain_exactly("fr")
end
it "serves a 404 page in the preferred locale" do
@ -1153,7 +1155,7 @@ RSpec.describe ApplicationController do
it "serves a RenderEmpty page in the preferred locale" do
get "/u/#{user.username}/preferences/interface"
expect(response.status).to eq(200)
expect(response.body).to have_tag("script", with: { src: "/assets/locales/fr.js" })
expect(main_locale_scripts(response.body)).to contain_exactly("fr")
end
end
end
@ -1166,7 +1168,7 @@ RSpec.describe ApplicationController do
get "/latest", headers: headers("zh-CN")
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/zh_CN.js")
expect(main_locale_scripts(response.body)).to contain_exactly("zh_CN")
end
end
@ -1177,7 +1179,7 @@ RSpec.describe ApplicationController do
get "/latest", headers: headers("")
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/en.js")
expect(main_locale_scripts(response.body)).to contain_exactly("en")
end
end
end
@ -1194,7 +1196,7 @@ RSpec.describe ApplicationController do
it "uses the locale from the cookie" do
get "/latest", headers: { Cookie: "locale=es" }
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/es.js")
expect(main_locale_scripts(response.body)).to contain_exactly("es")
expect(I18n.locale.to_s).to eq(SiteSettings::DefaultsProvider::DEFAULT_LOCALE) # doesn't leak after requests
end
end
@ -1203,7 +1205,7 @@ RSpec.describe ApplicationController do
it "returns the locale and region separated by an underscore" do
get "/latest", headers: { Cookie: "locale=zh-CN" }
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/zh_CN.js")
expect(main_locale_scripts(response.body)).to contain_exactly("zh_CN")
end
end
end
@ -1215,7 +1217,7 @@ RSpec.describe ApplicationController do
get "/latest", headers: { Cookie: "" }
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/en.js")
expect(main_locale_scripts(response.body)).to contain_exactly("en")
end
end
end
@ -1232,7 +1234,7 @@ RSpec.describe ApplicationController do
it "uses the locale from the param" do
get "/latest?lang=es"
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/es.js")
expect(main_locale_scripts(response.body)).to contain_exactly("es")
expect(I18n.locale.to_s).to eq(SiteSettings::DefaultsProvider::DEFAULT_LOCALE) # doesn't leak after requests
end
end
@ -1241,7 +1243,7 @@ RSpec.describe ApplicationController do
it "returns the locale and region separated by an underscore" do
get "/latest?lang=zh-CN"
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/zh_CN.js")
expect(main_locale_scripts(response.body)).to contain_exactly("zh_CN")
end
end
end
@ -1253,7 +1255,7 @@ RSpec.describe ApplicationController do
get "/latest"
expect(response.status).to eq(200)
expect(locale_scripts(response.body)).to contain_exactly("/assets/locales/en.js")
expect(main_locale_scripts(response.body)).to contain_exactly("en")
end
end
end