feat(web): 平台多语言运行时(i18n,档案 81 · R5)
⚠️ 本提交是**另一执行会话**已完成、且**已上线**的源码侧改动,由本会话代为入库 (线上 https://alotbuy.com/login.html 已引用 /i18n.js)。 - web/i18n.js(新增 276 行):词条表 + data-i18n / data-i18n-attr 就地替换。 口径(用户定):**这个项目是全球化的 ⇒ 默认语言 = 英语**; 优先级 ?lang= → cookie `dsh_lang` → navigator.language(**仅命中支持列表才用**)→ `en`; ⚠️ cookie 名不得含平台内部名(verify-static 的 BANNED 去痕迹约束会拦)。 - web/{index,login,register,wake}.html:接入 <script src="/i18n.js"> + 文案打标 + 页脚语言下拉(中性样式、不新增页面)。 - scripts/verify-static.mjs:新增「**已迁移多语言的页面不得再出现裸中文文案**」 —— I18N_PAGES 白名单(迁一页加一个,未迁页仍允许中文,不会误拦); 判定前先剥掉 HTML/JS 注释(注释不算文案)。 - scripts/verify-platform-admin-section.mjs:随「偏好设置 = 语言切换(全角色)」新增分区数断言 (admin 4 个分区 / 非 admin 3 个;切换走官方 ctx.locale.setLocale,语言 id 与官方 LOCALE_IDS 一致)。 验证:node scripts/verify-static.mjs ✓ | node scripts/verify-platform-admin-section.mjs ✓(本会话复核已跑通)
This commit is contained in:
1 parent
39a1f2e31b
commit
b617cdbaa7
7 files changed
+407
-56
No files matched your search
+17
-15
@@ -1,10 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>正在启动工作区</title>
|
||||
<title data-i18n="wake.title">Starting your workspace</title>
|
||||
<link rel="stylesheet" href="/design.css" />
|
||||
<!-- 档案 81 · R5:平台多语言运行时(默认英语) -->
|
||||
<script src="/i18n.js"></script>
|
||||
<style>
|
||||
/* 档案 77「AI 核心」加载体 —— 与实例页恢复浮层**同一套视觉语言**(配色跟随平台主题) */
|
||||
.wk-orb{position:relative;width:86px;height:86px;margin:2px auto 16px}
|
||||
@@ -56,16 +58,16 @@
|
||||
<div class="wk-ring"></div><div class="wk-core"></div>
|
||||
<div class="wk-dots"><i></i><i></i><i></i></div>
|
||||
</div>
|
||||
<h1 class="page-title" style="margin-bottom:6px">正在启动你的工作区…</h1>
|
||||
<p class="page-sub" style="margin-bottom:0">空闲一段时间后实例会被回收,首次访问需要几秒重新拉起。</p>
|
||||
<h1 class="page-title" style="margin-bottom:6px" data-i18n="wake.h1">Starting your workspace…</h1>
|
||||
<p class="page-sub" style="margin-bottom:0" data-i18n="wake.sub">Idle instances get reclaimed; the first visit takes a few seconds to spin it back up.</p>
|
||||
<div class="wk-bar"><i></i></div>
|
||||
<div class="wk-step" id="step">正在连接…</div>
|
||||
<div class="wk-step" id="step" data-i18n="wake.connecting">Connecting…</div>
|
||||
<div class="wk-label">dsh · auto recovery</div>
|
||||
<div class="wk-acts" id="acts" style="display:none">
|
||||
<button class="btn primary" id="retry">重试</button>
|
||||
<button class="btn primary" id="retry" data-i18n="common.retry">Retry</button>
|
||||
</div>
|
||||
<p class="wk-note" id="note">
|
||||
若长时间无反应,可点「重试」。本页会自动跳转,无需手动刷新。
|
||||
<p class="wk-note" id="note" data-i18n="wake.note">
|
||||
If nothing happens for a while, click “Retry”. This page redirects automatically — no manual refresh needed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -84,7 +86,7 @@
|
||||
document.getElementById('spin').style.display = 'none'
|
||||
acts.style.display = 'block'
|
||||
note.textContent = msg
|
||||
show('启动失败')
|
||||
show(I18N.t('wake.failed'))
|
||||
}
|
||||
|
||||
// 只允许跳回 *.alotbuy.com,避免被 next 参数带偏
|
||||
@@ -94,7 +96,7 @@
|
||||
|
||||
async function wake() {
|
||||
tried += 1
|
||||
show(tried === 1 ? '正在拉起实例(第 1 次,通常 5–15 秒)…' : '正在重试(第 ' + tried + ' 次)…')
|
||||
show(tried === 1 ? I18N.t('wake.launching') : I18N.t('wake.retrying', { n: tried }))
|
||||
try {
|
||||
// 复用平台已有的登录直达接口:它内部会 launch(实例不在则拉起)并返回带 token 的直达 URL
|
||||
var res = await fetch('/api/dsh/enter', {
|
||||
@@ -106,7 +108,7 @@
|
||||
var j = await res.json().catch(function () { return {} })
|
||||
var url = j.url || j.redirect || ''
|
||||
if (res.ok && url) {
|
||||
show('已就绪,正在跳转…')
|
||||
show(I18N.t('wake.ready'))
|
||||
location.replace(url)
|
||||
return
|
||||
}
|
||||
@@ -116,16 +118,16 @@
|
||||
}
|
||||
if (j.error === 'instance_circuit_open') { // 档案 78:被熔断冷却,重试没有意义
|
||||
var waitMin = Math.max(1, Math.ceil((j.retryAfterMs || 0) / 60000))
|
||||
fail('实例连续崩溃,平台已暂停自动拉起(约 ' + waitMin + ' 分钟后可重试)。若反复出现,请联系管理员查看维护日志。')
|
||||
fail(I18N.t('wake.circuit', { min: waitMin }))
|
||||
return
|
||||
}
|
||||
throw new Error(j.error || ('HTTP ' + res.status))
|
||||
} catch (err) {
|
||||
if (tried < MAX) {
|
||||
show('未就绪,2 秒后自动重试…')
|
||||
show(I18N.t('wake.notReady'))
|
||||
setTimeout(wake, 2000)
|
||||
} else {
|
||||
fail('多次尝试仍未就绪:' + (err && err.message ? err.message : '未知错误'))
|
||||
fail(I18N.t('wake.giveUp', { msg: (err && err.message ? err.message : I18N.t('wake.unknownErr')) }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +136,7 @@
|
||||
tried = 0
|
||||
document.getElementById('spin').style.display = ''
|
||||
acts.style.display = 'none'
|
||||
note.textContent = '本页会自动跳转,无需手动刷新。'
|
||||
note.textContent = I18N.t('wake.noteShort')
|
||||
wake()
|
||||
})
|
||||
|
||||
|
||||
Reference in new issue
Block a user