diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index be4c6983c0a..b5afb073f27 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -116,6 +116,8 @@ function i18nMoneyFormatter(amount: any): string { return `@[MONEY]{${i18next.t("common:money", { amount })}}`; } +const nsEn = []; + //#region Exports /** @@ -166,95 +168,7 @@ export async function initI18n(): Promise { }, }, defaultNS: "menu", - ns: [ - "ability", - "abilityTriggers", - "arenaFlyout", - "arenaTag", - "battle", - "battleScene", - "battleInfo", - "battleMessageUiHandler", - "battlePokemonForm", - "battlerTags", - "berry", - "bgmName", - "biome", - "challenges", - "commandUiHandler", - "common", - "achv", - "dialogue", - "battleSpecDialogue", - "miscDialogue", - "doubleBattleDialogue", - "egg", - "fightUiHandler", - "filterBar", - "gameMode", - "gameStatsUiHandler", - "growth", - "menu", - "menuUiHandler", - "modifier", - "modifierType", - "move", - "nature", - "pokeball", - "pokemon", - "pokemonForm", - "pokemonInfo", - "pokemonInfoContainer", - "pokemonSummary", - "saveSlotSelectUiHandler", - "settings", - "splashMessages", - "starterSelectUiHandler", - "statusEffect", - "terrain", - "titles", - "trainerClasses", - "trainerNames", - "tutorial", - "voucher", - "weather", - "partyUiHandler", - "modifierSelectUiHandler", - "moveTriggers", - "runHistory", - "mysteryEncounters/mysteriousChallengers", - "mysteryEncounters/mysteriousChest", - "mysteryEncounters/darkDeal", - "mysteryEncounters/fightOrFlight", - "mysteryEncounters/slumberingSnorlax", - "mysteryEncounters/trainingSession", - "mysteryEncounters/departmentStoreSale", - "mysteryEncounters/shadyVitaminDealer", - "mysteryEncounters/fieldTrip", - "mysteryEncounters/safariZone", - "mysteryEncounters/lostAtSea", - "mysteryEncounters/fieryFallout", - "mysteryEncounters/theStrongStuff", - "mysteryEncounters/thePokemonSalesman", - "mysteryEncounters/anOfferYouCantRefuse", - "mysteryEncounters/delibirdy", - "mysteryEncounters/absoluteAvarice", - "mysteryEncounters/aTrainersTest", - "mysteryEncounters/trashToTreasure", - "mysteryEncounters/berriesAbound", - "mysteryEncounters/clowningAround", - "mysteryEncounters/partTimer", - "mysteryEncounters/dancingLessons", - "mysteryEncounters/weirdDream", - "mysteryEncounters/theWinstrateChallenge", - "mysteryEncounters/teleportingHijinks", - "mysteryEncounters/bugTypeSuperfan", - "mysteryEncounters/funAndGames", - "mysteryEncounters/uncommonBreed", - "mysteryEncounters/globalTradeSystem", - "mysteryEncounters/theExpertPokemonBreeder", - "mysteryEncounterMessages", - ], + ns: nsEn, // assign with namespaces-i18n-plugin.ts detection: { lookupLocalStorage: "prLang" }, diff --git a/src/plugins/vite/namespaces-i18n-plugin.ts b/src/plugins/vite/namespaces-i18n-plugin.ts new file mode 100644 index 00000000000..24e7734b94c --- /dev/null +++ b/src/plugins/vite/namespaces-i18n-plugin.ts @@ -0,0 +1,85 @@ +import { normalizePath, type Plugin as VitePlugin } from "vite"; +import fs from "fs"; +import path from "path"; + +function kebabCaseToCamelCase(str: string): string { + return str.split("-").map((text, index) => { + if (index > 0) { + return text.split("").map((char, i) => i === 0 ? char.toUpperCase() : char).join(""); + } + return text; + }).join(""); +} + +function getNameSpaces(dir: string) { + const namespace: string[] = []; + const files = fs.readdirSync(dir); + + for (const file of files) { + const filePath = path.join(dir, file); + const stat = fs.lstatSync(filePath); + + if (stat.isDirectory()) { + const subnamespace = getNameSpaces(filePath); + for (let i = 0; i < subnamespace.length; i++) { + let ns = subnamespace[i]; + if (kebabCaseToCamelCase(file).replace(".json", "").startsWith("mysteryEncounters")) { + ns = subnamespace[i].replace(/Dialogue$/, ""); + } + namespace.push(`${kebabCaseToCamelCase(file).replace(".json", "")}/${ns}`); + } + } else if (path.extname(file) === ".json") { + namespace.push(kebabCaseToCamelCase(file).replace(".json", "")); + } + } + + return namespace; +} + +function isFileInsideDir(file, dir) { + const filePath = path.normalize(file); + const dirPath = path.normalize(dir); + return filePath.startsWith(dirPath); +} + +export function LocaleNamespace(): VitePlugin { + const nsLocation = "./public/locales"; + const nsEn = `${nsLocation}/en`; // Default namespace + let namespaces = getNameSpaces(nsEn); + // const nsEnRegex = new RegExp(`^${nsEn.replace(/\//g, "\\/")}.*\\.json$`); + const nsAbsolutePath = path.resolve(process.cwd(), nsLocation); // Convert to absolute path + + return { + name: "namespaces-i18next", + buildStart() { + if (process.env.NODE_ENV === "production") { + console.log("Assign namespace to constant nsEn"); + } + }, + configureServer(server) { + const restartHandler = async (file, action: string) => { + if (isFileInsideDir(file, nsAbsolutePath) && file.endsWith(".json")) { + console.log(`\x1b[34m${normalizePath(file.replace(nsAbsolutePath, ""))}\x1b[0m ${action}, reloading page...`); + + namespaces = await getNameSpaces(nsEn); + await server.moduleGraph.invalidateAll(); + await server.ws.send({ + type: "full-reload", + }); + } + }; + server.watcher.on("change", (file) => restartHandler(file, "updated")); + server.watcher.on("add", (file) => restartHandler(file, "added")); + server.watcher.on("unlink", (file) => restartHandler(file, "removed")); + }, + transform: { + handler(code, id) { + if (id.endsWith("i18n.ts")) { + return code.replace("const nsEn = [];", `const nsEn = ${JSON.stringify(namespaces)};`); + } + return code; + }, + }, + + }; +} diff --git a/vite.config.ts b/vite.config.ts index 946315c4b7b..2888f1ed510 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,11 +1,13 @@ import { defineConfig, loadEnv, Rollup, UserConfig } from 'vite'; import tsconfigPaths from 'vite-tsconfig-paths'; import { minifyJsonPlugin } from "./src/plugins/vite/vite-minify-json-plugin"; +import { LocaleNamespace } from "./src/plugins/vite/namespaces-i18n-plugin"; export const defaultConfig: UserConfig = { plugins: [ tsconfigPaths(), - minifyJsonPlugin(["images", "battle-anims"], true) + minifyJsonPlugin(["images", "battle-anims"], true), + LocaleNamespace() ], clearScreen: false, appType: "mpa",