perf: merge all namespace of i18n resource in prod

Signed-off-by: Innei <i@innei.in>
This commit is contained in:
Innei 2024-09-13 20:02:34 +08:00
parent 304cb7f59f
commit 92bfce0f7e
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
4 changed files with 71 additions and 38 deletions

View File

@ -1,5 +1,5 @@
import { readFileSync } from "node:fs"
import { resolve } from "node:path"
import fs, { readFileSync } from "node:fs"
import path, { resolve } from "node:path"
import * as babel from "@babel/core"
import generate from "@babel/generator"
@ -14,6 +14,51 @@ import { getGitHash } from "../scripts/lib"
const pkg = JSON.parse(readFileSync("package.json", "utf8"))
const isCI = process.env.CI === "true" || process.env.CI === "1"
function localesPlugin(): Plugin {
return {
name: "locales-merge",
enforce: "post",
generateBundle(options, bundle) {
const localesDir = path.resolve(__dirname, "locales")
const namespaces = fs.readdirSync(localesDir)
const languageResources = {}
namespaces.forEach((namespace) => {
const namespacePath = path.join(localesDir, namespace)
const files = fs.readdirSync(namespacePath).filter((file) => file.endsWith(".json"))
files.forEach((file) => {
const lang = path.basename(file, ".json")
const filePath = path.join(namespacePath, file)
const content = JSON.parse(fs.readFileSync(filePath, "utf-8"))
if (!languageResources[lang]) {
languageResources[lang] = {}
}
languageResources[lang][namespace] = content
})
})
Object.entries(languageResources).forEach(([lang, resources]) => {
const fileName = `locales/${lang}.js`
const content = `export default ${JSON.stringify(resources)};`
this.emitFile({
type: "asset",
fileName,
source: content,
})
})
// Remove original JSON chunks
Object.keys(bundle).forEach((key) => {
if (key.startsWith("locales/") && key.endsWith(".json")) {
delete bundle[key]
}
})
},
}
}
export const viteRenderBaseConfig = {
resolve: {
@ -52,6 +97,7 @@ export const viteRenderBaseConfig = {
},
}),
localesPlugin(),
viteTwToRawString(),
{

View File

@ -29,16 +29,7 @@ if (import.meta.hot) {
async ({ file, content }: { file: string; content: string }) => {
const resources = JSON.parse(content)
const i18next = jotaiStore.get(i18nAtom)
// `file` is absolute path e.g. /Users/innei/git/follow/locales/en.json
// Absolute path e.g. /Users/innei/git/follow/locales/<module-name>/en.json
// 1. parse root language
// if (!file.includes("locales/namespaces")) {
// const lang = file.split("/").pop()?.replace(".json", "")
// if (!lang) return
// i18next.addResourceBundle(lang, defaultNS, resources, true, true)
// i18next.reloadResources(lang, defaultNS)
// } else {
const nsName = file.match(/locales\/(.+?)\//)?.[1]
if (!nsName) return
@ -48,7 +39,6 @@ if (import.meta.hot) {
console.info("reload", lang, nsName)
await i18next.reloadResources(lang, nsName)
// }
import.meta.env.DEV && EventBus.dispatch("I18N_UPDATE", "")
},

View File

@ -42,37 +42,35 @@ const langChangedHandler = async (lang: string) => {
loadingLangLock.add(lang)
const nsGlobbyMap = import.meta.glob("@locales/*/*.json")
if (import.meta.env.DEV) {
const nsGlobbyMap = import.meta.glob("@locales/*/*.json")
// const rootGlobbyMap = import.meta.glob("@locales/*.json")
// const rootResources = await rootGlobbyMap[`../../locales/${lang}.json`]()
// .then((m: any) => m.default)
// .catch(() => {
// toast.error(`${t("common:tips.load-lng-error")}: ${lang}`)
// })
// i18next.addResourceBundle(lang, defaultNS, rootResources, true, true)
const namespaces = Object.keys(defaultResources.en)
const namespaces = Object.keys(defaultResources.en)
const res = await Promise.allSettled(
namespaces.map(async (ns) => {
const loader = nsGlobbyMap[`../../locales/${ns}/${lang}.json`]
const res = await Promise.allSettled(
namespaces.map(async (ns) => {
// if (ns === defaultNS) return
if (!loader) return
const nsResources = await loader().then((m: any) => m.default)
const loader = nsGlobbyMap[`../../locales/${ns}/${lang}.json`]
i18next.addResourceBundle(lang, ns, nsResources, true, true)
}),
)
if (!loader) return
const nsResources = await loader().then((m: any) => m.default)
for (const r of res) {
if (r.status === "rejected") {
toast.error(`${t("common:tips.load-lng-error")}: ${lang}`)
loadingLangLock.delete(lang)
i18next.addResourceBundle(lang, ns, nsResources, true, true)
}),
)
return
}
}
} else {
const res = await eval(`import('/locales/${lang}.js').then((res) => res?.default || res)`)
for (const r of res) {
if (r.status === "rejected") {
toast.error(`${t("common:tips.load-lng-error")}: ${lang}`)
loadingLangLock.delete(lang)
return
for (const namespace in res) {
i18next.addResourceBundle(lang, namespace, res[namespace], true, true)
}
}

View File

@ -79,7 +79,7 @@ export default ({ mode }) => {
__debug_proxy: resolve(ROOT, "/__debug_proxy.html"),
},
output: {
experimentalMinChunkSize: 500_000,
// experimentalMinChunkSize: 500_000,
},
},
},
@ -97,7 +97,6 @@ export default ({ mode }) => {
}),
htmlPlugin(typedEnv),
mkcert(),
devPrint(),
process.env.ANALYZER && analyzer(),