Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions emrg/gui/renderer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
<span>EMRG</span>
</div>
<!-- 成长状态卡(WorkBuddy P3:自进化可见化;#501) -->
<div class="growth-card" id="growth-card" title="EMRG 每次自我改进都会自动汇报">
<div class="growth-card" id="growth-card" title="EMRG 每次自我改进都会自动汇报" data-i18n-title="copy.growthCardTitle">
<div class="growth-line">🌱 <span data-i18n="copy.growthCountPrefix">已自我进化</span> <b id="growth-count">0</b> <span data-i18n="copy.times">次</span></div>
<div class="growth-note" id="growth-note">边工作边学习,越用越懂你</div>
<div class="growth-note" id="growth-note" data-i18n="copy.growthNote">边工作边学习,越用越懂你</div>
</div>
<button id="new-chat-btn" class="new-chat-btn" title="新对话 (⌘N)" data-i18n="sidebar.newChat" data-i18n-title="sidebar.newChatTitle">+ 新对话</button>
<nav id="conv-list"></nav>
<div class="sidebar-footer">
<span id="status-dot" class="status-dot gray" title="连接状态"></span>
<span id="status-dot" class="status-dot gray" title="连接状态" data-i18n-title="sidebar.statusTitle"></span>
<button id="settings-btn" class="btn btn-ghost" style="margin-left:auto;padding:6px 12px;min-height:30px;" data-i18n="sidebar.settings">设置</button>
</div>
</aside>
Expand All @@ -40,7 +40,7 @@
<!-- 聊天区 -->
<div id="chat-view"></div>
<!-- 回到底部悬浮按钮(上滑阅读时出现) -->
<button id="back-to-bottom" class="back-to-bottom hidden" title="回到底部">↓</button>
<button id="back-to-bottom" class="back-to-bottom hidden" title="回到底部" data-i18n-title="sidebar.backToBottom">↓</button>

<!-- 空状态欢迎屏 -->
<div id="empty-state" class="hidden">
Expand Down Expand Up @@ -98,7 +98,7 @@ <h2 data-i18n="settings.title">设置</h2>
<div id="model-form" class="model-form hidden">
<div class="model-form-row">
<label class="model-form-label" data-i18n="settings.name">名称
<input type="text" id="model-form-name" placeholder="如 deepseek-v3" maxlength="40" />
<input type="text" id="model-form-name" placeholder="如 deepseek-v3" maxlength="40" data-i18n-placeholder="settings.modelNamePlaceholder" />
</label>
<label class="model-form-label" data-i18n="settings.modelId">模型 ID(选填)
<input type="text" id="model-form-id" placeholder="默认同名称" maxlength="60" data-i18n-placeholder="settings.modelIdPlaceholder" />
Expand Down Expand Up @@ -144,15 +144,15 @@ <h2 data-i18n="settings.title">设置</h2>
</label>
</div>
<div class="settings-group">
<div class="settings-group-title">关于</div>
<div class="settings-group-title" data-i18n="settings.aboutTitle">关于</div>
<div class="about-box">
<div class="about-line">EMRG <span id="about-version">v0.2.8</span> · <span id="about-evolutions">🌱 <span data-i18n="copy.growthCountPrefix">已自我进化</span> 0 <span data-i18n="copy.times">次</span></span></div>
<div class="hint" style="margin-top:6px;">EMRG 是一个会自我进化的 AI 智能体——每次改进都会自动汇报,你可以随时在这里看到它的成长。</div>
<div class="hint" style="margin-top:6px;" data-i18n="settings.aboutDesc">EMRG 是一个会自我进化的 AI 智能体——每次改进都会自动汇报,你可以随时在这里看到它的成长。</div>
</div>
</div>
<!-- WorkBuddy P3:最近改进列表(daemon evolution_summary) -->
<div class="settings-group">
<div class="settings-group-title">最近改进</div>
<div class="settings-group-title" data-i18n="settings.recentTitle">最近改进</div>
<div id="about-recent" class="about-recent"></div>
</div>
<div class="dialog-actions">
Expand Down Expand Up @@ -316,11 +316,11 @@ <h2 data-i18n="tasks.title">后台任务</h2>

<!-- 进化完成 toast(WorkBuddy P3:进化发生时温和提示) -->
<div id="evolution-toast" class="evolution-toast hidden">
<div class="evolution-toast-title">🌱 EMRG 刚刚完成一次自我进化!</div>
<div class="evolution-toast-msg" id="evolution-toast-msg">它学会了新的东西,变得更好用了。</div>
<div class="evolution-toast-title" data-i18n="copy.evolutionToastTitle">🌱 EMRG 刚刚完成一次自我进化!</div>
<div class="evolution-toast-msg" id="evolution-toast-msg" data-i18n="copy.evolutionToastMsgStatic">它学会了新的东西,变得更好用了。</div>
<div class="evolution-toast-actions">
<button type="button" id="evolution-toast-see" class="btn btn-primary">去看看</button>
<button type="button" id="evolution-toast-dismiss" class="btn btn-ghost">知道了</button>
<button type="button" id="evolution-toast-see" class="btn btn-primary" data-i18n="copy.evolutionToastSee">去看看</button>
<button type="button" id="evolution-toast-dismiss" class="btn btn-ghost" data-i18n="copy.evolutionToastDismiss">知道了</button>
</div>
</div>

Expand Down
16 changes: 16 additions & 0 deletions emrg/gui/renderer/js/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const I18N = (() => {
"sidebar.newChat": "+ 新对话",
"sidebar.newChatTitle": "新对话 (⌘N)",
"sidebar.settings": "设置",
"sidebar.statusTitle": "连接状态",
"sidebar.backToBottom": "回到底部",

// 空状态欢迎屏
"empty.hello": "你好,我是 EMRG",
Expand Down Expand Up @@ -52,10 +54,14 @@ const I18N = (() => {
"settings.availableModels": "可用模型",
"settings.addModel": "+ 添加模型",
"settings.name": "名称",
"settings.modelNamePlaceholder": "如 deepseek-v3",
"settings.modelId": "模型 ID(选填)",
"settings.modelIdPlaceholder": "默认同名称",
"settings.vision": "支持图片",
"settings.cancel": "取消",
"settings.aboutTitle": "关于",
"settings.aboutDesc": "EMRG 是一个会自我进化的 AI 智能体——每次改进都会自动汇报,你可以随时在这里看到它的成长。",
"settings.recentTitle": "最近改进",
"settings.saveModel": "保存模型",
"settings.defaultHint": "点左侧圆点可设为默认模型,切换后下一条消息即生效",
"settings.groupWorkdir": "工作目录",
Expand Down Expand Up @@ -147,12 +153,14 @@ const I18N = (() => {
"copy.aboutEvolution": "EMRG 已自我成长 {n} 次,感谢你的每一次反馈",
"copy.aboutEvolutionEmpty": "EMRG 正在成长中",
// WorkBuddy P3(#501):成长卡 + 进化 toast
"copy.growthCardTitle": "EMRG 每次自我改进都会自动汇报",
"copy.growthCount": "已自我进化 {n} 次",
"copy.growthCountPrefix": "已自我进化",
"copy.times": "次",
"copy.growthNote": "边工作边学习,越用越懂你",
"copy.evolutionToastTitle": "EMRG 刚刚完成一次自我进化!",
"copy.evolutionToastMsg": "这是它的第 {n} 次自我改进,现在更好用了。",
"copy.evolutionToastMsgStatic": "它学会了新的东西,变得更好用了。",
"copy.evolutionToastSee": "去看看",
"copy.evolutionToastDismiss": "知道了",

Expand Down Expand Up @@ -272,6 +280,8 @@ const I18N = (() => {
"sidebar.newChat": "+ New chat",
"sidebar.newChatTitle": "New chat (⌘N)",
"sidebar.settings": "Settings",
"sidebar.statusTitle": "Connection status",
"sidebar.backToBottom": "Back to bottom",

// Empty state
"empty.hello": "Hi, I'm EMRG",
Expand Down Expand Up @@ -304,10 +314,14 @@ const I18N = (() => {
"settings.availableModels": "Available models",
"settings.addModel": "+ Add model",
"settings.name": "Name",
"settings.modelNamePlaceholder": "e.g. deepseek-v3",
"settings.modelId": "Model ID (optional)",
"settings.modelIdPlaceholder": "Defaults to name",
"settings.vision": "Supports images",
"settings.cancel": "Cancel",
"settings.aboutTitle": "About",
"settings.aboutDesc": "EMRG is a self-evolving AI agent — it reports every improvement, and you can watch it grow right here.",
"settings.recentTitle": "Recent improvements",
"settings.saveModel": "Save model",
"settings.defaultHint": "Click the dot on the left to set the default model — takes effect on the next message",
"settings.groupWorkdir": "Working directory",
Expand Down Expand Up @@ -399,12 +413,14 @@ const I18N = (() => {
"copy.aboutEvolution": "EMRG has self-evolved {n} times — thanks for every bit of feedback",
"copy.aboutEvolutionEmpty": "EMRG is still growing",
// WorkBuddy P3 (#501): growth card + evolution toast
"copy.growthCardTitle": "EMRG reports every self-improvement automatically",
"copy.growthCount": "Self-evolved {n} times",
"copy.growthCountPrefix": "Self-evolved",
"copy.times": "times",
"copy.growthNote": "Learning as I work — getting better with use",
"copy.evolutionToastTitle": "EMRG just completed a self-evolution!",
"copy.evolutionToastMsg": "That's its {n}th self-improvement — it's even better now.",
"copy.evolutionToastMsgStatic": "It learned something new and is even better now.",
"copy.evolutionToastSee": "See details",
"copy.evolutionToastDismiss": "Got it",

Expand Down
30 changes: 30 additions & 0 deletions emrg/gui/test/i18n.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,33 @@ test("Stage2:成长卡/关于区静态文案键(#501 吸收)", () => {
const { ctx: zh } = makeSandbox({ navigator: { language: "zh-CN" } });
assert.strictEqual(evalIn(zh, 'I18N.t("copy.growthCount", { n: 7 })'), "已自我进化 7 次");
});

// ── Stage 3(cycle 20260806-225341):index.html 无遗漏硬编码中文 ──
test("Stage3:index.html 中文字符串必须带 data-i18n(防漏网回归)", () => {
const html = fs.readFileSync(path.join(__dirname, "..", "renderer", "index.html"), "utf8");
const cjk = /[\u4e00-\u9fff]/;
const leaks = [];
for (const [i, line] of html.split("\n").entries()) {
const s = line.trim();
if (!cjk.test(s)) continue; // 无中文
if (s.startsWith("<!--")) continue; // HTML 注释
if (s.includes("data-i18n")) continue; // 已接入 i18n(含 data-i18n-title/placeholder)
leaks.push(`L${i + 1}: ${s.slice(0, 80)}`);
}
assert.strictEqual(leaks.length, 0, "index.html 存在未接入 i18n 的中文(补 data-i18n 或 data-i18n-title/placeholder):\n" + leaks.join("\n"));
});

test("Stage3:新增静态键双语一致(growth-card title / about / status-dot / toast 静态)", () => {
const { ctx } = makeSandbox({ navigator: { language: "en-US" } });
assert.strictEqual(evalIn(ctx, 'I18N.t("copy.growthCardTitle")'), "EMRG reports every self-improvement automatically");
assert.strictEqual(evalIn(ctx, 'I18N.t("sidebar.statusTitle")'), "Connection status");
assert.strictEqual(evalIn(ctx, 'I18N.t("sidebar.backToBottom")'), "Back to bottom");
assert.strictEqual(evalIn(ctx, 'I18N.t("settings.aboutTitle")'), "About");
assert.strictEqual(evalIn(ctx, 'I18N.t("settings.recentTitle")'), "Recent improvements");
assert.strictEqual(evalIn(ctx, 'I18N.t("settings.modelNamePlaceholder")'), "e.g. deepseek-v3");
assert.strictEqual(evalIn(ctx, 'I18N.t("copy.evolutionToastMsgStatic")'), "It learned something new and is even better now.");
const { ctx: zh } = makeSandbox({ navigator: { language: "zh-CN" } });
assert.strictEqual(evalIn(zh, 'I18N.t("settings.aboutTitle")'), "关于");
assert.strictEqual(evalIn(zh, 'I18N.t("settings.recentTitle")'), "最近改进");
assert.strictEqual(evalIn(zh, 'I18N.t("sidebar.backToBottom")'), "回到底部");
});
Loading