feat(ui): translate channels views and update terminology

- Keep 'Token' and 'Skills' in English per user preference
- Add i18n to channels.ts, channels.shared.ts, channels.whatsapp.ts
- Add WhatsApp-specific translations (linked, authAge, showQr, etc.)
- Update nav.tabs.skills to 'Skills'
- Update config.sections.skills to 'Skills'
This commit is contained in:
Claude 2026-01-28 23:45:51 +00:00
parent edeb35dbcc
commit e1066b0a42
No known key found for this signature in database
5 changed files with 61 additions and 40 deletions

View File

@ -186,10 +186,13 @@ export const enUS = {
connected: "Connected",
},
// General
accounts: "Accounts",
// WhatsApp
whatsapp: {
title: "WhatsApp",
desc: "WhatsApp via Baileys (multi-device).",
desc: "Link WhatsApp Web and monitor connection health.",
start: "Start",
relink: "Relink",
logout: "Logout",
@ -197,6 +200,12 @@ export const enUS = {
linking: "Linking…",
waitingForQr: "Waiting for QR code…",
notConfigured: "Not configured.",
linked: "Linked",
lastConnect: "Last connect",
authAge: "Auth age",
showQr: "Show QR",
waitForScan: "Wait for scan",
working: "Working…",
},
// Telegram

View File

@ -78,7 +78,7 @@ export const zhTW = {
instances: "實例",
sessions: "工作階段",
cron: "排程任務",
skills: "技能",
skills: "Skills",
nodes: "節點",
chat: "對話",
config: "組態",
@ -91,7 +91,7 @@ export const zhTW = {
instances: "來自已連線用戶端與節點的存在訊號。",
sessions: "檢視進行中的工作階段並調整個別設定。",
cron: "排程喚醒與週期性代理執行。",
skills: "管理技能的啟用狀態與 API 金鑰。",
skills: "管理 Skills 的啟用狀態與 API 金鑰。",
nodes: "已配對的裝置、功能與指令權限。",
chat: "直接與閘道器對話,進行快速操作。",
config: "安全地編輯 ~/.clawdbot/moltbot.json 設定檔。",
@ -107,7 +107,7 @@ export const zhTW = {
gatewayAccess: "閘道器連線",
gatewayAccessDesc: "控制台連線位置與認證方式。",
websocketUrl: "WebSocket 網址",
gatewayToken: "閘道器 Token",
gatewayToken: "Gateway Token",
password: "密碼(不會儲存)",
passwordPlaceholder: "系統或共用密碼",
defaultSessionKey: "預設工作階段金鑰",
@ -193,10 +193,13 @@ export const zhTW = {
connected: "已連線",
},
// 一般
accounts: "帳號",
// WhatsApp
whatsapp: {
title: "WhatsApp",
desc: "透過 Baileys 連接 WhatsApp多裝置。",
desc: "連結 WhatsApp Web 並監控連線狀態。",
start: "開始",
relink: "重新連結",
logout: "登出",
@ -204,6 +207,12 @@ export const zhTW = {
linking: "連結中…",
waitingForQr: "等待 QR Code…",
notConfigured: "尚未設定。",
linked: "已連結",
lastConnect: "上次連線",
authAge: "認證時長",
showQr: "顯示 QR",
waitForScan: "等待掃描",
working: "處理中…",
},
// Telegram
@ -388,12 +397,12 @@ export const zhTW = {
},
},
// 技能頁面
// Skills 頁面
skills: {
title: "技能",
desc: "管理內建與已安裝的技能。",
noSkills: "找不到技能。",
filter: "篩選技能",
title: "Skills",
desc: "管理內建與已安裝的 Skills。",
noSkills: "找不到 Skills。",
filter: "篩選 Skills",
shown: "個顯示中",
apiKey: "API 金鑰",
saveKey: "儲存金鑰",
@ -417,7 +426,7 @@ export const zhTW = {
desc: "已配對的裝置與即時連結。",
noNodes: "找不到節點。",
devices: "裝置",
devicesDesc: "配對請求與角色權杖。",
devicesDesc: "配對請求與角色 Token。",
noDevices: "沒有已配對的裝置。",
approve: "核准",
reject: "拒絕",
@ -427,8 +436,8 @@ export const zhTW = {
paired: "已配對",
approved: "已核准",
offline: "離線",
tokens: "權杖",
tokensNone: "權杖:無",
tokens: "Token",
tokensNone: "Token:無",
role: "角色",
requested: "請求於",
repair: "修復",
@ -530,7 +539,7 @@ export const zhTW = {
messages: "訊息",
commands: "指令",
hooks: "鉤子",
skills: "技能",
skills: "Skills",
tools: "工具",
gateway: "閘道器",
wizard: "設定精靈",

View File

@ -1,10 +1,11 @@
import { html, nothing } from "lit";
import { t } from "../../i18n";
import type { ChannelAccountSnapshot } from "../types";
import type { ChannelKey, ChannelsProps } from "./channels.types";
export function formatDuration(ms?: number | null) {
if (!ms && ms !== 0) return "n/a";
if (!ms && ms !== 0) return t("common.na");
const sec = Math.round(ms / 1000);
if (sec < 60) return `${sec}s`;
const min = Math.round(sec / 60);
@ -41,5 +42,5 @@ export function renderChannelAccountCount(
) {
const count = getChannelAccountCount(key, channelAccounts);
if (count < 2) return nothing;
return html`<div class="account-count">Accounts (${count})</div>`;
return html`<div class="account-count">${t("channels.accounts")} (${count})</div>`;
}

View File

@ -1,5 +1,6 @@
import { html, nothing } from "lit";
import { t } from "../../i18n";
import { formatAgo } from "../format";
import type {
ChannelAccountSnapshot,
@ -77,10 +78,10 @@ export function renderChannels(props: ChannelsProps) {
<section class="card" style="margin-top: 18px;">
<div class="row" style="justify-content: space-between;">
<div>
<div class="card-title">Channel health</div>
<div class="card-sub">Channel status snapshots from the gateway.</div>
<div class="card-title">${t("channels.health")}</div>
<div class="card-sub">${t("channels.healthDesc")}</div>
</div>
<div class="muted">${props.lastSuccessAt ? formatAgo(props.lastSuccessAt) : "n/a"}</div>
<div class="muted">${props.lastSuccessAt ? formatAgo(props.lastSuccessAt) : t("common.na")}</div>
</div>
${props.lastError
? html`<div class="callout danger" style="margin-top: 12px;">
@ -88,7 +89,7 @@ export function renderChannels(props: ChannelsProps) {
</div>`
: nothing}
<pre class="code-block" style="margin-top: 12px;">
${props.snapshot ? JSON.stringify(props.snapshot, null, 2) : "No snapshot yet."}
${props.snapshot ? JSON.stringify(props.snapshot, null, 2) : t("channels.noSnapshot")}
</pre>
</section>
`;

View File

@ -1,5 +1,6 @@
import { html, nothing } from "lit";
import { t } from "../../i18n";
import { formatAgo } from "../format";
import type { WhatsAppStatus } from "../types";
import type { ChannelsProps } from "./channels.types";
@ -16,46 +17,46 @@ export function renderWhatsAppCard(params: {
return html`
<div class="card">
<div class="card-title">WhatsApp</div>
<div class="card-sub">Link WhatsApp Web and monitor connection health.</div>
<div class="card-sub">${t("channels.whatsapp.desc")}</div>
${accountCountLabel}
<div class="status-list" style="margin-top: 16px;">
<div>
<span class="label">Configured</span>
<span>${whatsapp?.configured ? "Yes" : "No"}</span>
<span class="label">${t("channels.labels.configured")}</span>
<span>${whatsapp?.configured ? t("common.yes") : t("common.no")}</span>
</div>
<div>
<span class="label">Linked</span>
<span>${whatsapp?.linked ? "Yes" : "No"}</span>
<span class="label">${t("channels.whatsapp.linked")}</span>
<span>${whatsapp?.linked ? t("common.yes") : t("common.no")}</span>
</div>
<div>
<span class="label">Running</span>
<span>${whatsapp?.running ? "Yes" : "No"}</span>
<span class="label">${t("channels.labels.running")}</span>
<span>${whatsapp?.running ? t("common.yes") : t("common.no")}</span>
</div>
<div>
<span class="label">Connected</span>
<span>${whatsapp?.connected ? "Yes" : "No"}</span>
<span class="label">${t("channels.labels.connected")}</span>
<span>${whatsapp?.connected ? t("common.yes") : t("common.no")}</span>
</div>
<div>
<span class="label">Last connect</span>
<span class="label">${t("channels.whatsapp.lastConnect")}</span>
<span>
${whatsapp?.lastConnectedAt
? formatAgo(whatsapp.lastConnectedAt)
: "n/a"}
: t("common.na")}
</span>
</div>
<div>
<span class="label">Last message</span>
<span class="label">${t("channels.lastInbound")}</span>
<span>
${whatsapp?.lastMessageAt ? formatAgo(whatsapp.lastMessageAt) : "n/a"}
${whatsapp?.lastMessageAt ? formatAgo(whatsapp.lastMessageAt) : t("common.na")}
</span>
</div>
<div>
<span class="label">Auth age</span>
<span class="label">${t("channels.whatsapp.authAge")}</span>
<span>
${whatsapp?.authAgeMs != null
? formatDuration(whatsapp.authAgeMs)
: "n/a"}
: t("common.na")}
</span>
</div>
</div>
@ -84,31 +85,31 @@ export function renderWhatsAppCard(params: {
?disabled=${props.whatsappBusy}
@click=${() => props.onWhatsAppStart(false)}
>
${props.whatsappBusy ? "Working…" : "Show QR"}
${props.whatsappBusy ? t("channels.whatsapp.working") : t("channels.whatsapp.showQr")}
</button>
<button
class="btn"
?disabled=${props.whatsappBusy}
@click=${() => props.onWhatsAppStart(true)}
>
Relink
${t("channels.whatsapp.relink")}
</button>
<button
class="btn"
?disabled=${props.whatsappBusy}
@click=${() => props.onWhatsAppWait()}
>
Wait for scan
${t("channels.whatsapp.waitForScan")}
</button>
<button
class="btn danger"
?disabled=${props.whatsappBusy}
@click=${() => props.onWhatsAppLogout()}
>
Logout
${t("channels.whatsapp.logout")}
</button>
<button class="btn" @click=${() => props.onRefresh(true)}>
Refresh
${t("common.refresh")}
</button>
</div>