feedback5 W5S: i18n phase 2, ui — HUD, shop bar, detail card, bond strip and popup, 机变 overlay, enemy drawer, team panel, under-frame, guide, emotes, match chrome, connection banner, components, the loadout / roster models, the store, net and observe texts through t() / tParts() (whole sentences where a number or a name sat inside), msgid tables marked N_() and translated where shown, the shop's refusal reasons compared through t(); tools/i18n.mjs reads aliased imports (t as tr); English for the 59 new msgids; Chinese output unchanged (zh render proof; five rewrites checked by hand, listed in the notes)

This commit is contained in:
sganggs committed 2026-10-06 18:09:50 +08:00
1 parent 166c4fbbfb
commit 647bd23ef2
33 files changed
+530 -427

No files matched your search

+20 -19
View File
@@ -12,14 +12,15 @@ import { net } from '../net.js';
import { store, useStore, shallowEqual, emptyMatch, createStore, isSpectating } from '../store.js';
import { GIcon } from './gameComponents.js';
import { GuideButton } from './guide.js';
import { t, tParts, N_ } from '../../../shared/i18n.js';
/**
* Exit dialog lines. 放弃模拟 (g.leave → room.leave) ends the run on the server as 'abandoned' and returns to the
* lobby straight away — no settlement screen follows, so the solo text must not promise one.
*/
export const EXIT_TEXT = Object.freeze({
soloRest: '独立模拟的休整期没有时间限制,你可以随时继续。',
soloQuit: '放弃模拟将立即结束本局并返回大厅,本局进度不会保留,也不会进行结算。',
soloRest: N_('独立模拟的休整期没有时间限制,你可以随时继续。'),
soloQuit: N_('放弃模拟将立即结束本局并返回大厅,本局进度不会保留,也不会进行结算。'),
});
/** Local "暂离 / AI 托管" flag (the server keeps no per-client autoplay view). */
@@ -67,10 +68,10 @@ export function ExitModal({ open, onClose, solo, onAway }) {
onClose();
};
if (spectator) {
return html`<${Modal} open=${open} onClose=${onClose} tone="red" title="离开观战" micro="LEAVE SPECTATING" width="6.8rem"
actions=${html`<${Button} variant="secondary" onClick=${onClose}>取消<//>
<${Button} variant="danger" icon="exit" loading=${busy === 'quit'} onClick=${quit}>离开观战<//>`}>
<div class="exitm"><p>离开观战席并返回大厅,本局模拟不受影响;观战席空着时可以凭同盟密钥再次观战。</p></div>
return html`<${Modal} open=${open} onClose=${onClose} tone="red" title=${t('离开观战')} micro="LEAVE SPECTATING" width="6.8rem"
actions=${html`<${Button} variant="secondary" onClick=${onClose}>${t('取消')}<//>
<${Button} variant="danger" icon="exit" loading=${busy === 'quit'} onClick=${quit}>${t('离开观战')}<//>`}>
<div class="exitm"><p>${t('离开观战席并返回大厅,本局模拟不受影响;观战席空着时可以凭同盟密钥再次观战。')}</p></div>
<//>`;
}
const away = async () => {
@@ -80,16 +81,16 @@ export function ExitModal({ open, onClose, solo, onAway }) {
onClose();
if (ok) { awayStore.set({ away: true }); onAway?.(); }
};
return html`<${Modal} open=${open} onClose=${onClose} tone="red" title="离开模拟" micro="LEAVE SIMULATION" width="6.8rem"
return html`<${Modal} open=${open} onClose=${onClose} tone="red" title=${t('离开模拟')} micro="LEAVE SIMULATION" width="6.8rem"
actions=${html`
<${Button} variant="secondary" onClick=${onClose}>取消<//>
${!solo ? html`<${Button} variant="ice" icon="robot" loading=${busy === 'away'} onClick=${away}>暂离(AI 托管)<//>` : null}
<${Button} variant="danger" icon="exit" loading=${busy === 'quit'} onClick=${quit}>放弃模拟<//>`}>
<${Button} variant="secondary" onClick=${onClose}>${t('取消')}<//>
${!solo ? html`<${Button} variant="ice" icon="robot" loading=${busy === 'away'} onClick=${away}>${t('暂离(AI 托管)')}<//>` : null}
<${Button} variant="danger" icon="exit" loading=${busy === 'quit'} onClick=${quit}>${t('放弃模拟')}<//>`}>
<div class="exitm">
${solo
? html`<p>${EXIT_TEXT.soloRest}</p><p class="t-lo">${EXIT_TEXT.soloQuit}</p>`
: html`<p><b class="t-ice">暂离</b>:由 AI 托管你的席位(自动部署、准备与选择),随时可以返回。</p>
<p><b class="t-red">放弃模拟</b>:离开后无法返回本局,你的干员将回到共享卡池。</p>`}
? html`<p>${t(EXIT_TEXT.soloRest)}</p><p class="t-lo">${t(EXIT_TEXT.soloQuit)}</p>`
: html`<p>${tParts('{away}:由 AI 托管你的席位(自动部署、准备与选择),随时可以返回。', { away: html`<b class="t-ice">${t('暂离')}</b>` })}</p>
<p>${tParts('{quit}:离开后无法返回本局,你的干员将回到共享卡池。', { quit: html`<b class="t-red">${t('放弃模拟')}</b>` })}</p>`}
</div>
<//>`;
}
@@ -97,13 +98,13 @@ export function ExitModal({ open, onClose, solo, onAway }) {
/** Full-screen "AI 托管中" overlay with 返回模拟. */
export function AwayOverlay({ onBack = () => {} }) {
const [busy, setBusy] = useState(false);
return html`<div class="awayov" role="dialog" aria-label="AI 托管中">
return html`<div class="awayov" role="dialog" aria-label=${t('AI 托管中')}>
<div class="awayov__box brackets">
<${GIcon} name="robot" class="awayov__icon" />
<${MicroLabel} tone="mint">AUTOPILOT // AI 托管</${MicroLabel}>
<h2>AI 托管中</h2>
<p class="t-lo">AI 正在代为操作你的席位</p>
<${Button} variant="primary" size="lg" icon="play" loading=${busy} onClick=${async () => { setBusy(true); const ok = await actions.autoplay(false); setBusy(false); if (ok) { awayStore.set({ away: false }); onBack(); } }}>返回模拟<//>
<${MicroLabel} tone="mint">${t('AUTOPILOT // AI 托管')}</${MicroLabel}>
<h2>${t('AI 托管中')}</h2>
<p class="t-lo">${t('AI 正在代为操作你的席位')}</p>
<${Button} variant="primary" size="lg" icon="play" loading=${busy} onClick=${async () => { setBusy(true); const ok = await actions.autoplay(false); setBusy(false); if (ok) { awayStore.set({ away: false }); onBack(); } }}>${t('返回模拟')}<//>
</div>
</div>`;
}
@@ -116,7 +117,7 @@ export function StepHeader({ step, of, title, micro, pub, total, onExit }) {
const conn = useStore((s) => s.connection, shallowEqual);
return html`<header class="stephead">
<div class="stephead__left">
<${Button} variant="danger" size="lg" square=${true} icon="exit" onClick=${onExit} aria-label="离开" title="离开" />
<${Button} variant="danger" size="lg" square=${true} icon="exit" onClick=${onExit} aria-label=${t('离开')} title=${t('离开')} />
<div class="stephead__meta">
<${PingPill} ms=${conn.ping} online=${conn.status === 'online'} />
${pub?.difficulty ? html`<${DifficultyTag} difficulty=${pub.difficulty} />` : null}