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 { isMobile } from "./touch-controls";
|
||||
import * as Utils from "./utils";
|
||||
import { initI18n } from "./plugins/i18n";
|
||||
import { initPokemonPrevolutions } from "#app/data/pokemon-evolutions";
|
||||
import { initBiomes } from "#app/data/biomes";
|
||||
import { initEggMoves } from "#app/data/egg-moves";
|
||||
@ -33,7 +32,6 @@ export class LoadingScene extends SceneBase {
|
||||
super(LoadingScene.KEY);
|
||||
|
||||
Phaser.Plugins.PluginCache.register("Loader", CacheBustedLoaderPlugin, "load");
|
||||
initI18n();
|
||||
}
|
||||
|
||||
preload() {
|
||||
|
54
src/main.ts
54
src/main.ts
@ -1,12 +1,11 @@
|
||||
import Phaser from "phaser";
|
||||
import BattleScene from "./battle-scene";
|
||||
import InvertPostFX from "./pipelines/invert";
|
||||
import { version } from "../package.json";
|
||||
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
|
||||
import BBCodeTextPlugin from "phaser3-rex-plugins/plugins/bbcodetext-plugin";
|
||||
import InputTextPlugin from "phaser3-rex-plugins/plugins/inputtext-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.
|
||||
@ -25,7 +24,31 @@ window.addEventListener("unhandledrejection", (event) => {
|
||||
//alert(errorString);
|
||||
});
|
||||
|
||||
const config: Phaser.Types.Core.GameConfig = {
|
||||
/**
|
||||
* Sets this object's position relative to another object with a given offset
|
||||
*/
|
||||
const setPositionRelative = function (guideObject: Phaser.GameObjects.GameObject, x: number, y: number) {
|
||||
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
|
||||
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
|
||||
this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
|
||||
};
|
||||
|
||||
Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Sprite.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Image.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.NineSlice.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Text.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Rectangle.prototype.setPositionRelative = setPositionRelative;
|
||||
|
||||
document.fonts.load("16px emerald").then(() => document.fonts.load("10px pkmnems"));
|
||||
|
||||
let game;
|
||||
|
||||
const startGame = async () => {
|
||||
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: {
|
||||
@ -69,30 +92,7 @@ const config: Phaser.Types.Core.GameConfig = {
|
||||
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
|
||||
*/
|
||||
const setPositionRelative = function (guideObject: Phaser.GameObjects.GameObject, x: number, y: number) {
|
||||
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
|
||||
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
|
||||
this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
|
||||
};
|
||||
|
||||
Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Sprite.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Image.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.NineSlice.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Text.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Rectangle.prototype.setPositionRelative = setPositionRelative;
|
||||
|
||||
document.fonts.load("16px emerald").then(() => document.fonts.load("10px pkmnems"));
|
||||
|
||||
let game;
|
||||
|
||||
const startGame = () => {
|
||||
game = new Phaser.Game(config);
|
||||
});
|
||||
game.sound.pauseOnBlur = false;
|
||||
};
|
||||
|
||||
|
@ -1,18 +1,11 @@
|
||||
import i18next from "i18next";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
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";
|
||||
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";
|
||||
//#region Interfaces/Types
|
||||
|
||||
interface LoadingFontFaceProperty {
|
||||
face: FontFace,
|
||||
@ -20,6 +13,10 @@ interface LoadingFontFaceProperty {
|
||||
only?: Array<string>
|
||||
}
|
||||
|
||||
//#region Constants
|
||||
|
||||
let isInitialized = false;
|
||||
|
||||
const unicodeRanges = {
|
||||
fullwidth: "U+FF00-FFEF",
|
||||
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",
|
||||
specialCharacters: "U+266A,U+2605,U+2665,U+2663" //♪.★,♥,♣
|
||||
};
|
||||
|
||||
const rangesByLanguage = {
|
||||
korean: [unicodeRanges.CJKCommon, unicodeRanges.hangul].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) {
|
||||
const results = await Promise.allSettled(
|
||||
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> {
|
||||
// Prevent reinitialization
|
||||
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.
|
||||
*/
|
||||
|
||||
i18next.use(HttpBackend);
|
||||
i18next.use(LanguageDetector);
|
||||
i18next.use(processor);
|
||||
i18next.use(new KoreanPostpositionProcessor());
|
||||
@ -120,8 +138,112 @@ export async function initI18n(): Promise<void> {
|
||||
nonExplicitSupportedLngs: true,
|
||||
fallbackLng: "en",
|
||||
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",
|
||||
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: {
|
||||
lookupLocalStorage: "prLang"
|
||||
},
|
||||
@ -129,41 +251,6 @@ export async function initI18n(): Promise<void> {
|
||||
interpolation: {
|
||||
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"],
|
||||
});
|
||||
|
||||
@ -189,11 +276,10 @@ export async function initI18n(): Promise<void> {
|
||||
await initFonts(localStorage.getItem("prLang") ?? undefined);
|
||||
}
|
||||
|
||||
export default i18next;
|
||||
|
||||
export function getIsInitialized(): boolean {
|
||||
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 {
|
||||
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