Skip to content

Commit 0ddc179

Browse files
committed
UX: Reduce JS bundle size by loading translation files on demand photoprism#4778
Signed-off-by: Michael Mayer <michael@photoprism.app>
1 parent a125219 commit 0ddc179

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+126
-44
lines changed

frontend/gettext.config.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const glob = require("glob");
2+
const path = require("path");
3+
const poPath = path.resolve(__dirname, "src/locales");
4+
5+
// Generates a list of existing locales based on the files in src/locales.
6+
const languageCodes = glob.sync(poPath + "/*.po").map((filePath) => {
7+
const fileName = path.basename(filePath);
8+
return fileName.replace(".po", "");
9+
});
10+
11+
// Generates one JSON file per locale from the gettext *.po files located in src/locales.
12+
module.exports = {
13+
output: {
14+
path: path.resolve(__dirname, "src/locales"),
15+
potPath: "src/locales/translations.pot",
16+
jsonPath: "json",
17+
locales: languageCodes,
18+
splitJson: true,
19+
flat: true,
20+
},
21+
};

frontend/package-lock.json

+18-18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"testcafe": "testcafe",
2020
"acceptance-local": "testcafe chromium --selector-timeout 5000 -S -s tests/acceptance/screenshots tests/acceptance",
2121
"gettext-extract": "gettext-extract --output src/locales/translations.pot $(find ${SRC:-src} -type f \\( -iname \\*.vue -o -iname \\*.js \\) -not -path src/common/gettext.js)",
22-
"gettext-compile": "gettext-compile --output src/locales/translations.json src/locales/*.po",
22+
"gettext-compile": "vue-gettext-compile",
2323
"dep-list": "npx npm-check-updates"
2424
},
2525
"dependencies": {

frontend/src/common/config.js

+26-5
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ Additional information can be found in our Developer Guide:
2626
import $api from "common/api";
2727
import $event from "common/event";
2828
import * as themes from "options/themes";
29-
import translations from "locales/translations.json";
3029
import { Languages } from "options/options";
3130
import { Photo } from "model/photo";
3231
import { onInit, onSetTheme } from "common/hooks";
@@ -48,7 +47,7 @@ export default class Config {
4847
this.updating = false;
4948

5049
this.$vuetify = null;
51-
this.translations = translations;
50+
this.translations = {};
5251

5352
if (!values || !values.siteTitle) {
5453
// Omit warning in unit tests.
@@ -166,7 +165,7 @@ export default class Config {
166165
return this.updating;
167166
}
168167

169-
setValues(values) {
168+
async setValues(values) {
170169
if (!values || typeof values !== "object") {
171170
return;
172171
}
@@ -185,7 +184,7 @@ export default class Config {
185184

186185
if (values.settings) {
187186
this.setBatchSize(values.settings);
188-
this.setLanguage(values.settings.ui.language, true);
187+
await this.setLanguage(values.settings.ui.language, true);
189188
this.setTheme(values.settings.ui.theme);
190189
}
191190

@@ -417,16 +416,38 @@ export default class Config {
417416
return !this.allowAny(resource, perm);
418417
}
419418

419+
// loadTranslation asynchronously loads the specified locale file.
420+
async loadTranslation(locale) {
421+
if (!locale || (this.translations && this.translations[locale])) {
422+
return;
423+
}
424+
425+
try {
426+
// Dynamically import the translation JSON file.
427+
await import(
428+
/* webpackChunkName: "[request]" */
429+
/* webpackMode: "lazy" */
430+
`../locales/json/${locale}.json`
431+
).then((module) => {
432+
Object.assign(this.translations, module.default);
433+
});
434+
} catch (error) {
435+
console.error(`failed to load translations for locale ${locale}:`, error);
436+
}
437+
}
438+
420439
// setLanguage sets the ISO/IEC 15897 locale,
421440
// e.g. "en" or "zh_TW" (minimum 2 letters).
422-
setLanguage(locale, apply) {
441+
async setLanguage(locale, apply) {
423442
// Skip setting language if no locale is specified.
424443
if (!locale) {
425444
return this;
426445
}
427446

428447
// Apply locale to browser window?
429448
if (apply) {
449+
await this.loadTranslation(locale);
450+
430451
// Update the Accept-Language header for XHR requests.
431452
if ($api) {
432453
$api.defaults.headers.common["Accept-Language"] = locale;

frontend/src/locales/README.md

+3-3

frontend/src/locales/json/af.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/ar.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/be.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/bg.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/ca.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/cs.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/da.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/de.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/el.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/en.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"en":{"Next":"Next"}}

frontend/src/locales/json/es.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/et.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/eu.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/fa.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/fi.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/fr.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/ga.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/he.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/hi.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/hr.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/hu.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/id.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/it.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/ja.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/ko.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/ku.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/lt.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/ms.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/nb.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/nl.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/pl.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/pt.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/pt_BR.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/ro.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/ru.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/sk.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/sl.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/sv.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/th.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/tr.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/uk.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/vi.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/zh.json

+1
Large diffs are not rendered by default.

frontend/src/locales/json/zh_TW.json

+1
Large diffs are not rendered by default.

frontend/src/locales/translations.json

-1
This file was deleted.

frontend/tests/unit/common/config_test.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -290,22 +290,22 @@ describe("common/config", () => {
290290
assert.equal(cfg.values.count.all, 139);
291291
});
292292

293-
it("should return user interface direction string", () => {
293+
it("should return user interface direction string", async () => {
294294
const cfg = new Config(new StorageShim(), Object.assign({}, window.__CONFIG__));
295-
cfg.setLanguage("en", true);
295+
await cfg.setLanguage("en", true);
296296
assert.equal(document.dir, "ltr", "document.dir should be ltr");
297297
assert.equal(cfg.dir(), "ltr");
298298
assert.equal(cfg.dir(true), "rtl");
299299
assert.equal(cfg.dir(false), "ltr");
300-
cfg.setLanguage("he", false);
300+
await cfg.setLanguage("he", false);
301301
assert.equal(document.dir, "ltr", "document.dir should still be ltr");
302-
cfg.setLanguage("he", true);
302+
await cfg.setLanguage("he", true);
303303
assert.equal(cfg.dir(), "rtl");
304304
assert.equal(document.dir, "rtl", "document.dir should now be rtl");
305305
assert.equal(cfg.dir(), "rtl");
306306
assert.equal(cfg.dir(true), "rtl");
307307
assert.equal(cfg.dir(false), "ltr");
308-
cfg.setLanguage("en", true);
308+
await cfg.setLanguage("en", true);
309309
assert.equal(document.dir, "ltr", "document.dir should be ltr again");
310310
assert.equal(cfg.dir(), "ltr");
311311
});

frontend/webpack.config.js

+8-11
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ const PATHS = {
4646
share: path.join(__dirname, "src/share.js"),
4747
splash: path.join(__dirname, "src/splash.js"),
4848
build: path.join(__dirname, "../assets/static/build"),
49-
public: "./",
5049
};
5150

5251
if (isCustom) {
@@ -72,8 +71,10 @@ const config = {
7271
},
7372
output: {
7473
path: PATHS.build,
75-
publicPath: PATHS.public,
74+
publicPath: "auto",
7675
filename: "[name].[contenthash].js",
76+
chunkFilename: "chunk/[name].[contenthash].js",
77+
asyncChunks: true,
7778
clean: true,
7879
},
7980
resolve: {
@@ -147,16 +148,18 @@ const config = {
147148
},
148149
],
149150
},
151+
{
152+
test: /\.json$/,
153+
include: PATHS.src,
154+
type: "json",
155+
},
150156
{
151157
test: /\.css$/,
152158
include: isCustom ? [PATHS.custom, PATHS.css] : [PATHS.css],
153159
exclude: /node_modules/,
154160
use: [
155161
{
156162
loader: MiniCssExtractPlugin.loader,
157-
options: {
158-
publicPath: PATHS.public,
159-
},
160163
},
161164
{
162165
loader: "css-loader",
@@ -183,9 +186,6 @@ const config = {
183186
use: [
184187
{
185188
loader: MiniCssExtractPlugin.loader,
186-
options: {
187-
publicPath: PATHS.public,
188-
},
189189
},
190190
{
191191
loader: "css-loader",
@@ -211,9 +211,6 @@ const config = {
211211
use: [
212212
{
213213
loader: MiniCssExtractPlugin.loader,
214-
options: {
215-
publicPath: PATHS.public,
216-
},
217214
},
218215
{
219216
loader: "css-loader",

0 commit comments

Comments
 (0)