mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-25 00:39:27 +02:00
implement: i18next language lazy-loading
This commit is contained in:
parent
685bda2b99
commit
2476e70cb6
@ -6,7 +6,6 @@ import { SceneBase } from "./scene-base";
|
|||||||
import { WindowVariant, getWindowVariantSuffix } from "./ui/ui-theme";
|
import { WindowVariant, getWindowVariantSuffix } from "./ui/ui-theme";
|
||||||
import { isMobile } from "./touch-controls";
|
import { isMobile } from "./touch-controls";
|
||||||
import * as Utils from "./utils";
|
import * as Utils from "./utils";
|
||||||
import { initI18n } from "./plugins/i18n";
|
|
||||||
import { initPokemonPrevolutions } from "#app/data/pokemon-evolutions";
|
import { initPokemonPrevolutions } from "#app/data/pokemon-evolutions";
|
||||||
import { initBiomes } from "#app/data/biomes";
|
import { initBiomes } from "#app/data/biomes";
|
||||||
import { initEggMoves } from "#app/data/egg-moves";
|
import { initEggMoves } from "#app/data/egg-moves";
|
||||||
@ -33,7 +32,6 @@ export class LoadingScene extends SceneBase {
|
|||||||
super(LoadingScene.KEY);
|
super(LoadingScene.KEY);
|
||||||
|
|
||||||
Phaser.Plugins.PluginCache.register("Loader", CacheBustedLoaderPlugin, "load");
|
Phaser.Plugins.PluginCache.register("Loader", CacheBustedLoaderPlugin, "load");
|
||||||
initI18n();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
|
100
src/main.ts
100
src/main.ts
@ -1,12 +1,11 @@
|
|||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import BattleScene from "./battle-scene";
|
|
||||||
import InvertPostFX from "./pipelines/invert";
|
import InvertPostFX from "./pipelines/invert";
|
||||||
import { version } from "../package.json";
|
import { version } from "../package.json";
|
||||||
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
|
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
|
||||||
import BBCodeTextPlugin from "phaser3-rex-plugins/plugins/bbcodetext-plugin";
|
import BBCodeTextPlugin from "phaser3-rex-plugins/plugins/bbcodetext-plugin";
|
||||||
import InputTextPlugin from "phaser3-rex-plugins/plugins/inputtext-plugin";
|
import InputTextPlugin from "phaser3-rex-plugins/plugins/inputtext-plugin";
|
||||||
import TransitionImagePackPlugin from "phaser3-rex-plugins/templates/transitionimagepack/transitionimagepack-plugin";
|
import TransitionImagePackPlugin from "phaser3-rex-plugins/templates/transitionimagepack/transitionimagepack-plugin";
|
||||||
import { LoadingScene } from "./loading-scene";
|
import { initI18n } from "./plugins/i18n";
|
||||||
|
|
||||||
|
|
||||||
// Catch global errors and display them in an alert so users can report the issue.
|
// Catch global errors and display them in an alert so users can report the issue.
|
||||||
@ -25,52 +24,6 @@ window.addEventListener("unhandledrejection", (event) => {
|
|||||||
//alert(errorString);
|
//alert(errorString);
|
||||||
});
|
});
|
||||||
|
|
||||||
const config: Phaser.Types.Core.GameConfig = {
|
|
||||||
type: Phaser.WEBGL,
|
|
||||||
parent: "app",
|
|
||||||
scale: {
|
|
||||||
width: 1920,
|
|
||||||
height: 1080,
|
|
||||||
mode: Phaser.Scale.FIT
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
global: [{
|
|
||||||
key: "rexInputTextPlugin",
|
|
||||||
plugin: InputTextPlugin,
|
|
||||||
start: true
|
|
||||||
}, {
|
|
||||||
key: "rexBBCodeTextPlugin",
|
|
||||||
plugin: BBCodeTextPlugin,
|
|
||||||
start: true
|
|
||||||
}, {
|
|
||||||
key: "rexTransitionImagePackPlugin",
|
|
||||||
plugin: TransitionImagePackPlugin,
|
|
||||||
start: true
|
|
||||||
}],
|
|
||||||
scene: [{
|
|
||||||
key: "rexUI",
|
|
||||||
plugin: UIPlugin,
|
|
||||||
mapping: "rexUI"
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
input: {
|
|
||||||
mouse: {
|
|
||||||
target: "app"
|
|
||||||
},
|
|
||||||
touch: {
|
|
||||||
target: "app"
|
|
||||||
},
|
|
||||||
gamepad: true
|
|
||||||
},
|
|
||||||
dom: {
|
|
||||||
createContainer: true
|
|
||||||
},
|
|
||||||
pixelArt: true,
|
|
||||||
pipeline: [ InvertPostFX ] as unknown as Phaser.Types.Core.PipelineConfig,
|
|
||||||
scene: [ LoadingScene, BattleScene ],
|
|
||||||
version: version
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets this object's position relative to another object with a given offset
|
* Sets this object's position relative to another object with a given offset
|
||||||
*/
|
*/
|
||||||
@ -91,8 +44,55 @@ document.fonts.load("16px emerald").then(() => document.fonts.load("10px pkmnems
|
|||||||
|
|
||||||
let game;
|
let game;
|
||||||
|
|
||||||
const startGame = () => {
|
const startGame = async () => {
|
||||||
game = new Phaser.Game(config);
|
await initI18n();
|
||||||
|
const LoadingScene = (await import("./loading-scene")).LoadingScene;
|
||||||
|
const BattleScene = (await import("./battle-scene")).default;
|
||||||
|
game = new Phaser.Game({
|
||||||
|
type: Phaser.WEBGL,
|
||||||
|
parent: "app",
|
||||||
|
scale: {
|
||||||
|
width: 1920,
|
||||||
|
height: 1080,
|
||||||
|
mode: Phaser.Scale.FIT
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
global: [{
|
||||||
|
key: "rexInputTextPlugin",
|
||||||
|
plugin: InputTextPlugin,
|
||||||
|
start: true
|
||||||
|
}, {
|
||||||
|
key: "rexBBCodeTextPlugin",
|
||||||
|
plugin: BBCodeTextPlugin,
|
||||||
|
start: true
|
||||||
|
}, {
|
||||||
|
key: "rexTransitionImagePackPlugin",
|
||||||
|
plugin: TransitionImagePackPlugin,
|
||||||
|
start: true
|
||||||
|
}],
|
||||||
|
scene: [{
|
||||||
|
key: "rexUI",
|
||||||
|
plugin: UIPlugin,
|
||||||
|
mapping: "rexUI"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
mouse: {
|
||||||
|
target: "app"
|
||||||
|
},
|
||||||
|
touch: {
|
||||||
|
target: "app"
|
||||||
|
},
|
||||||
|
gamepad: true
|
||||||
|
},
|
||||||
|
dom: {
|
||||||
|
createContainer: true
|
||||||
|
},
|
||||||
|
pixelArt: true,
|
||||||
|
pipeline: [ InvertPostFX ] as unknown as Phaser.Types.Core.PipelineConfig,
|
||||||
|
scene: [ LoadingScene, BattleScene ],
|
||||||
|
version: version
|
||||||
|
});
|
||||||
game.sound.pauseOnBlur = false;
|
game.sound.pauseOnBlur = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,18 +1,11 @@
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import LanguageDetector from "i18next-browser-languagedetector";
|
import LanguageDetector from "i18next-browser-languagedetector";
|
||||||
import processor, { KoreanPostpositionProcessor } from "i18next-korean-postposition-processor";
|
import processor, { KoreanPostpositionProcessor } from "i18next-korean-postposition-processor";
|
||||||
|
import HttpBackend from "i18next-http-backend";
|
||||||
|
import { camelCaseToKebabCase } from "#app/utils";
|
||||||
|
import pkg from "../../package.json";
|
||||||
|
|
||||||
import { caEsConfig} from "../../public/locales/ca_ES/config";
|
//#region Interfaces/Types
|
||||||
import { deConfig } from "../../public/locales/de/config";
|
|
||||||
import { enConfig } from "../../public/locales/en/config";
|
|
||||||
import { esConfig } from "../../public/locales/es/config";
|
|
||||||
import { frConfig } from "../../public/locales/fr/config";
|
|
||||||
import { itConfig } from "../../public/locales/it/config";
|
|
||||||
import { koConfig } from "../../public/locales/ko/config";
|
|
||||||
import { jaConfig } from "../../public/locales/ja/config";
|
|
||||||
import { ptBrConfig } from "../../public/locales/pt_BR/config";
|
|
||||||
import { zhCnConfig } from "../../public/locales/zh_CN/config";
|
|
||||||
import { zhTwConfig } from "../../public/locales/zh_TW/config";
|
|
||||||
|
|
||||||
interface LoadingFontFaceProperty {
|
interface LoadingFontFaceProperty {
|
||||||
face: FontFace,
|
face: FontFace,
|
||||||
@ -20,6 +13,10 @@ interface LoadingFontFaceProperty {
|
|||||||
only?: Array<string>
|
only?: Array<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#region Constants
|
||||||
|
|
||||||
|
let isInitialized = false;
|
||||||
|
|
||||||
const unicodeRanges = {
|
const unicodeRanges = {
|
||||||
fullwidth: "U+FF00-FFEF",
|
fullwidth: "U+FF00-FFEF",
|
||||||
hangul: "U+1100-11FF,U+3130-318F,U+A960-A97F,U+AC00-D7AF,U+D7B0-D7FF",
|
hangul: "U+1100-11FF,U+3130-318F,U+A960-A97F,U+AC00-D7AF,U+D7B0-D7FF",
|
||||||
@ -28,6 +25,7 @@ const unicodeRanges = {
|
|||||||
CJKIdeograph: "U+4E00-9FFF",
|
CJKIdeograph: "U+4E00-9FFF",
|
||||||
specialCharacters: "U+266A,U+2605,U+2665,U+2663" //♪.★,♥,♣
|
specialCharacters: "U+266A,U+2605,U+2665,U+2663" //♪.★,♥,♣
|
||||||
};
|
};
|
||||||
|
|
||||||
const rangesByLanguage = {
|
const rangesByLanguage = {
|
||||||
korean: [unicodeRanges.CJKCommon, unicodeRanges.hangul].join(","),
|
korean: [unicodeRanges.CJKCommon, unicodeRanges.hangul].join(","),
|
||||||
chinese: [unicodeRanges.CJKCommon, unicodeRanges.fullwidth, unicodeRanges.CJKIdeograph].join(","),
|
chinese: [unicodeRanges.CJKCommon, unicodeRanges.fullwidth, unicodeRanges.CJKIdeograph].join(","),
|
||||||
@ -74,6 +72,19 @@ const fonts: Array<LoadingFontFaceProperty> = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/** maps namespaces that deviate from the file-name */
|
||||||
|
const namespaceMap = {
|
||||||
|
titles: "trainer-titles",
|
||||||
|
moveTriggers: "move-trigger",
|
||||||
|
abilityTriggers: "ability-trigger",
|
||||||
|
battlePokemonForm: "pokemon-form-battle",
|
||||||
|
miscDialogue: "dialogue-misc",
|
||||||
|
battleSpecDialogue: "dialogue-final-boss",
|
||||||
|
doubleBattleDialogue: "dialogue-double-battle",
|
||||||
|
};
|
||||||
|
|
||||||
|
//#region Functions
|
||||||
|
|
||||||
async function initFonts(language: string | undefined) {
|
async function initFonts(language: string | undefined) {
|
||||||
const results = await Promise.allSettled(
|
const results = await Promise.allSettled(
|
||||||
fonts
|
fonts
|
||||||
@ -89,6 +100,12 @@ async function initFonts(language: string | undefined) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#region Exports
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize i18n with fonts
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
export async function initI18n(): Promise<void> {
|
export async function initI18n(): Promise<void> {
|
||||||
// Prevent reinitialization
|
// Prevent reinitialization
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
@ -113,6 +130,7 @@ export async function initI18n(): Promise<void> {
|
|||||||
* A: In src/system/settings.ts, add a new case to the Setting.Language switch statement.
|
* A: In src/system/settings.ts, add a new case to the Setting.Language switch statement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
i18next.use(HttpBackend);
|
||||||
i18next.use(LanguageDetector);
|
i18next.use(LanguageDetector);
|
||||||
i18next.use(processor);
|
i18next.use(processor);
|
||||||
i18next.use(new KoreanPostpositionProcessor());
|
i18next.use(new KoreanPostpositionProcessor());
|
||||||
@ -120,8 +138,112 @@ export async function initI18n(): Promise<void> {
|
|||||||
nonExplicitSupportedLngs: true,
|
nonExplicitSupportedLngs: true,
|
||||||
fallbackLng: "en",
|
fallbackLng: "en",
|
||||||
supportedLngs: ["en", "es", "fr", "it", "de", "zh", "pt", "ko", "ja", "ca"],
|
supportedLngs: ["en", "es", "fr", "it", "de", "zh", "pt", "ko", "ja", "ca"],
|
||||||
|
backend: {
|
||||||
|
loadPath(lng: string, [ ns ]: string[]) {
|
||||||
|
let fileName: string;
|
||||||
|
if (namespaceMap[ns]) {
|
||||||
|
fileName = namespaceMap[ns];
|
||||||
|
} else {
|
||||||
|
fileName = camelCaseToKebabCase(ns);
|
||||||
|
if (fileName.startsWith("mystery-encounters/")) {
|
||||||
|
fileName += "-dialogue";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `/locales/${lng}/${fileName}.json?v=${pkg.version}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
defaultNS: "menu",
|
defaultNS: "menu",
|
||||||
ns: Object.keys(enConfig),
|
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",
|
||||||
|
// DO NOT REMOVE
|
||||||
|
// "mysteryEncounter/unit_test_dialogue": "{{test}}{{test}} {{test{{test}}}} {{test1}} {{test\}} {{test\\}} {{test\\\}} {test}}",
|
||||||
|
"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/pokemonSalesman",
|
||||||
|
"mysteryEncounters/offerYouCantRefuse",
|
||||||
|
"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",
|
||||||
|
"mysteryEncounterMessages",
|
||||||
|
|
||||||
|
],
|
||||||
detection: {
|
detection: {
|
||||||
lookupLocalStorage: "prLang"
|
lookupLocalStorage: "prLang"
|
||||||
},
|
},
|
||||||
@ -129,41 +251,6 @@ export async function initI18n(): Promise<void> {
|
|||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false,
|
escapeValue: false,
|
||||||
},
|
},
|
||||||
resources: {
|
|
||||||
en: {
|
|
||||||
...enConfig
|
|
||||||
},
|
|
||||||
es: {
|
|
||||||
...esConfig
|
|
||||||
},
|
|
||||||
fr: {
|
|
||||||
...frConfig
|
|
||||||
},
|
|
||||||
it: {
|
|
||||||
...itConfig
|
|
||||||
},
|
|
||||||
de: {
|
|
||||||
...deConfig
|
|
||||||
},
|
|
||||||
"pt-BR": {
|
|
||||||
...ptBrConfig
|
|
||||||
},
|
|
||||||
"zh-CN": {
|
|
||||||
...zhCnConfig
|
|
||||||
},
|
|
||||||
"zh-TW": {
|
|
||||||
...zhTwConfig
|
|
||||||
},
|
|
||||||
ko: {
|
|
||||||
...koConfig
|
|
||||||
},
|
|
||||||
ja: {
|
|
||||||
...jaConfig
|
|
||||||
},
|
|
||||||
"ca-ES": {
|
|
||||||
...caEsConfig
|
|
||||||
}
|
|
||||||
},
|
|
||||||
postProcess: ["korean-postposition"],
|
postProcess: ["korean-postposition"],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -189,11 +276,10 @@ export async function initI18n(): Promise<void> {
|
|||||||
await initFonts(localStorage.getItem("prLang") ?? undefined);
|
await initFonts(localStorage.getItem("prLang") ?? undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default i18next;
|
|
||||||
|
|
||||||
export function getIsInitialized(): boolean {
|
export function getIsInitialized(): boolean {
|
||||||
return isInitialized;
|
return isInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
let isInitialized = false;
|
export default i18next;
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
12
src/utils.ts
12
src/utils.ts
@ -638,3 +638,15 @@ export function isBetween(num: number, min: number, max: number): boolean {
|
|||||||
export function animationFileName(move: Moves): string {
|
export function animationFileName(move: Moves): string {
|
||||||
return Moves[move].toLowerCase().replace(/\_/g, "-");
|
return Moves[move].toLowerCase().replace(/\_/g, "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a camelCase string into a kebab-case string
|
||||||
|
* @param str The camelCase string
|
||||||
|
* @returns A kebab-case string
|
||||||
|
*
|
||||||
|
* @source {@link https://stackoverflow.com/a/67243723/}
|
||||||
|
*/
|
||||||
|
export function camelCaseToKebabCase(str: string): string {
|
||||||
|
console.log("str:", str);
|
||||||
|
return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, (s, o) => (o ? "-" : "") + s.toLowerCase());
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user