diff --git a/docs/I18N.md b/docs/I18N.md index 7e5bdf95..7b3dcfbf 100644 --- a/docs/I18N.md +++ b/docs/I18N.md @@ -40,7 +40,8 @@ html`${tParts('第 {r} 回合 · 最终攻势', { r: html`$ current language (§2). Do not wrap them in `t()`. - **Re-rendering**: `main.js` App subscribes with `useLang()` (`public/js/ui/lang.js`), so the whole tree re-renders on a switch. A component that caches text outside rendering (a `useMemo` returning text, a module-level string) must - depend on the language (`useLang()`) or move the `t()` call into rendering. + depend on the language (`useLang()`) or move the `t()` call into rendering; a memo over data records (the roster, the + strategy list, `useGameData()`) lists `data.locale()` among its dependencies, so the game texts follow the switch. - Font samples, data keys and other Chinese that is not UI text can carry `// i18n-ignore` on the line. ### Translating diff --git a/public/js/screens/bandDraft.js b/public/js/screens/bandDraft.js index 0f0b3a03..46d9e791 100644 --- a/public/js/screens/bandDraft.js +++ b/public/js/screens/bandDraft.js @@ -179,7 +179,7 @@ export function BandDraftScreen() { const mode = gd.config?.modes?.[pub?.modeId]; const offBonds = modeOffBonds(mode); // the bonds this mode never activates (标准: 10 of 23) const solo = roomSolo || mode?.type === 'SINGLE' || String(pub?.modeId || '').includes('single'); - const bands = useMemo(() => allowedBands(gd.list('bands'), mode?.type || (solo ? 'SINGLE' : 'MULTI')), [gd.ready, mode?.type, solo]); + const bands = useMemo(() => allowedBands(gd.list('bands'), mode?.type || (solo ? 'SINGLE' : 'MULTI')), [gd.ready, mode?.type, solo, data.locale()]); const players = sortedPlayers(pub); const draft = normalizeDraft(pub?.draft, players); const myPick = draft.picks.get(myId) || priv?.bandId || null; diff --git a/public/js/screens/game.js b/public/js/screens/game.js index ab58efca..43563c64 100644 --- a/public/js/screens/game.js +++ b/public/js/screens/game.js @@ -1044,7 +1044,7 @@ function MatchScreen() { const u = (Array.isArray(field?.units) ? field.units : []).find((x) => x && x.id === detail.unitId); return u ? { ...detail, unit: u } : detail; }, [detail, field]); - const resolved = useMemo(() => resolveDetail(detailTarget, placeCtx.pieces, { priv, backups: gd.backups }), [detailTarget, placeCtx, gd.ready]); + const resolved = useMemo(() => resolveDetail(detailTarget, placeCtx.pieces, { priv, backups: gd.backups }), [detailTarget, placeCtx, gd.ready, data.locale()]); useEffect(() => { if (detail && !resolved && detail.kind === 'piece') setDetail(null); }, [resolved]); const snapHp = (() => { const id = resolved?.unitId; diff --git a/public/js/screens/loadout.js b/public/js/screens/loadout.js index c716f919..07728cab 100644 --- a/public/js/screens/loadout.js +++ b/public/js/screens/loadout.js @@ -403,7 +403,7 @@ function LoadoutScreen({ st }) { const m = data.get('assets'); const getChess = (id) => data.lookup('chess', id); const getBond = (id) => data.lookup('bonds', id); - const roster = useMemo(() => rosterOf(data.list('chess')), [ready]); + const roster = useMemo(() => rosterOf(data.list('chess')), [ready, data.locale()]); // (names follow a language switch) const bonds = useMemo(() => { const used = new Set(roster.flatMap((c) => c.bonds || [])); return (data.list('bonds') || []).filter((b) => b && used.has(b.bondId)) diff --git a/public/js/screens/ownership.js b/public/js/screens/ownership.js index 579b4b2d..d1ecc0e8 100644 --- a/public/js/screens/ownership.js +++ b/public/js/screens/ownership.js @@ -23,7 +23,7 @@ const ROMAN = ['', 'I', 'II', 'III', 'IV', 'V', 'VI']; /** The roster of the tab (data.list('chess'), memoised on the data being ready). */ export function useOwnershipRoster(ready) { - return useMemo(() => ownershipRoster(data.list('chess')), [ready]); + return useMemo(() => ownershipRoster(data.list('chess')), [ready, data.locale()]); } /** One chess: the operator → its stand-in, and the 持有 switch (the whole card is the switch). */ diff --git a/public/js/ui/gameComponents.js b/public/js/ui/gameComponents.js index 3ba221fd..b531e986 100644 --- a/public/js/ui/gameComponents.js +++ b/public/js/ui/gameComponents.js @@ -24,7 +24,8 @@ export const GAME_FILES = ['config', 'assets', 'chess', 'bonds', 'items', 'bands */ export function useGameData() { const ready = useData(...GAME_FILES); - return useMemo(() => makeLookups(ready), [ready]); + // (config / factions / choices are read once per memo: a language switch reads them again in the new locale) + return useMemo(() => makeLookups(ready), [ready, data.locale()]); } /** A 自选 operator's summon record (data/backups.json `tokens`, 0.2.0), or null. */