Skip to content
This repository was archived by the owner on Sep 10, 2024. It is now read-only.

Commit 4a39089

Browse files
committed
Replace i18next backend and detection with our own implementation
This reduces the gzip size of the JS bundle by 10KB
1 parent a037287 commit 4a39089

File tree

3 files changed

+68
-27
lines changed

3 files changed

+68
-27
lines changed

frontend/package-lock.json

Lines changed: 12 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@
3535
"graphql": "^16.8.1",
3636
"history": "^5.3.0",
3737
"i18next": "^23.10.1",
38-
"i18next-browser-languagedetector": "^7.2.0",
39-
"i18next-http-backend": "^2.5.0",
4038
"react": "^18.2.0",
4139
"react-dom": "^18.2.0",
4240
"react-i18next": "^14.1.0",

frontend/src/i18n.ts

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import { default as i18n, InitOptions } from "i18next";
16-
import LanguageDetector, {
17-
DetectorOptions,
18-
} from "i18next-browser-languagedetector";
19-
import I18NextHttpBackend, { HttpBackendOptions } from "i18next-http-backend";
15+
import {
16+
default as i18n,
17+
InitOptions,
18+
LanguageDetectorModule,
19+
BackendModule,
20+
ReadCallback,
21+
ResourceKey,
22+
} from "i18next";
2023
import { initReactI18next } from "react-i18next";
2124

2225
// This generates a map of locale names to their URL (based on import.meta.url), which looks like this:
@@ -30,39 +33,71 @@ const locales = import.meta.glob<string>("../locales/*.json", {
3033
eager: true,
3134
});
3235

33-
const getLocaleUrl = (name: string): string =>
36+
const getLocaleUrl = (name: string): string | undefined =>
3437
locales[`../locales/${name}.json`];
3538

3639
const supportedLngs = Object.keys(locales).map(
3740
(url) => url.match(/\/([^/]+)\.json$/)![1],
3841
);
3942

43+
// A simple language detector that reads the `lang` attribute from the HTML tag
44+
const LanguageDetector = {
45+
type: "languageDetector",
46+
47+
detect(): string | undefined {
48+
const htmlTag =
49+
typeof document !== "undefined" ? document.documentElement : null;
50+
51+
if (htmlTag && typeof htmlTag.getAttribute === "function") {
52+
return htmlTag.getAttribute("lang") || undefined;
53+
}
54+
},
55+
} satisfies LanguageDetectorModule;
56+
57+
// A backend that fetches the locale files from the URLs generated by the glob above
58+
const Backend = {
59+
type: "backend",
60+
init(): void {},
61+
read(language: string, _namespace: string, callback: ReadCallback): void {
62+
(async function (): Promise<ResourceKey> {
63+
const url = getLocaleUrl(language);
64+
if (!url) {
65+
throw new Error(`Locale ${language} not found`);
66+
}
67+
68+
const response = await fetch(url, {
69+
credentials: "omit",
70+
headers: {
71+
Accept: "application/json",
72+
},
73+
});
74+
75+
if (!response.ok) {
76+
throw Error(`Failed to fetch ${url}`);
77+
}
78+
79+
// XXX: we don't check the JSON shape here, which should be fine
80+
return await response.json();
81+
})().then(
82+
(data) => callback(null, data),
83+
(error) => callback(error, null),
84+
);
85+
},
86+
} satisfies BackendModule;
87+
4088
i18n
41-
.use(I18NextHttpBackend)
89+
.use(Backend)
4290
.use(LanguageDetector)
4391
.use(initReactI18next)
4492
.init({
4593
fallbackLng: "en",
4694
keySeparator: ".",
4795
pluralSeparator: ":",
4896
supportedLngs,
49-
detection: {
50-
// This lets the backend fully decide the language to use
51-
order: ["htmlTag"],
52-
} satisfies DetectorOptions,
5397
interpolation: {
5498
escapeValue: false, // React has built-in XSS protections
5599
},
56-
backend: {
57-
crossDomain: true,
58-
loadPath(lngs: string[], _ns: string[]): string {
59-
return getLocaleUrl(lngs[0]);
60-
},
61-
requestOptions: {
62-
credentials: "same-origin",
63-
},
64-
},
65-
} satisfies InitOptions<HttpBackendOptions>);
100+
} satisfies InitOptions);
66101

67102
import.meta.hot?.on("locales-update", () => {
68103
i18n.reloadResources().then(() => {

0 commit comments

Comments
 (0)