Explorar el Código

feat(i18n): add zh-TW locale and sync 74 missing keys in zh-CN (#1017) (#1025)

* fix(i18n): sync zh-CN to match en structure

- Add 74 missing keys covering login.resetPassword, printers.firmwareModal
  badges, settings.spoolbuddy device management, settings.tabs.spoolbuddy,
  spoolbuddy.settings system config
- Fix fileManager.uploadFailed placeholder bug (had stray {{count}} copied
  from zipFilesFailed; en value is plain "Upload failed")

Refs #1017

* feat(i18n): add zh-TW locale and enforce 3-way parity

- Add frontend/src/i18n/locales/zh-TW.ts (Traditional Chinese, Taiwan usage)
  with full key set aligned to en.ts
- Register zh-TW in frontend/src/i18n/index.ts: import, resources,
  supportedLngs, availableLanguages
- Add frontend/scripts/check-i18n-parity.mjs: TypeScript Compiler API-based
  3-way gate checking key set equality, placeholder equality, and legacy
  _plural / _one+_other suffix handling across en / zh-CN / zh-TW
- Wire check:i18n into test:run npm script so frontend-tests CI job
  (ci.yml:227) gates future locale drift

Fixes #1017
Minidoracat hace 4 meses
padre
commit
a584e671ec

+ 3 - 2
frontend/package.json

@@ -9,9 +9,10 @@
     "lint": "eslint .",
     "lint": "eslint .",
     "preview": "vite preview",
     "preview": "vite preview",
     "test": "vitest",
     "test": "vitest",
-    "test:run": "vitest run",
+    "test:run": "vitest run && npm run check:i18n",
     "test:coverage": "vitest run --coverage",
     "test:coverage": "vitest run --coverage",
-    "test:ui": "vitest --ui"
+    "test:ui": "vitest --ui",
+    "check:i18n": "node scripts/check-i18n-parity.mjs"
   },
   },
   "dependencies": {
   "dependencies": {
     "@dnd-kit/core": "^6.3.1",
     "@dnd-kit/core": "^6.3.1",

+ 174 - 0
frontend/scripts/check-i18n-parity.mjs

@@ -0,0 +1,174 @@
+#!/usr/bin/env node
+// Verifies parity across locale files (en / zh-CN / zh-TW):
+//   1. Leaf-key sets are identical
+//   2. Each leaf's {{placeholder}} set is identical
+//   3. Plural suffixes: every en key ending in _plural / _one / _other must
+//      exist in every other locale, and other locales must not introduce an
+//      _one key that en does not have.
+// Malformed input (missing `export default`, parse errors, non-string leaves,
+// unsupported property kinds) fails loudly instead of silently passing the gate.
+// Exits 1 with a diagnostic report on any failure, else exits 0.
+
+import fs from 'node:fs';
+import path from 'node:path';
+import url from 'node:url';
+
+const scriptDir = path.dirname(url.fileURLToPath(import.meta.url));
+const frontendDir = path.resolve(scriptDir, '..');
+const localesDir = path.join(frontendDir, 'src/i18n/locales');
+const tsPath = path.join(frontendDir, 'node_modules/typescript/lib/typescript.js');
+
+const tsModule = await import(url.pathToFileURL(tsPath).href);
+const ts = tsModule.default ?? tsModule;
+
+function collectLeaves(node, prefix, leaves) {
+  if (!ts.isObjectLiteralExpression(node)) return;
+  for (const prop of node.properties) {
+    if (!ts.isPropertyAssignment(prop)) {
+      console.error(
+        `Unsupported property kind ${ts.SyntaxKind[prop.kind]} at "${prefix}" ` +
+        `(locale files must use plain \`key: value\` assignments — no spread, shorthand, methods, or accessors).`,
+      );
+      process.exit(1);
+    }
+    let name;
+    if (ts.isIdentifier(prop.name)) name = prop.name.text;
+    else if (ts.isStringLiteral(prop.name) || ts.isNoSubstitutionTemplateLiteral(prop.name)) name = prop.name.text;
+    else if (ts.isComputedPropertyName(prop.name)) {
+      console.error(`ComputedPropertyName not allowed in locale file at path "${prefix}"`);
+      process.exit(1);
+    } else {
+      console.error(`Unsupported property-name kind ${ts.SyntaxKind[prop.name.kind]} at "${prefix}"`);
+      process.exit(1);
+    }
+    const p = prefix ? `${prefix}.${name}` : name;
+    if (ts.isObjectLiteralExpression(prop.initializer)) {
+      collectLeaves(prop.initializer, p, leaves);
+    } else {
+      const value = extractStringValue(prop.initializer, p);
+      leaves.set(p, value);
+    }
+  }
+}
+
+function extractStringValue(node, keyPath) {
+  if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
+  if (ts.isTemplateExpression(node)) {
+    let out = node.head.text;
+    for (const span of node.templateSpans) {
+      out += '${' + span.expression.getText() + '}';
+      out += span.literal.text;
+    }
+    return out;
+  }
+  console.error(
+    `Non-string leaf at "${keyPath}" (kind=${ts.SyntaxKind[node.kind]}): ${node.getText()}\n` +
+    `Locale files must only contain string or template literals as leaf values.`,
+  );
+  process.exit(1);
+}
+
+function loadLocale(filePath) {
+  const src = fs.readFileSync(filePath, 'utf8');
+  const sf = ts.createSourceFile(filePath, src, ts.ScriptTarget.Latest, true);
+  if (sf.parseDiagnostics && sf.parseDiagnostics.length > 0) {
+    console.error(`${filePath}: ${sf.parseDiagnostics.length} parse error(s):`);
+    for (const d of sf.parseDiagnostics.slice(0, 10)) {
+      const msg = typeof d.messageText === 'string' ? d.messageText : d.messageText.messageText;
+      const { line, character } = sf.getLineAndCharacterOfPosition(d.start ?? 0);
+      console.error(`  ${line + 1}:${character + 1} ${msg}`);
+    }
+    process.exit(1);
+  }
+  const leaves = new Map();
+  let foundExport = false;
+  ts.forEachChild(sf, (n) => {
+    if (ts.isExportAssignment(n)) {
+      foundExport = true;
+      collectLeaves(n.expression, '', leaves);
+    }
+  });
+  if (!foundExport) {
+    console.error(`${filePath}: no \`export default\` found — locale files must use \`export default { ... }\`.`);
+    process.exit(1);
+  }
+  if (leaves.size === 0) {
+    console.error(`${filePath}: \`export default\` resolved to zero leaves — file is empty or not a nested object.`);
+    process.exit(1);
+  }
+  return leaves;
+}
+
+const locales = {
+  en: loadLocale(path.join(localesDir, 'en.ts')),
+  'zh-CN': loadLocale(path.join(localesDir, 'zh-CN.ts')),
+  'zh-TW': loadLocale(path.join(localesDir, 'zh-TW.ts')),
+};
+
+let failed = false;
+const MAX_REPORT = 20;
+
+function reportList(label, items) {
+  if (items.length === 0) return;
+  failed = true;
+  console.error(`\n[${label}] (${items.length})`);
+  items.slice(0, MAX_REPORT).forEach((i) => console.error(`  ${i}`));
+  if (items.length > MAX_REPORT) console.error(`  ... and ${items.length - MAX_REPORT} more`);
+}
+
+// Check 1: key set equality
+const enKeys = new Set(locales.en.keys());
+for (const [code, map] of Object.entries(locales)) {
+  if (code === 'en') continue;
+  const keys = new Set(map.keys());
+  const missing = [...enKeys].filter((k) => !keys.has(k)).sort();
+  const extra = [...keys].filter((k) => !enKeys.has(k)).sort();
+  reportList(`${code}: missing keys vs en`, missing);
+  reportList(`${code}: extra keys vs en`, extra);
+}
+
+// Check 2: placeholder set equality per leaf
+const placeholderRe = /\{\{[^{}]+\}\}/g;
+for (const [code, map] of Object.entries(locales)) {
+  if (code === 'en') continue;
+  const mismatches = [];
+  for (const [key, enValue] of locales.en) {
+    const otherValue = map.get(key);
+    if (otherValue === undefined) continue;
+    const enPlaceholders = new Set((enValue.match(placeholderRe) ?? []));
+    const otherPlaceholders = new Set((otherValue.match(placeholderRe) ?? []));
+    const missingPh = [...enPlaceholders].filter((p) => !otherPlaceholders.has(p));
+    const extraPh = [...otherPlaceholders].filter((p) => !enPlaceholders.has(p));
+    if (missingPh.length || extraPh.length) {
+      mismatches.push(`${key}: en=${[...enPlaceholders].join(',') || '∅'} vs ${code}=${[...otherPlaceholders].join(',') || '∅'}`);
+    }
+  }
+  reportList(`${code}: placeholder mismatch vs en`, mismatches);
+}
+
+// Check 3: plural suffix presence + reverse _one guard
+for (const [code, map] of Object.entries(locales)) {
+  if (code === 'en') continue;
+  const pluralIssues = [];
+  for (const key of enKeys) {
+    if (key.endsWith('_plural') && !map.has(key)) pluralIssues.push(`missing _plural key: ${key}`);
+    if (key.endsWith('_one') && !map.has(key)) pluralIssues.push(`missing _one key: ${key}`);
+    if (key.endsWith('_other') && !map.has(key)) pluralIssues.push(`missing _other key: ${key}`);
+  }
+  for (const key of map.keys()) {
+    if (key.endsWith('_one') && !enKeys.has(key)) {
+      pluralIssues.push(`unexpected _one not present in en: ${key}`);
+    }
+  }
+  reportList(`${code}: plural key mismatch`, pluralIssues);
+}
+
+if (failed) {
+  console.error('\n❌ i18n parity check failed.');
+  process.exit(1);
+}
+
+console.log(`All locales in parity (en / zh-CN / zh-TW):`);
+for (const [code, map] of Object.entries(locales)) {
+  console.log(`  ${code}: ${map.size} leaves`);
+}

+ 4 - 1
frontend/src/i18n/index.ts

@@ -10,6 +10,7 @@ import ja from './locales/ja';
 import it from './locales/it';
 import it from './locales/it';
 import ptBR from './locales/pt-BR';
 import ptBR from './locales/pt-BR';
 import zhCN from './locales/zh-CN';
 import zhCN from './locales/zh-CN';
+import zhTW from './locales/zh-TW';
 
 
 const resources = {
 const resources = {
   en: { translation: en },
   en: { translation: en },
@@ -19,6 +20,7 @@ const resources = {
   it: { translation: it },
   it: { translation: it },
   'pt-BR': { translation: ptBR },
   'pt-BR': { translation: ptBR },
   'zh-CN': { translation: zhCN },
   'zh-CN': { translation: zhCN },
+  'zh-TW': { translation: zhTW },
 };
 };
 
 
 i18n
 i18n
@@ -27,7 +29,7 @@ i18n
   .init({
   .init({
     resources,
     resources,
     fallbackLng: 'en',
     fallbackLng: 'en',
-    supportedLngs: ['en', 'de', 'fr', 'ja', 'it', 'pt-BR', 'zh-CN'],
+    supportedLngs: ['en', 'de', 'fr', 'ja', 'it', 'pt-BR', 'zh-CN', 'zh-TW'],
 
 
     detection: {
     detection: {
       // Order of detection methods
       // Order of detection methods
@@ -58,4 +60,5 @@ export const availableLanguages = [
   { code: 'it', name: 'Italian', nativeName: 'Italiano' },
   { code: 'it', name: 'Italian', nativeName: 'Italiano' },
   { code: 'pt-BR', name: 'Portuguese (Brazil)', nativeName: 'Português (Brasil)' },
   { code: 'pt-BR', name: 'Portuguese (Brazil)', nativeName: 'Português (Brasil)' },
   { code: 'zh-CN', name: 'Chinese (Simplified)', nativeName: '简体中文' },
   { code: 'zh-CN', name: 'Chinese (Simplified)', nativeName: '简体中文' },
+  { code: 'zh-TW', name: 'Chinese (Traditional)', nativeName: '繁體中文' },
 ];
 ];

+ 80 - 1
frontend/src/i18n/locales/zh-CN.ts

@@ -566,6 +566,13 @@ export default {
       uploadFirmware: '上传固件',
       uploadFirmware: '上传固件',
       uploadFailed: '上传启动失败:{{error}}',
       uploadFailed: '上传启动失败:{{error}}',
       uploadedToast: '固件已上传!请在打印机屏幕上触发更新。',
       uploadedToast: '固件已上传!请在打印机屏幕上触发更新。',
+      availableVersions: '可用版本',
+      usable: '可用',
+      unavailable: '不可用',
+      installed: '已安装',
+      newerBadge: '较新',
+      olderBadge: '较旧',
+      currentBadge: '当前',
     },
     },
     accessCodePlaceholder: '留空以保持当前值',
     accessCodePlaceholder: '留空以保持当前值',
     // ROI editor
     // ROI editor
@@ -1335,6 +1342,7 @@ export default {
       network: '网络',
       network: '网络',
       apiKeys: 'API 密钥',
       apiKeys: 'API 密钥',
       virtualPrinter: '虚拟打印机',
       virtualPrinter: '虚拟打印机',
+      spoolbuddy: 'SpoolBuddy',
       failureDetection: '故障检测',
       failureDetection: '故障检测',
       users: '身份验证',
       users: '身份验证',
       backup: '备份',
       backup: '备份',
@@ -1343,6 +1351,50 @@ export default {
       twoFa: '双因素认证',
       twoFa: '双因素认证',
       oidc: 'SSO / OIDC',
       oidc: 'SSO / OIDC',
     },
     },
+    spoolbuddy: {
+      infoTitle: 'SpoolBuddy 设备',
+      infoBody: 'SpoolBuddy kiosk 通过心跳自动注册。如果设备不再使用,或守护进程崩溃遗留了陈旧的重复项,可在此注销。',
+      duplicatesTitle: '已注册 {{count}} 台设备',
+      duplicatesBody: 'kiosk 界面只使用最先注册的设备。如果其中有因崩溃遗留的陈旧重复项,请注销它——在线设备会在下次心跳时重新注册自己。',
+      empty: '尚未注册任何 SpoolBuddy 设备。',
+      online: '在线',
+      offline: '离线',
+      unregister: '注销',
+      unregisterSuccess: '设备已注销',
+      unregisterError: '注销设备失败',
+      confirmTitle: '注销 SpoolBuddy 设备?',
+      confirmBody: '将从数据库中移除 "{{hostname}}" ({{deviceId}})。如果设备在线,会在下次心跳时重新注册自己。',
+      ipAddress: 'IP 地址',
+      firmware: '固件',
+      lastSeen: '上次在线',
+      daemonUptime: '守护进程运行时间',
+      systemUptime: '系统运行时间',
+      never: '从未',
+      nfc: 'NFC',
+      scale: '秤',
+      cpuTemp: 'CPU 温度',
+      memory: '内存',
+      disk: '磁盘',
+      // Device actions
+      update: '更新',
+      updateConfirmTitle: '更新 SpoolBuddy 守护进程?',
+      updateConfirmBody: '对 "{{hostname}}" 触发软件更新?更新完成后守护进程将重启。',
+      restartBrowser: '重启浏览器',
+      restartBrowserConfirmTitle: '重启 kiosk 浏览器?',
+      restartBrowserConfirmBody: '在 "{{hostname}}" 上重启 kiosk 浏览器?显示将短暂黑屏。',
+      restartDaemon: '重启守护进程',
+      restartDaemonConfirmTitle: '重启 SpoolBuddy 守护进程?',
+      restartDaemonConfirmBody: '在 "{{hostname}}" 上重启 SpoolBuddy 守护进程?设备将离线几秒钟。',
+      reboot: '重启',
+      rebootConfirmTitle: '重启设备?',
+      rebootConfirmBody: '重启 "{{hostname}}"?设备将离线约一分钟。',
+      shutdown: '关机',
+      shutdownConfirmTitle: '关闭设备?',
+      shutdownConfirmBody: '关闭 "{{hostname}}"?您需要物理访问才能重新开机。',
+      commandConfirm: '确认',
+      commandQueued: '命令已加入队列',
+      commandError: '发送命令失败',
+    },
     ldap: {
     ldap: {
       title: 'LDAP 认证',
       title: 'LDAP 认证',
       enabledDesc: 'LDAP 认证已启用',
       enabledDesc: 'LDAP 认证已启用',
@@ -2292,6 +2344,20 @@ export default {
     resetStep3: '他们可以为您设置一个临时密码',
     resetStep3: '他们可以为您设置一个临时密码',
     resetStep4: '使用新密码登录并在设置中修改密码',
     resetStep4: '使用新密码登录并在设置中修改密码',
     gotIt: '知道了',
     gotIt: '知道了',
+    resetPassword: {
+      title: '设置新密码',
+      subtitle: '请在下方输入并确认您的新密码。',
+      newPassword: '新密码',
+      newPasswordPlaceholder: '至少 8 个字符',
+      confirmPassword: '确认密码',
+      confirmPasswordPlaceholder: '重复输入新密码',
+      saving: '保存中…',
+      submit: '设置新密码',
+      backToLogin: '返回登录',
+      passwordsDoNotMatch: '密码不匹配',
+      passwordTooShort: '密码至少需要 8 个字符',
+      resetFailed: '密码重置失败。链接可能已过期。',
+    },
     twoFA: {
     twoFA: {
       title: '两步验证',
       title: '两步验证',
       subtitle: '您的账户已启用两步验证。请在下方输入验证码。',
       subtitle: '您的账户已启用两步验证。请在下方输入验证码。',
@@ -2792,7 +2858,7 @@ export default {
     willBeExtracted: '将被解压',
     willBeExtracted: '将被解压',
     filesExtracted: '已解压 {{count}} 个文件',
     filesExtracted: '已解压 {{count}} 个文件',
     uploadComplete: '上传完成:{{succeeded}} 个成功',
     uploadComplete: '上传完成:{{succeeded}} 个成功',
-    uploadFailed: '{{count}} 个失败',
+    uploadFailed: '上传失败',
     zipFilesFailed: '{{count}} 个文件失败',
     zipFilesFailed: '{{count}} 个文件失败',
     uploading: '上传中...',
     uploading: '上传中...',
     changeLink: '更改链接...',
     changeLink: '更改链接...',
@@ -4825,6 +4891,19 @@ export default {
       deviceInfo: '设备信息',
       deviceInfo: '设备信息',
       hostname: '主机',
       hostname: '主机',
       uptime: '运行时间',
       uptime: '运行时间',
+      systemConfig: '后端与认证',
+      backendUrl: 'Bambuddy 后端 URL',
+      apiToken: 'API 令牌',
+      apiTokenPlaceholder: '输入 API 令牌',
+      saveConfig: '保存配置',
+      systemQueued: '配置已加入队列。',
+      nfcDiagnostic: 'NFC 诊断',
+      scaleDiagnostic: '秤诊断',
+      readTagDiagnostic: '读取标签诊断',
+      testNfc: '测试读卡器',
+      testScale: '测试精度',
+      testReadTag: '读取标签',
+      systemFieldsRequired: '后端 URL 为必填项。',
       // Display tab
       // Display tab
       brightness: '亮度',
       brightness: '亮度',
       saved: '已保存',
       saved: '已保存',

+ 5061 - 0
frontend/src/i18n/locales/zh-TW.ts

@@ -0,0 +1,5061 @@
+export default {
+  // Navigation
+  nav: {
+    printers: '印表機',
+    archives: '歸檔',
+    queue: '佇列',
+    stats: '統計',
+    profiles: '設定檔案',
+    maintenance: '維護',
+    projects: '專案',
+    inventory: '耗材',
+    files: '檔案管理器',
+    notifications: '通知',
+    settings: '設定',
+    system: '系統',
+    collapseSidebar: '收起側邊欄',
+    expandSidebar: '展開側邊欄',
+    update: '更新',
+    updateAvailable: '有可用更新:v{{version}}',
+    updateAvailableBanner: '版本 {{version}} 已發布!',
+    viewUpdate: '檢視更新',
+    viewOnGithub: '在 GitHub 上檢視',
+    keyboardShortcuts: '鍵盤快捷鍵 (?)',
+    switchToLight: '切換到淺色模式',
+    switchToDark: '切換到深色模式',
+    smartSwitches: '智慧開關',
+    logout: '登出',
+  },
+
+  // Common
+  common: {
+    save: '儲存',
+    saving: '儲存中...',
+    cancel: '取消',
+    delete: '刪除',
+    edit: '編輯',
+    add: '新增',
+    close: '關閉',
+    confirm: '確認',
+    loading: '載入中...',
+    error: '錯誤',
+    success: '成功',
+    warning: '警告',
+    enabled: '已啟用',
+    disabled: '已停用',
+    yes: '是',
+    no: '否',
+    on: '開',
+    off: '關',
+    all: '全部',
+    none: '無',
+    search: '搜尋',
+    filter: '篩選',
+    sort: '排序',
+    refresh: '重新整理',
+    download: '下載',
+    upload: '上傳',
+    uploading: '上傳中...',
+    uploadFailed: '上傳失敗',
+    actions: '操作',
+    status: '狀態',
+    name: '名稱',
+    description: '描述',
+    date: '日期',
+    time: '時間',
+    hours: '小時',
+    minutes: '分鐘',
+    seconds: '秒',
+    days: '天',
+    enable: '啟用',
+    disable: '停用',
+    permissions: '權限',
+    noPrinters: '未設定印表機',
+    noData: '尚無資料',
+    linkNotFound: '未找到連結',
+    required: '必填',
+    optional: '可選',
+    dismiss: '關閉',
+    apply: '套用',
+    reset: '重設',
+    export: '匯出',
+    import: '匯入',
+    clear: '清除',
+    selectAll: '全選',
+    deselectAll: '取消全選',
+    noChange: '— 不更改 —',
+    unchanged: '未更改',
+    unassigned: '未分配',
+    unknown: '未知',
+    unknownError: '未知錯誤',
+    today: '今天',
+    tomorrow: '明天',
+    asap: '儘快',
+    overdue: '已逾期',
+    now: '現在',
+    collapse: '收起',
+    expand: '展開',
+    viewArchive: '檢視歸檔',
+    viewInFileManager: '在檔案管理器中檢視',
+    addedBy: '由 {{username}} 新增',
+    prints: '次列印',
+    more: '還有 {{count}} 個',
+    ascending: '升序',
+    descending: '降序',
+    back: '返回',
+    copy: '複製',
+    copied: '已複製!',
+    printer: '印表機',
+    remove: '移除',
+    type: '類型',
+    print: '列印',
+    rename: '重新命名',
+    move: '移動',
+    create: '建立',
+    duplicate: '複製',
+    left: '左',
+    right: '右',
+  },
+
+  // Printers page
+  printers: {
+    title: '印表機',
+    addPrinter: '新增印表機',
+    editPrinter: '編輯印表機',
+    deletePrinter: '刪除印表機',
+    printerName: '印表機名稱',
+    serialNumber: '序列號',
+    ipAddress: 'IP 位址 / 主機名稱',
+    accessCode: '存取碼',
+    model: '型號',
+    nozzleCount: '噴嘴數量',
+    autoArchive: '自動歸檔',
+    status: {
+      available: '可用',
+      idle: '空閒',
+      printing: '列印中',
+      paused: '已暫停',
+      offline: '離線',
+      problem: '故障',
+      error: '錯誤',
+      finished: '已完成',
+      unknown: '未知',
+    },
+    temperatures: {
+      nozzle: '噴嘴',
+      bed: '熱床',
+      chamber: '腔室',
+    },
+    progress: '{{percent}}% 完成',
+    timeRemaining: '剩餘 {{time}}',
+    deleteConfirm: '確定要刪除"{{name}}"嗎?',
+    maintenanceOk: '維護正常',
+    maintenanceWarning: '{{count}} 個警告',
+    maintenanceWarning_plural: '{{count}} 個警告',
+    maintenanceDue: '{{count}} 個到期',
+    maintenanceDue_plural: '{{count}} 個到期',
+    // Sort options
+    sort: {
+      name: '名稱',
+      status: '狀態',
+      model: '型號',
+      location: '位置',
+      ascending: '升序排列',
+      descending: '降序排列',
+    },
+    // Card size
+    cardSize: {
+      small: '小卡片',
+      medium: '中卡片',
+      large: '大卡片',
+      extraLarge: '超大卡片',
+    },
+    // Controls
+    hideOffline: '隱藏離線',
+    nextAvailable: '下一個可用',
+    powerOn: '開機',
+    offlinePrintersWithPlugs: '帶智慧插座的離線印表機',
+    noPrintersConfigured: '尚未設定印表機',
+    search: '搜尋印表機...',
+    noSearchResults: '沒有印表機符合您的搜尋或篩選條件',
+    filter: {
+      allStatuses: '所有狀態',
+      allLocations: '所有位置',
+    },
+    // Printer card
+    readyToPrint: '準備列印',
+    external: '外部',
+    extL: '外接左',
+    extR: '外接右',
+    deleteArchives: '刪除列印歸檔',
+    noLabel: '無標籤',
+    printPreview: '列印預覽',
+    width: '寬度',
+    height: '高度',
+    noObjectsFound: '未找到物件',
+    objectsLoadedOnPrintStart: '物件在列印開始時載入',
+    willBeSkipped: '將被跳過',
+    name: '名稱',
+    serialCannotBeChanged: '序列號無法更改',
+    locationHelp: '用於分組印表機和篩選佇列任務',
+    // WiFi signal strength
+    wifiSignal: {
+      veryWeak: '非常弱',
+      weak: '弱',
+      fair: '一般',
+      good: '良好',
+      excellent: '優秀',
+    },
+    // Maintenance
+    maintenanceUpToDate: '所有維護均已完成 - 點選檢視',
+    // Chamber light
+    chamberLightOn: '開啟腔室燈',
+    chamberLightOff: '關閉腔室燈',
+    // Files
+    files: '檔案',
+    browseFiles: '瀏覽印表機檔案',
+    // Smart plug
+    autoOffAfterPrint: '列印後自動關機',
+    autoOffExecuted: '已執行自動關機 - 開啟印表機以重設',
+    // HMS errors
+    hmsErrors: 'HMS 錯誤',
+    viewHmsErrors: '檢視 {{count}} 個 HMS 錯誤',
+    // Actions
+    resume: '繼續',
+    pause: '暫停',
+    stop: '停止',
+    camera: '攝影機',
+    skipObject: '跳過物件',
+    reconnect: '重新連線',
+    forceRefresh: '強制重新整理',
+    forceRefreshSuccess: '已請求重新整理',
+    mqttDebug: 'MQTT 偵錯',
+    printerInformation: '印表機資訊',
+    copyToClipboard: '複製',
+    copied: '已複製!',
+    state: '狀態',
+    wifiSignalLabel: 'Wi-Fi 訊號',
+    developerMode: '開發者模式',
+    enabled: '已啟用',
+    disabled: '已停用',
+    addedOn: '新增日期',
+    sdCard: 'SD 卡',
+    inserted: '已插入',
+    notInserted: '未插入',
+    totalPrintHours: '列印時長',
+    activeNozzle: '目前:{{nozzle}} 噴嘴',
+    nozzleRack: '噴嘴架',
+    nozzleDocked: '已停靠',
+    nozzleMounted: '已安裝',
+    nozzleActive: '使用中',
+    nozzleIdle: '空閒',
+    nozzleDiameter: '直徑',
+    nozzleType: '類型',
+    nozzleStatus: '狀態',
+    nozzleFilament: '耗材',
+    nozzleWear: '磨損',
+    nozzleMaxTemp: '最高溫度',
+    nozzleSerial: '序列號',
+    nozzleHardenedSteel: '硬化鋼',
+    nozzleStainlessSteel: '不鏽鋼',
+    nozzleTungstenCarbide: '碳化鎢',
+    nozzleFlow: '流量',
+    nozzleHighFlow: '高流量',
+    nozzleStandardFlow: '標準',
+    // Firmware
+    firmwareUpdate: '韌體更新',
+    firmwareInstructions: '在印表機觸控式螢幕上,前往',
+    firmwareNav: '導航到',
+    settings: '設定',
+    firmware: '韌體',
+    // Discovery
+    discoverPrinters: '發現印表機',
+    searching: '搜尋中...',
+    manualEntry: '手動輸入',
+    addFromCloud: '從雲端新增',
+    // Toast messages
+    toast: {
+      printerDeleted: '印表機已刪除',
+      missingSpoolAssignment: '已在{{printer}}上開始列印。以下料槽未分配耗材: {{slots}}',
+      printerAdded: '印表機已新增',
+      printerUpdated: '印表機已更新',
+      failedToDelete: '刪除印表機失敗',
+      failedToAdd: '新增印表機失敗',
+      failedToUpdate: '更新印表機失敗',
+      commandSent: '命令已傳送',
+      failedToSendCommand: '傳送命令失敗',
+      turnedOn: '{{name}} 已開啟',
+      failedToPowerOn: '開啟 {{name}} 失敗',
+      scriptTriggered: '腳本已觸發',
+      printStopped: '列印已停止',
+      printPaused: '列印已暫停',
+      printResumed: '列印已繼續',
+      referenceDeleted: '參考已刪除',
+      detectionAreaSaved: '檢測區域已儲存',
+      failedToRunScript: '執行腳本失敗',
+      failedToStopPrint: '停止列印失敗',
+      failedToPausePrint: '暫停列印失敗',
+      failedToResumePrint: '繼續列印失敗',
+      failedToControlChamberLight: '控制腔室燈失敗',
+      failedToSetSpeed: '設定列印速度失敗',
+      failedToUpdateSetting: '更新設定失敗',
+      failedToSkipObjects: '跳過物件失敗',
+      failedToRereadRfid: '重新讀取 RFID 失敗',
+      failedToCheckPlate: '檢查列印板失敗',
+      failedToUpdateLabel: '更新標籤失敗',
+      failedToDeleteReference: '刪除參考失敗',
+      failedToSaveDetectionArea: '儲存檢測區域失敗',
+      plateCheckEnabled: '列印板檢查已啟用',
+      plateCheckDisabled: '列印板檢查已停用',
+      calibrationSaved: '校準已儲存!',
+      calibrationFailed: '校準失敗',
+      rfidRereadInitiated: '已發起 RFID 重新讀取',
+    },
+    // Connection status
+    connection: {
+      connected: '已連線',
+      offline: '離線',
+    },
+    // Queue info
+    queue: {
+      inQueue: '佇列中有 {{count}} 個列印任務',
+      inQueue_plural: '佇列中有 {{count}} 個列印任務',
+    },
+    // Controls section
+    controls: '控制',
+    // RFID
+    rfid: {
+      reread: '重新讀取 RFID',
+    },
+    bedJog: {
+      title: '移動熱床',
+      bed: '熱床',
+      step: '步長 (mm)',
+      up: '熱床上移',
+      down: '熱床下移',
+      disabledWhilePrinting: '列印中已停用',
+      notHomedTitle: '印表機未歸零',
+      notHomedMessage: '自上次列印以來 Z 軸尚未歸零。請先歸零 Z 以確保安全定位,或者直接移動 — 軟限位將被繞過。',
+      homeZ: '歸零 Z',
+      moveAnyway: '強制移動',
+      homingStarted: 'Z 軸歸零中…',
+    },
+    // Permissions
+    permission: {
+      noAdd: '您沒有新增印表機的權限',
+      noEdit: '您沒有編輯印表機的權限',
+      noDelete: '您沒有刪除印表機的權限',
+      noControl: '您沒有控制印表機的權限',
+      noFiles: '您沒有存取印表機檔案的權限',
+      noAmsRfid: '您沒有重新讀取 AMS RFID 的權限',
+      noSmartPlugControl: '您沒有控制智慧插座的權限',
+      noCamera: '您沒有檢視攝影機的權限',
+    },
+    // Add/Edit modal
+    modal: {
+      addTitle: '新增印表機',
+      editTitle: '編輯印表機',
+      myPrinter: '我的印表機',
+      selectModel: '選擇型號...',
+      locationGroup: '位置 / 分組(可選)',
+      locationPlaceholder: '例如:工作室、辦公室、地下室',
+      autoArchiveLabel: '自動歸檔已完成的列印',
+      fromPrinterSettings: '來自印表機設定',
+      modelOptional: '型號(可選)',
+      saveChanges: '儲存更改',
+    },
+    // Skip objects
+    skipObjects: {
+      tooltip: '跳過物件',
+      onlyWhilePrinting: '跳過物件(僅在列印時)',
+      requiresMultiple: '跳過物件(需要2個以上物件)',
+      title: '跳過物件',
+      matchIdsInfo: '將 ID 與印表機螢幕上的 ID 進行對照',
+      printerShowsIds: '印表機螢幕上顯示列印板上物件的 ID',
+      skipSelected: '跳過所選',
+      skipping: '跳過中...',
+      noObjectsSelected: '未選擇物件',
+      selectObjectsToSkip: '選擇要從目前列印中跳過的物件',
+      skipped: '已跳過',
+      objectsSkipped: '物件已跳過',
+      activeCount: '{{count}} 個活躍',
+      waitForLayer: '等待第2層以上才能跳過物件(目前第 {{layer}} 層)',
+      skip: '跳過',
+      confirmTitle: '跳過物件?',
+      confirmMessage: '確定要跳過"{{name}}"嗎?此操作無法復原。',
+    },
+    // Confirm modals
+    confirm: {
+      deleteTitle: '刪除印表機',
+      deleteMessage: '確定要刪除"{{name}}"嗎?這將移除所有連線設定。',
+      deleteArchivesNote: '此印表機的所有列印歷史將被永久刪除。',
+      keepArchivesNote: '列印歷史將保留,但不再與此印表機連結。',
+      stopTitle: '停止列印',
+      stopMessage: '確定要停止"{{name}}"上的目前列印嗎?這將取消列印任務。',
+      stopButton: '停止列印',
+      pauseTitle: '暫停列印',
+      pauseMessage: '確定要暫停"{{name}}"上的目前列印嗎?',
+      pauseButton: '暫停列印',
+      resumeTitle: '繼續列印',
+      resumeMessage: '確定要繼續"{{name}}"上的列印嗎?',
+      resumeButton: '繼續列印',
+      powerOnTitle: '開啟印表機',
+      powerOnMessage: '確定要開啟"{{name}}"的電源嗎?',
+      powerOnButton: '開機',
+      powerOffTitle: '關閉印表機',
+      powerOffMessage: '確定要關閉"{{name}}"的電源嗎?',
+      powerOffWarning: '警告:"{{name}}"正在列印中!確定要關閉電源嗎?這將中斷列印並可能損壞印表機。',
+      powerOffButton: '關機',
+    },
+    // Bulk actions
+    bulk: {
+      select: '選擇',
+      selectAll: '全選',
+      selectByLocation: '按位置選擇',
+      selected: '已選擇{{count}}臺',
+      actions: {
+        stop: '停止',
+        pause: '暫停',
+        resume: '繼續',
+        clearPlate: '清除列印床',
+        clearHMS: '清除通知',
+      },
+      confirm: {
+        stopTitle: '停止{{count}}個列印任務',
+        stopMessage: '這將取消{{count}}臺印表機上的活動列印任務。此操作無法復原。',
+        stopButton: '全部停止',
+        pauseTitle: '暫停{{count}}個列印任務',
+        pauseMessage: '這將暫停{{count}}臺印表機上的活動列印任務。',
+        pauseButton: '全部暫停',
+        clearPlateTitle: '清除{{count}}個列印床',
+        clearPlateMessage: '這將清除{{count}}臺印表機的列印床,可能會觸發佇列中的任務。',
+        clearPlateButton: '全部清除',
+      },
+      success: '{{action}}已在{{count}}臺印表機上完成',
+      partial: '{{succeeded}}成功,{{failed}}失敗',
+      noneApplicable: '沒有選中的印表機處於適合此操作的狀態',
+      selectByState: '按狀態選擇',
+    },
+    // Discovery
+    discovery: {
+      title: '發現印表機',
+      searching: '搜尋中...',
+      scanning: '掃描中...',
+      scanProgress: '掃描中... {{scanned}}/{{total}}',
+      foundPrinters: '發現 {{count}} 臺印表機',
+      noPrintersFound: '未找到印表機',
+      noPrintersFoundSubnet: '在指定子網中未找到印表機。',
+      noPrintersFoundNetwork: '在網路上未找到印表機。',
+      allConfigured: '所有發現的印表機已設定完畢。',
+      alreadyAdded: '已新增',
+      select: '選擇',
+      manualEntry: '手動輸入',
+      addFromCloud: '從雲端新增',
+      subnetToScan: '要掃描的子網',
+      dockerNote: '偵測到 Docker 環境。請以 CIDR 格式輸入印表機所在子網。需要在 docker-compose.yml 中設定 network_mode: host。',
+      scanSubnet: '掃描子網查詢印表機',
+      discoverNetwork: '在網路上發現印表機',
+      scanningSubnet: '正在掃描子網查詢拓竹印表機...',
+      scanningNetwork: '正在掃描網路...',
+      serialRequired: '需要序列號',
+      unknown: '未知',
+      failedToStart: '啟動發現失敗',
+    },
+    // AMS Drying
+    drying: {
+      start: '開始乾燥',
+      stop: '停止乾燥',
+      temperature: '溫度',
+      duration: '時長',
+      hours: '小時',
+      timeRemaining: '剩餘 {{time}}',
+      active: '乾燥中',
+      notSupported: '不支援乾燥',
+      powerRequired: '連線AMS電源介面卡以啟用乾燥',
+      startingDrying: '正在啟動乾燥...',
+      stoppingDrying: '正在停止乾燥...',
+      rotateTray: '乾燥時旋轉料盤',
+    },
+    // Filaments section
+    filaments: '耗材',
+    // Camera
+    openCameraOverlay: '開啟攝影機疊加層',
+    openCameraWindow: '在新視窗中開啟攝影機',
+    // Firmware
+    firmwareUpdateAvailable: '韌體更新可用:{{current}} → {{latest}}',
+    firmwareUpToDate: '韌體 {{version}} — 已是最新',
+    firmwareUpdateButton: '更新',
+    // Plate detection
+    plateDetection: {
+      noPermission: '您沒有更新印表機的權限',
+      enabledClick: '列印板檢查已啟用 - 點選停用',
+      disabledClick: '列印板檢查已停用 - 點選啟用',
+      manageCalibration: '管理列印板檢測校準',
+      calibrationRequired: '需要校準',
+      calibrationInstructions: '請確保列印板<strong>完全空置</strong>,然後點選校準。',
+      calibrationDescription: '校準會拍攝空置列印板的參考影像。後續檢查將與此參考進行比較以檢測物體。',
+      calibrationTip: '<strong>提示:</strong>您最多可以為不同的列印板儲存5個校準。系統會在檢查時自動使用最佳匹配。',
+      plateEmpty: '列印板似乎是空的',
+      objectsDetected: '在列印板上偵測到物體',
+      confidence: '置信度',
+      difference: '差異',
+      analysisPreview: '分析預覽:',
+      analysisLegend: '綠色框 = 檢測區域,紅色覆蓋 = 與校準的差異',
+      savedReferences: '已儲存的參考 ({{count}}/{{max}})',
+      deleteReference: '刪除參考',
+      labelPlaceholder: '標籤...',
+      clickToEdit: '{{label}} - 點選編輯',
+      clickToAddLabel: '點選新增標籤',
+    },
+    // Speed
+    speed: {
+      title: '列印速度',
+      silent: '靜音 (50%)',
+      standard: '標準 (100%)',
+      sport: '運動 (124%)',
+      ludicrous: '瘋狂 (166%)',
+    },
+    airduct: {
+      title: '風道模式',
+      cooling: '製冷',
+      heating: '加熱',
+    },
+    noSdCard: '無SD',
+    door: {
+      open: '開',
+      closed: '關',
+    },
+    // Fans
+    fans: {
+      partCooling: '零件冷卻風扇',
+      auxiliary: '輔助風扇',
+      chamber: '腔室風扇',
+    },
+    // HMS errors
+    clickToViewHmsErrors: '點選檢視 HMS 錯誤',
+    estimatedCompletion: '預計完成時間',
+    plateNumber: '板 {{number}}',
+    slotOptions: '槽位選項',
+    // AMS hover popup
+    amsPopup: {
+      friendlyName: 'AMS 名稱',
+      friendlyNamePlaceholder: '例如 AMS 友好名稱',
+      serialNumber: '序列號',
+      firmwareVersion: '韌體',
+      save: '儲存',
+      clear: '清除',
+      noEditPermission: '您沒有重新命名 AMS 單元的權限',
+    },
+    // Firmware modal
+    firmwareModal: {
+      title: '韌體更新',
+      titleUpToDate: '韌體資訊',
+      currentVersion: '目前版本:',
+      latestVersion: '最新版本:',
+      releaseNotes: '發布說明',
+      checkingPrereqs: '正在檢查前提條件...',
+      sdCardReady: 'SD 卡已就緒。點選下方上傳韌體。',
+      uploadedSuccess: '韌體已上傳到 SD 卡!',
+      applyInstructions: '在印表機上套用更新:',
+      step1: '在印表機觸控式螢幕上,前往<strong>設定</strong>',
+      step2: '導航到<strong>韌體</strong>',
+      step3: '選擇<strong>從 SD 卡更新</strong>',
+      step4: '更新將需要 10-20 分鐘',
+      done: '完成',
+      starting: '啟動中...',
+      uploadFirmware: '上傳韌體',
+      uploadFailed: '上傳啟動失敗:{{error}}',
+      uploadedToast: '韌體已上傳!請在印表機螢幕上觸發更新。',
+      availableVersions: '可用版本',
+      usable: '可用',
+      unavailable: '不可用',
+      installed: '已安裝',
+      newerBadge: '較新',
+      olderBadge: '較舊',
+      currentBadge: '目前',
+    },
+    accessCodePlaceholder: '留空以保持目前值',
+    // ROI editor
+    roi: {
+      title: '檢測區域 (ROI)',
+      xStart: 'X 起點',
+      yStart: 'Y 起點',
+      width: '寬度',
+      height: '高度',
+      instruction: '調整檢測區域以聚焦到列印板。預覽中的綠色框顯示目前區域。',
+    },
+    developerModeWarning: '以下印表機未啟用開發者區域網路模式:{{names}}。某些功能可能無法使用。',
+    howToEnable: '如何啟用',
+    incompatibleFile: '此檔案是為 {{slicedFor}} 切片的,但該印表機是 {{printerModel}}',
+    dropNotPrintable: '只能列印 .gcode 和 .gcode.3mf 檔案',
+    dropToPrint: '拖放以列印',
+    cannotPrint: '印表機忙碌',
+  },
+
+  // Archives page
+  archives: {
+    title: '列印歸檔',
+    searchPlaceholder: '搜尋歸檔...',
+    filterByPrinter: '按印表機篩選',
+    filterByStatus: '按狀態篩選',
+    sortBy: '排序方式',
+    sortNewest: '最新優先',
+    sortOldest: '最舊優先',
+    sortName: '名稱',
+    sortDuration: '時長',
+    sortLargest: '最大優先',
+    sortSmallest: '最小優先',
+    sortSize: '大小',
+    noArchives: '未找到歸檔',
+    noArchivesSearch: '沒有匹配搜尋的歸檔',
+    originalPrintNotVisible: '原始列印不可見 - 請嘗試清除篩選條件',
+    noArchivesYet: '尚無歸檔',
+    prints: '條列印',
+    pagination: {
+      showing: '顯示',
+      to: '至',
+      of: '共',
+      show: '每頁',
+      page: '頁',
+      all: '全部',
+    },
+    loadingArchives: '載入歸檔中...',
+    releaseToUpload: '放開以上傳',
+    showAll: '顯示全部',
+    showFavoritesOnly: '僅顯示收藏',
+    gridView: '網格檢視',
+    listView: '列表檢視',
+    calendarView: '日曆檢視',
+    logView: '列印日誌',
+    manageTags: '管理標籤',
+    showFailedPrints: '顯示失敗的列印',
+    hideFailedPrints: '隱藏失敗的列印',
+    hideDuplicates: '隱藏重複項',
+    viewOriginalPrint: '點選檢視原始列印 (#{{id}})',
+    printTime: '列印時間',
+    filamentUsed: '耗材用量',
+    cost: '成本',
+    reprint: '重新列印',
+    preview: '預覽',
+    deleteArchive: '刪除歸檔',
+    deleteConfirm: '確定要刪除此歸檔嗎?',
+    favorite: '收藏',
+    unfavorite: '取消收藏',
+    viewDetails: '檢視詳情',
+    status: {
+      completed: '已完成',
+      failed: '失敗',
+      stopped: '已停止',
+    },
+    toast: {
+      source3mfAttached: '源 3MF 已附加:{{filename}}',
+      failedUploadSource3mf: '上傳源 3MF 失敗',
+      source3mfRemoved: '源 3MF 已移除',
+      failedRemoveSource3mf: '移除源 3MF 失敗',
+      f3dAttached: 'F3D 已附加:{{filename}}',
+      failedUploadF3d: '上傳 F3D 失敗',
+      f3dRemoved: 'F3D 已移除',
+      failedRemoveF3d: '移除 F3D 失敗',
+      timelapseAttached: '縮時攝影已附加:{{filename}}',
+      timelapseAlreadyAttached: '縮時攝影已附加',
+      noMatchingTimelapse: '未找到匹配的縮時攝影',
+      failedScanTimelapse: '掃描縮時攝影失敗',
+      failedAttachTimelapse: '附加縮時攝影失敗',
+      timelapseRemoved: '縮時攝影已移除',
+      failedRemoveTimelapse: '移除縮時攝影失敗',
+      timelapseUploaded: '縮時攝影已上傳:{{filename}}',
+      failedUploadTimelapse: '上傳縮時攝影失敗',
+      archiveDeleted: '歸檔已刪除',
+      failedDeleteArchive: '刪除歸檔失敗',
+      addedToFavorites: '已新增到收藏',
+      removedFromFavorites: '已從收藏中移除',
+      projectUpdated: '專案已更新',
+      failedUpdateProject: '更新項目失敗',
+      linkCopied: '連結已複製到剪貼簿',
+      failedCopyLink: '複製連結失敗',
+      photoDeleted: '照片已刪除',
+      failedDeletePhoto: '刪除照片失敗',
+      failedDeleteArchives: '刪除歸檔失敗',
+      failedUpdateFavorites: '更新收藏失敗',
+      exportDownloaded: '匯出已下載',
+      exportFailed: '匯出失敗',
+    },
+    menu: {
+      print: '列印',
+      schedule: '排程',
+      openInBambuStudio: '在切片軟體中開啟',
+      slice: '切片',
+      externalLink: '外部連結',
+      viewOnMakerWorld: '在 MakerWorld 上檢視',
+      preview3d: '3D 預覽',
+      viewTimelapse: '檢視縮時攝影',
+      scanForTimelapse: '掃描縮時攝影',
+      uploadTimelapse: '上傳縮時攝影',
+      removeTimelapse: '移除縮時攝影',
+      downloadSource3mf: '下載源 3MF',
+      uploadSource3mf: '上傳源 3MF',
+      replaceSource3mf: '替換源 3MF',
+      removeSource3mf: '移除源 3MF',
+      uploadF3d: '上傳 F3D',
+      replaceF3d: '替換 F3D',
+      downloadF3d: '下載 F3D',
+      removeF3d: '移除 F3D',
+      download: '下載',
+      copyDownloadLink: '複製下載連結',
+      qrCode: 'QR Code',
+      viewPhotos: '檢視照片',
+      viewPhotosCount: '檢視照片 ({{count}})',
+      projectPage: '專案頁面',
+      addToFavorites: '新增到收藏',
+      removeFromFavorites: '從收藏中移除',
+      edit: '編輯',
+      goToProject: '前往專案:{{name}}',
+      addToProject: '新增到專案',
+      removeFromProject: '從專案中移除',
+      loading: '載入中...',
+      noProjectsAvailable: '無可用專案',
+      select: '選擇',
+      deselect: '取消選擇',
+      delete: '刪除',
+    },
+    permission: {
+      noReprint: '您沒有重新列印此歸檔的權限',
+      noAddToQueue: '您沒有新增到佇列的權限',
+      noUpdateArchives: '您沒有更新歸檔的權限',
+      noUploadFiles: '您沒有上傳檔案的權限',
+      noDownload: '您沒有下載歸檔的權限',
+      noCopyLink: '您沒有複製下載連結的權限',
+      noDelete: '您沒有刪除此歸檔的權限',
+      noCreate: '您沒有建立歸檔的權限',
+    },
+    card: {
+      previousPlate: '上一個板',
+      nextPlate: '下一個板',
+      plateNumber: '板 {{index}}',
+      moreOptions: '右鍵檢視更多選項',
+      addToFavorites: '新增到收藏',
+      removeFromFavorites: '從收藏中移除',
+      cancelled: '已取消',
+      failed: '失敗',
+      duplicate: '重複',
+      duplicateTitle: '此模型之前已列印過',
+      openSource3mf: '在 Bambu Studio 中開啟源 3MF(右鍵檢視更多選項)',
+      downloadF3d: '下載 Fusion 360 設計檔案',
+      viewTimelapse: '檢視縮時攝影',
+      viewPhoto: '檢視 1 張照片',
+      viewPhotos: '檢視 {{count}} 張照片',
+      openFolder: '開啟資料夾:{{name}}',
+      slicedFile: '已切片檔案 - 可以列印',
+      sourceFile: '僅原始檔 - 無 AMS 對應可用',
+      gcode: 'GCODE',
+      source: '原始檔',
+      project: '專案:{{name}}',
+      estimated: '預計:{{time}}',
+      actual: '實際:{{time}}',
+      accuracy: '準確度:{{percent}}%',
+      filament: '{{weight}}g',
+      layer: '{{count}} 層',
+      layers: '{{count}} 層',
+      object: '{{count}} 個物件',
+      objects: '{{count}} 個物件',
+      slicedFor: '為 {{model}} 切片',
+      uploadedBy: '上傳者',
+      noPermissionReprint: '您沒有重新列印的權限',
+      noFileForReprint: '無可用的 3MF 檔案 — 列印紀錄時無法從印表機下載該檔案',
+      noPermissionEdit: '您沒有編輯歸檔的權限',
+      noPermissionDelete: '您沒有刪除歸檔的權限',
+      reprint: '重新列印',
+      schedulePrint: '排程列印',
+      schedule: '排程',
+      openInBambuStudio: '在切片軟體中開啟',
+      openInBambuStudioToSlice: '在切片軟體中開啟進行切片',
+      slice: '切片',
+      externalLink: '外部連結',
+      makerWorld: 'MakerWorld:{{designer}}',
+      viewProject: '檢視專案',
+      noExternalLink: '無外部連結',
+      preview3d: '3D 預覽',
+      download: '下載',
+      edit: '編輯',
+      delete: '刪除',
+    },
+    modal: {
+      deleteArchive: '刪除歸檔',
+      deleteConfirm: '確定要刪除"{{name}}"嗎?此操作無法復原。',
+      deleteButton: '刪除',
+      removeSource3mf: '移除源 3MF',
+      removeSource3mfConfirm: '確定要從"{{name}}"中移除源 3MF 檔案嗎?這將刪除原始切片專案檔案。',
+      removeButton: '移除',
+      removeF3d: '移除 F3D',
+      removeF3dConfirm: '確定要從"{{name}}"中移除 Fusion 360 設計檔案嗎?',
+      removeTimelapse: '移除縮時攝影',
+      removeTimelapseConfirm: '確定要從"{{name}}"中移除縮時攝影影片嗎?',
+      timelapse: '{{name}} - 縮時攝影',
+      selectTimelapse: '選擇縮時攝影',
+      selectTimelapseDesc: '未找到自動匹配。請選擇此列印的縮時攝影:',
+      deleteArchives: '刪除歸檔',
+      deleteArchivesConfirm: '確定要刪除 {{count}} 個歸檔嗎?此操作無法復原。',
+      deleteCount: '刪除 {{count}} 個',
+    },
+    page: {
+      title: '歸檔',
+      printsCount: '{{filtered}} / {{total}} 次列印',
+      dropFilesHere: '將 .3mf 檔案拖放到此處',
+      releaseToUpload: '放開以上傳',
+      only3mfSupported: '僅支援 .3mf 檔案',
+      close: '關閉',
+      selected: '已選擇 {{count}} 個',
+      selectAll: '全選',
+      tags: '標籤',
+      project: '專案',
+      favorite: '收藏',
+      delete: '刪除',
+      toggledFavorites: '已切換 {{count}} 個歸檔的收藏狀態',
+      failedUpdateFavorites: '更新收藏失敗',
+      archivesDeleted: '已刪除 {{count}} 個歸檔',
+      failedDeleteArchives: '刪除歸檔失敗',
+      photoDeleted: '照片已刪除',
+      failedDeletePhoto: '刪除照片失敗',
+    },
+    list: {
+      name: '名稱',
+      printer: '印表機',
+      date: '日期',
+      size: '大小',
+      actions: '操作',
+      hasTimelapse: '有縮時攝影',
+    },
+    log: {
+      date: '日期',
+      printName: '列印名稱',
+      printer: '印表機',
+      user: '使用者',
+      status: '狀態',
+      duration: '時長',
+      filament: '耗材',
+      allPrinters: '所有印表機',
+      allUsers: '所有使用者',
+      allStatuses: '所有狀態',
+      cancelled: '已取消',
+      skipped: '已跳過',
+      dateFrom: '從',
+      dateTo: '到',
+      noEntries: '未找到列印日誌條目',
+      showing: '顯示 {{count}} / {{total}} 條',
+      rowsPerPage: '行數',
+      page: '頁',
+      prev: '上一頁',
+      next: '下一頁',
+      clearLog: '清除日誌',
+      clearLogTitle: '清除列印日誌',
+      clearLogConfirm: '所有列印日誌條目將被永久刪除。歸檔和佇列項目不受影響。此操作無法復原。確定要繼續嗎?',
+      clearLogButton: '全部清除',
+      cleared: '已清除 {{count}} 條日誌',
+      clearFailed: '清除列印日誌失敗',
+    },
+  },
+
+  // Queue page
+  queue: {
+    title: '列印佇列',
+    subtitle: '排程和管理您的列印任務',
+    addToQueue: '新增到佇列',
+    // Print modal
+    print: '列印',
+    reprint: '重新列印',
+    schedulePrint: '排程列印',
+    editQueueItem: '編輯佇列項目',
+    printToPrinters: '列印到 {{count}} 臺印表機',
+    queueToPrinters: '佇列到 {{count}} 臺印表機',
+    queueSelectedPlates: '將 {{count}} 個熱床加入佇列',
+    selectAllPlates: '選擇全部 {{count}} 個熱床',
+    deselectAll: '取消全選',
+    printQueued: '已加入列印佇列',
+    itemsQueued: '{{count}} 個任務已加入佇列',
+    sending: '傳送中...',
+    sendingProgress: '傳送中 {{current}}/{{total}}...',
+    adding: '新增中...',
+    addingProgress: '新增中 {{current}}/{{total}}...',
+    savingProgress: '儲存中 {{current}}/{{total}}...',
+    clearQueue: '清空佇列',
+    clearHistory: '清除歷史',
+    emptyQueue: '佇列為空',
+    position: '位置',
+    scheduledTime: '排程時間',
+    moveUp: '上移',
+    moveDown: '下移',
+    startNow: '立即開始',
+    printingInProgress: '列印進行中...',
+    viewArchive: '檢視歸檔',
+    viewInFileManager: '在檔案管理器中檢視',
+    itemCount: '{{count}} 個項目',
+    itemCount_plural: '{{count}} 個項目',
+    dragToReorder: '拖曳以重新排序(僅限盡快)',
+    reorderHint: '位置僅影響"儘快"項目。排程項目按設定時間執行。',
+    sjf: {
+      label: 'SJF',
+      tooltip: '最短任務優先 — 排程器優先處理較短的列印任務',
+    },
+    addedBy: '由 {{name}} 新增',
+    nextInQueue: '佇列中的下一個',
+    clearPlate: '清理列印板並開始下一個',
+    clearPlateSuccess: '列印板已清理 — 準備進行下一個列印',
+    plateReady: '列印板已清理 — 準備進行下一個列印',
+    plateNumber: '板 {{index}}',
+    // Batch / quantity
+    quantity: '數量',
+    quantityHint: '建立 {{count}} 個佇列項目',
+    activeBatches: '活躍批次',
+    batchProgress: '已完成 {{completed}}/{{total}}',
+    cancelBatch: '取消剩餘',
+    batchCancelled: '已取消剩餘批次項目',
+    cancelBatchConfirmTitle: '取消批次',
+    cancelBatchConfirmMessage: '取消此批次中所有剩餘的待處理項目?',
+    batch: '批次',
+    // Sections
+    sections: {
+      currentlyPrinting: '正在列印',
+      queued: '佇列中',
+      history: '歷史',
+    },
+    // Status
+    status: {
+      pending: '等待中',
+      waiting: '等待中',
+      printing: '列印中',
+      paused: '已暫停',
+      completed: '已完成',
+      failed: '失敗',
+      skipped: '已跳過',
+      cancelled: '已取消',
+    },
+    // Summary cards
+    summary: {
+      printing: '列印中',
+      queued: '佇列中',
+      totalTime: '總佇列時間',
+      totalWeight: '總佇列重量',
+      history: '歷史',
+    },
+    // Filters
+    filter: {
+      allPrinters: '所有印表機',
+      unassigned: '未分配',
+      allStatus: '所有狀態',
+      allLocations: '所有位置',
+      any: '任意',
+    },
+    // Sort
+    sort: {
+      byPosition: '按位置排序',
+      byName: '按名稱排序',
+      byPrinter: '按印表機排序',
+      bySchedule: '按排程排序',
+      byDate: '按日期排序',
+      ascendingOldest: '升序(最舊優先)',
+      descendingNewest: '降序(最新優先)',
+    },
+    // Badges
+    badges: {
+      staged: '已暫存',
+      requiresPrevious: '需要前一個成功',
+      autoPowerOff: '自動關機',
+      gcodeInjection: 'G-code',
+    },
+    // Empty state
+    empty: {
+      title: '沒有排程的列印',
+      description: '從歸檔頁面使用右鍵選單中的"排程"選項來排程列印,或拖放檔案開始。',
+    },
+    // Time
+    time: {
+      asap: '儘快',
+      overdue: '已逾期',
+      now: '現在',
+      lessThanMinute: '不到一分鐘',
+      inMinutes: '{{count}} 分鐘後',
+      inHours: '{{count}} 小時後',
+    },
+    // Actions
+    actions: {
+      stopPrint: '停止列印',
+      startPrint: '開始列印',
+      requeue: '重新佇列',
+    },
+    // Bulk edit
+    bulkEdit: {
+      title: '編輯 {{count}} 個項目',
+      title_plural: '編輯 {{count}} 個項目',
+      description: '僅更改的設定將套用於所選項目。',
+      printer: '印表機',
+      noChange: '— 不更改 —',
+      queueOptions: '佇列選項',
+      staged: '暫存(手動開始)',
+      autoPowerOff: '列印後自動關機',
+      requirePrevious: '要求前一個成功',
+      printOptions: '列印選項',
+      bedLevelling: '熱床調平',
+      flowCalibration: '流量校準',
+      vibrationCalibration: '振動校準',
+      layerInspection: '首層檢查',
+      timelapse: '縮時攝影',
+      useAms: '使用 AMS',
+      applyChanges: '套用更改',
+      selectAll: '全選',
+      deselectAll: '取消全選',
+      selected: '已選擇 {{count}} 個',
+      editSelected: '編輯所選',
+      cancelSelected: '取消所選',
+    },
+    // Confirmations
+    confirm: {
+      cancelTitle: '取消排程列印',
+      cancelMessage: '確定要取消"{{name}}"嗎?',
+      stopTitle: '停止列印',
+      stopMessage: '確定要停止目前列印"{{name}}"嗎?這將取消印表機上的列印任務。',
+      removeTitle: '從歷史中移除',
+      removeMessage: '確定要從佇列歷史中移除"{{name}}"嗎?',
+      clearHistoryTitle: '清除歷史',
+      clearHistoryMessage: '確定要從歷史中移除所有 {{count}} 個項目嗎?',
+      cancelButton: '取消列印',
+      stopButton: '停止列印',
+      thisPrint: '此列印',
+      thisItem: '此項目',
+    },
+    // Toast messages
+    toast: {
+      cancelled: '佇列項目已取消',
+      cancelFailed: '取消項目失敗',
+      removed: '佇列項目已移除',
+      removeFailed: '移除項目失敗',
+      stopped: '列印已停止',
+      stopFailed: '停止列印失敗',
+      released: '列印已加入佇列',
+      startFailed: '開始列印失敗',
+      reorderFailed: '重新排序佇列失敗',
+      historyCleared: '已清除 {{count}} 條歷史紀錄',
+      clearHistoryFailed: '清除歷史失敗',
+      updateFailed: '更新項目失敗',
+      bulkCancelled: '已取消 {{count}} 個項目',
+      bulkCancelFailed: '批次取消項目失敗',
+    },
+    // Timeline view
+    timeline: {
+      listView: '列表',
+      timelineView: '時間線',
+      unassigned: '未分配',
+      noData: '當天沒有計畫的列印任務',
+      allDoneBy: '所有列印預計在 {{time}} 前完成',
+      staged: '暫存',
+      filterAll: '全部顯示',
+      filterPrinting: '列印中',
+      filterQueued: '佇列中',
+      time: {
+        anyMoment: '即將完成',
+        minutesLeft: '剩餘{{minutes}} 分鐘',
+        hoursLeft: '剩餘{{hours}}小時',
+        hoursMinutesLeft: '剩餘{{hours}}小時{{minutes}} 分鐘',
+      },
+      day: {
+        previous: '前一天',
+        next: '後一天',
+        today: '今天',
+      },
+    },
+    // Permissions
+    permissions: {
+      noStopPrint: '您沒有停止列印的權限',
+      noStartPrint: '您沒有開始列印的權限',
+      noEdit: '您沒有編輯此佇列項目的權限',
+      noCancel: '您沒有取消此佇列項目的權限',
+      noRequeue: '您沒有重新佇列的權限',
+      noRemove: '您沒有移除此佇列項目的權限',
+      noClearHistory: '您沒有清除所有歷史的權限',
+      noEditItems: '您沒有編輯佇列項目的權限',
+      noCancelItems: '您沒有取消佇列項目的權限',
+    },
+  },
+
+  backgroundDispatch: {
+    unknownFile: '未知檔案',
+    unknownPrinter: '未知印表機',
+    startingPrints: '正在開始列印',
+    progressSummary: '{{complete}}/{{total}} 完成 • 已分發:{{dispatched}} • 處理中:{{processing}}',
+    expandDetails: '展開分發詳情',
+    collapseDetails: '收起分發詳情',
+    dismissToast: '關閉分發通知',
+    cancelDispatchJob: '取消分發任務',
+    cancel: '取消',
+    cancelling: '取消中…',
+    status: {
+      dispatched: '已分發',
+      processing: '處理中',
+      completed: '已完成',
+      failed: '失敗',
+      cancelled: '已取消',
+    },
+    toast: {
+      cancellingUpload: '取消上傳中...',
+      cancelled: '分發已取消',
+      cancelFailed: '取消分發失敗',
+      completeWithFailures: '後台分發完成:{{completed}} 成功,{{failed}} 失敗',
+      completeSuccess: '後台分發完成:{{completed}} 成功',
+      printStartedRemaining: '{{completed}} 個列印已開始,{{remaining}} 個正在傳送...',
+    },
+  },
+
+  // Statistics page
+  stats: {
+    title: '儀表板',
+    subtitle: '拖曳小工具以重新排列。點選眼睛圖示隱藏。',
+    overview: '概覽',
+    totalPrints: '總列印次數',
+    successRate: '成功率',
+    totalPrintTime: '總列印時間',
+    printTime: '列印時間',
+    totalFilament: '總耗材用量',
+    filamentUsed: '耗材用量',
+    filamentCost: '耗材成本',
+    totalCost: '總成本',
+    energyUsed: '能耗',
+    energyCost: '能源成本',
+    energyWarmingUpTooltip: '能耗追蹤正在收集每小時快照。當所選範圍之前至少存在一個快照時,時間段合計將變得準確。早期數值可能偏低。',
+    averagePrintTime: '平均列印時間',
+    printsPerDay: '每日列印次數',
+    byPrinter: '按印表機',
+    printsByPrinter: '各印表機列印次數',
+    byMaterial: '按材料',
+    byMonth: '按月份',
+    last7Days: '最近 7 天',
+    last30Days: '最近 30 天',
+    last90Days: '最近 90 天',
+    allTime: '全部時間',
+    // Widgets
+    quickStats: '快速統計',
+    printActivity: '列印活動',
+    filamentTypes: '耗材類型',
+    filamentTrends: '耗材趨勢',
+    failureAnalysis: '失敗分析',
+    timeAccuracy: '時間準確度',
+    successful: '成功:',
+    failed: '失敗:',
+    perfectEstimate: '100% = 完美估計',
+    noTimeAccuracyData: '尚無時間準確度資料',
+    noFilamentData: '尚無耗材資料',
+    noPrinterData: '尚無印表機資料',
+    noPrintData: '尚無列印資料',
+    noPrintDataLast30Days: '最近 30 天無列印資料',
+    failureReasons: '失敗原因',
+    topFailureReasons: '主要失敗原因',
+    failedPrintsCount: '{{failed}} / {{total}} 次列印失敗',
+    lastWeekRate: '上週:{{rate}}%',
+    // Actions
+    resetLayout: '重設佈局',
+    recalculateCosts: '重新計算成本',
+    recalculateCostsHint: '使用目前耗材價格重新計算所有歸檔成本',
+    exportStats: '匯出統計',
+    exportAsCsv: '匯出為 CSV',
+    exportAsExcel: '匯出為 Excel',
+    hiddenCount: '{{count}} 個已隱藏',
+    // Toast
+    exportDownloaded: '匯出已下載',
+    exportFailed: '匯出失敗',
+    layoutReset: '佈局已重設',
+    recalculatedCosts: '已為 {{count}} 個歸檔重新計算成本',
+    recalculateFailed: '重新計算成本失敗',
+    // Loading
+    loadingStats: '載入統計資料中...',
+    // Permissions
+    noPermissionResetLayout: '您沒有重設佈局的權限',
+    noPermissionRecalculate: '您沒有重新計算成本的權限',
+    noPrintDataInRange: '所選範圍內無列印資料',
+    periodFilament: '期間耗材',
+    periodCost: '期間成本',
+    avgPerPrint: '每次列印平均',
+    usageOverTime: '隨時間的使用量',
+    filamentByWeight: '重量',
+    printDuration: '列印時長',
+    printerUtilization: '印表機利用率',
+    filamentSuccess: '按材料成功率',
+    printHabits: '列印習慣',
+    printTimeOfDay: '列印時段',
+    colorDistribution: '顏色分佈',
+    noColorData: '尚無顏色資料',
+    records: '紀錄',
+    longestPrint: '最長列印',
+    heaviestPrint: '最重列印',
+    mostExpensivePrint: '最貴列印',
+    busiestDay: '最忙碌的一天',
+    successStreak: '連續成功',
+    streakPrint: '連續列印',
+    streakPrints: '{{count}} 次連續列印',
+    printerStats: '印表機統計',
+    hours: '小時',
+    avgPrints: '平均列印',
+    noArchiveData: '尚無列印資料',
+    filamentByTime: '時間',
+    avgWeight: '平均重量',
+    avgTime: '平均時間',
+    filamentByPrints: '列印次數',
+    timeframe: {
+      today: '今天',
+      'this-week': '本週',
+      'this-month': '本月',
+      'last-7': '最近 7 天',
+      'last-30': '最近 30 天',
+      'last-90': '最近 90 天',
+      'this-year': '今年',
+      'all-time': '全部時間',
+      custom: '自訂範圍',
+      from: '從',
+      to: '到',
+    },
+    allUsers: '所有使用者',
+    noUser: '無使用者(系統)',
+    filterByUser: '按使用者篩選',
+  },
+
+  // Maintenance page
+  maintenance: {
+    title: '維護',
+    overview: '概覽',
+    allOk: '所有維護均已完成',
+    dueCount: '{{count}} 項到期',
+    dueCount_plural: '{{count}} 項到期',
+    warningCount: '{{count}} 個警告',
+    warningCount_plural: '{{count}} 個警告',
+    totalPrintTime: '總列印時間',
+    nextMaintenance: '下次維護',
+    nothingDue: '無到期項目',
+    tasks: '任務',
+    lastPerformed: '上次執行',
+    interval: '間隔',
+    hoursRemaining: '剩餘 {{hours}} 小時',
+    hoursOverdue: '逾期 {{hours}} 小時',
+    markDone: '標記為完成',
+    performMaintenance: '執行維護',
+    history: '歷史',
+    noHistory: '無維護歷史',
+    editPrintHours: '編輯列印時間',
+    currentHours: '目前小時數',
+    // Tabs
+    statusTab: '狀態',
+    settingsTab: '設定',
+    // Status
+    overdueCount: '{{count}} 個逾期',
+    dueSoonCount: '{{count}} 個即將到期',
+    dueSoon: '即將到期',
+    allGood: '一切正常',
+    overdueBy: '逾期 {{duration}}',
+    dueIn: '{{duration}} 後到期',
+    timeLeft: '剩餘 {{duration}}',
+    // Duration formats
+    day: '1 天',
+    days: '{{count}} 天',
+    week: '1 週',
+    weeks: '{{count}} 週',
+    month: '1 個月',
+    months: '{{count}} 個月',
+    year: '1 年',
+    // Settings
+    maintenanceTypes: '維護類型',
+    maintenanceTypesDescription: '系統類型和您的自訂維護任務',
+    addCustomType: '新增自訂類型',
+    restoreDefaults: '恢復預設任務',
+    intervalType: '間隔類型',
+    intervalValue: '間隔 ({{type}})',
+    icon: '圖示',
+    documentationLink: '說明文件連結(可選)',
+    assignToPrinters: '分配給印表機',
+    selectAtLeastOnePrinter: '至少選擇一臺印表機',
+    addType: '新增類型',
+    custom: '自訂',
+    printHours: '列印小時數',
+    calendarDays: '日曆天數',
+    exampleName: '例如:更換 HEPA 過濾器',
+    viewDocumentation: '檢視說明文件',
+    timeBasedInterval: '基於時間的間隔',
+    // Interval overrides
+    intervalOverrides: '間隔覆蓋',
+    intervalOverridesDescription: '為特定印表機自訂間隔',
+    // Printer assignment
+    assignedToPrinters: '已分配給印表機:',
+    noPrintersAssigned: '未分配印表機',
+    addPrinterShort: '新增:',
+    printersAssignedClick: '已分配 {{count}} 臺印表機 - 點選管理',
+    removeFromPrinter: '從此印表機移除',
+    // Types
+    types: {
+      lubricateCarbonRods: '潤滑碳纖維杆',
+      lubricateRails: '潤滑線性導軌',
+      cleanNozzle: '清潔噴嘴/熱端',
+      checkBelts: '檢查皮帶張力',
+      cleanBuildPlate: '清潔列印板',
+      checkExtruder: '檢查擠出機齒輪',
+      checkCooling: '檢查冷卻風扇',
+      generalInspection: '綜合檢查',
+      cleanCarbonRods: '清潔碳纖維杆',
+      lubricateSteelRods: '潤滑鋼杆',
+      cleanSteelRods: '清潔鋼杆',
+      cleanLinearRails: '清潔線性導軌',
+      checkPtfeTube: '檢查 PTFE 管',
+      replaceHepaFilter: '更換 HEPA 過濾器',
+      replaceCarbonFilter: '更換活性炭過濾器',
+      lubricateLeftNozzleRail: '潤滑左噴嘴導軌',
+    },
+    // Toast
+    maintenanceComplete: '維護已標記為完成',
+    typeUpdated: '維護類型已更新',
+    typeDeleted: '維護類型已刪除',
+    defaultsRestored: '已恢復 {{count}} 個預設任務',
+    printHoursUpdated: '列印小時數已更新',
+    printerAssigned: '印表機已分配',
+    printerRemoved: '印表機已移除',
+    // Confirmation
+    deleteTypeConfirm: '刪除"{{name}}"?',
+    deleteSystemTypeTitle: '刪除預設維護任務?',
+    deleteSystemTypeMessage: '確定要刪除預設維護任務"{{name}}"嗎?',
+    // Permissions
+    noPermissionUpdate: '您沒有更新維護項目的權限',
+    noPermissionPerform: '您沒有執行維護的權限',
+    noPermissionEditTypes: '您沒有編輯維護類型的權限',
+    noPermissionDeleteTypes: '您沒有刪除維護類型的權限',
+    noPermissionEditHours: '您沒有編輯列印時間的權限',
+    noPermissionRemovePrinter: '您沒有移除印表機分配的權限',
+    noPermissionAssignPrinter: '您沒有分配印表機的權限',
+    noPermissionEditIntervals: '您沒有編輯間隔的權限',
+    // Configure link
+    configureSettings: '設定維護類型和間隔',
+  },
+
+  // Settings page
+  settings: {
+    title: '設定',
+    general: '通用',
+    // Tab names
+    tabs: {
+      general: '通用',
+      smartPlugs: '智慧插座',
+      notifications: '通知',
+      queue: '工作流程',
+      filament: '耗材',
+      network: '網路',
+      apiKeys: 'API 金鑰',
+      virtualPrinter: '虛擬印表機',
+      spoolbuddy: 'SpoolBuddy',
+      failureDetection: '故障檢測',
+      users: '身份驗證',
+      backup: '備份',
+      emailAuth: '信箱認證',
+      ldap: 'LDAP',
+      twoFa: '雙因素認證',
+      oidc: 'SSO / OIDC',
+    },
+    spoolbuddy: {
+      infoTitle: 'SpoolBuddy 裝置',
+      infoBody: 'SpoolBuddy kiosk 透過心跳自動註冊。如果裝置不再使用,或守護程式當機遺留了過時的重複項,可在此取消註冊。',
+      duplicatesTitle: '已註冊 {{count}} 台裝置',
+      duplicatesBody: 'kiosk 介面只使用最先註冊的裝置。如果其中有因當機遺留的過時重複項,請取消註冊它——線上裝置會在下次心跳時重新註冊自己。',
+      empty: '尚未註冊任何 SpoolBuddy 裝置。',
+      online: '線上',
+      offline: '離線',
+      unregister: '取消註冊',
+      unregisterSuccess: '裝置已取消註冊',
+      unregisterError: '取消註冊裝置失敗',
+      confirmTitle: '取消註冊 SpoolBuddy 裝置?',
+      confirmBody: '將從資料庫中移除 "{{hostname}}" ({{deviceId}})。如果裝置線上,會在下次心跳時重新註冊自己。',
+      ipAddress: 'IP 位址',
+      firmware: '韌體',
+      lastSeen: '上次線上',
+      daemonUptime: '守護程式執行時間',
+      systemUptime: '系統執行時間',
+      never: '從未',
+      nfc: 'NFC',
+      scale: '磅秤',
+      cpuTemp: 'CPU 溫度',
+      memory: '記憶體',
+      disk: '磁碟',
+      // Device actions
+      update: '更新',
+      updateConfirmTitle: '更新 SpoolBuddy 守護程式?',
+      updateConfirmBody: '對 "{{hostname}}" 觸發軟體更新?更新完成後守護程式將重新啟動。',
+      restartBrowser: '重新啟動瀏覽器',
+      restartBrowserConfirmTitle: '重新啟動 kiosk 瀏覽器?',
+      restartBrowserConfirmBody: '在 "{{hostname}}" 上重新啟動 kiosk 瀏覽器?顯示將短暫黑屏。',
+      restartDaemon: '重新啟動守護程式',
+      restartDaemonConfirmTitle: '重新啟動 SpoolBuddy 守護程式?',
+      restartDaemonConfirmBody: '在 "{{hostname}}" 上重新啟動 SpoolBuddy 守護程式?裝置將離線幾秒鐘。',
+      reboot: '重新開機',
+      rebootConfirmTitle: '重新開機?',
+      rebootConfirmBody: '重新開機 "{{hostname}}"?裝置將離線約一分鐘。',
+      shutdown: '關機',
+      shutdownConfirmTitle: '關閉裝置?',
+      shutdownConfirmBody: '關閉 "{{hostname}}"?您需要實體存取才能重新開機。',
+      commandConfirm: '確認',
+      commandQueued: '命令已加入佇列',
+      commandError: '傳送命令失敗',
+    },
+    ldap: {
+      title: 'LDAP 認證',
+      enabledDesc: 'LDAP 認證已啟用',
+      disabledDesc: 'LDAP 認證已停用',
+      disabledHint: '在下方設定並儲存 LDAP 設定,然後啟用。',
+      enabled: 'LDAP 認證已啟用',
+      disabled: 'LDAP 認證已停用',
+      feature1: '使用者可以使用 LDAP 憑據登入',
+      feature2: '本機管理員帳戶作為後備保留',
+      feature3: '登入時 LDAP 群組對應到 BamBuddy 群組',
+      serverConfig: 'LDAP 伺服器設定',
+      serverUrl: '伺服器 URL',
+      serverUrlHint: '使用 ldap:// 進行標準連線或 ldaps:// 進行 SSL 連線',
+      security: '安全',
+      securityHint: 'StartTLS 將普通連線升級為 TLS。LDAPS 從一開始就使用 TLS。',
+      bindDn: '繫結 DN(服務帳戶)',
+      bindPassword: '繫結密碼',
+      searchBase: '搜尋基礎 DN',
+      userFilter: '使用者搜尋過濾器',
+      userFilterHint: '{username} 替換為登入使用者名稱。OpenLDAP 使用 (uid={username})。',
+      autoProvision: '自動建立使用者',
+      autoProvisionHint: '首次 LDAP 登入時自動建立 BamBuddy 帳戶',
+      defaultGroup: '預設群組',
+      defaultGroupNone: '— 無(無復原)—',
+      defaultGroupHint: '當 LDAP 使用者透過身份驗證但不在任何已對應的 LDAP 群組中時分配的備援群組。留空以使未對應的使用者沒有權限。',
+      groupMapping: '群組對應(JSON)',
+      groupMappingHint: '將 LDAP 群組 DN 對應到 BamBuddy 群組。可用群組:',
+      testConnection: '測試連線',
+      settingsSaved: 'LDAP 設定已儲存',
+      errors: {
+        serverRequired: 'LDAP 伺服器 URL 為必填項',
+        searchBaseRequired: '搜尋基礎 DN 為必填項',
+        enableAuthFirst: '請先啟用認證',
+        configureLdapFirst: '請先儲存 LDAP 設定',
+      },
+    },
+    // Email settings
+    email: {
+      smtpSettings: 'SMTP 設定',
+      smtpHost: 'SMTP 伺服器',
+      smtpPort: 'SMTP 連接埠',
+      security: '安全',
+      authentication: '認證',
+      username: '使用者名稱',
+      password: '密碼',
+      fromEmail: '寄件信箱',
+      fromName: '寄件人名稱',
+      testConnection: '測試 SMTP 連線',
+      testRecipient: '測試收件信箱',
+      sendTest: '傳送測試郵件',
+      sending: '傳送中...',
+      save: '儲存設定',
+      saving: '儲存中...',
+      advancedAuth: '進階認證',
+      advancedAuthEnabled: '進階認證已啟用',
+      advancedAuthEnabledDesc: '基於信箱的使用者管理功能已啟用。新使用者將透過郵件收到自動產生的密碼,使用者可以透過忘記密碼功能重設密碼。',
+      advancedAuthDisabled: '進階認證已停用',
+      advancedAuthDisabledDesc: '啟用進階認證以啟用基於信箱的使用者管理功能。',
+      enable: '啟用',
+      disable: '停用',
+      feature1: '密碼自動產生並透過郵件傳送給新使用者',
+      feature2: '使用者可以使用使用者名稱或信箱登入',
+      feature3: '忘記密碼功能可用',
+      feature4: '管理員可以透過郵件重設使用者密碼',
+      // Error messages
+      errors: {
+        requiredFields: '請填寫所有必填欄位',
+        usernameRequired: '啟用認證時需要使用者名稱',
+        enterTestEmail: '請輸入測試信箱地址',
+        smtpServerAndEmail: '測試前請填寫 SMTP 伺服器和寄件信箱',
+        usernamePasswordRequired: '啟用認證時需要使用者名稱和密碼',
+        configureSmtpFirst: '請先設定並測試 SMTP 設定',
+        enableAuthFirst: '請先啟用身份驗證才能使用基於電子郵件的功能。',
+      },
+      // Success messages
+      success: {
+        settingsSaved: 'SMTP 設定儲存成功',
+      },
+      // Security options
+      securityOptions: {
+        starttls: 'STARTTLS(連接埠 587)',
+        ssl: 'SSL/TLS(連接埠 465)',
+        none: '無(連接埠 25)',
+      },
+      // Authentication options
+      authOptions: {
+        enabled: '已啟用',
+        disabled: '已停用',
+      },
+    },
+    appearance: '外觀',
+    notifications: '通知',
+    smartPlugs: '智慧插座',
+    spoolman: 'Spoolman',
+    updates: '更新',
+    language: '語言',
+    languageDescription: '選擇您的首選語言',
+    theme: '主題',
+    themeLight: '淺色',
+    themeDark: '深色',
+    themeSystem: '跟隨系統',
+    defaultView: '預設檢視',
+    defaultViewDescription: '開啟應用程式時顯示的頁面',
+    checkForUpdates: '檢查更新',
+    autoUpdate: '自動更新',
+    currentVersion: '目前版本',
+    latestVersion: '最新版本',
+    upToDate: '已是最新版本',
+    updateAvailable: '有可用更新',
+    // Notifications
+    notificationLanguage: '通知語言',
+    notificationLanguageDescription: '推送通知的語言',
+    bedCooledThreshold: '熱床冷卻閾值',
+    bedCooledThresholdDescription: '列印後熱床被視為已冷卻的溫度',
+    userNotificationsEnabled: '使用者通知',
+    userNotificationsEnabledDescription: '啟用使用者通知選單和列印任務事件的郵件通知。需要進階身份驗證。',
+    userNotificationsDisabledHint: '請啟用進階身份驗證以使用使用者通知。',
+    notificationProviders: '通知提供者',
+    addProvider: '新增提供者',
+    editProvider: '編輯提供者',
+    providerType: '提供者類型',
+    testNotification: '測試通知',
+    testSuccess: '測試通知傳送成功',
+    testFailed: '傳送測試通知失敗',
+    quietHours: '免打擾時間',
+    quietHoursDescription: '在此時間段內不傳送通知',
+    quietHoursStart: '開始',
+    quietHoursEnd: '結束',
+    events: {
+      title: '通知事件',
+      printStart: '列印開始',
+      printComplete: '列印完成',
+      printFailed: '列印失敗',
+      printStopped: '列印停止',
+      printProgress: '進度里程碑',
+      printProgressDescription: '在 25%、50%、75% 時通知',
+      printerOffline: '印表機離線',
+      printerError: '印表機錯誤',
+      filamentLow: '耗材不足',
+      maintenanceDue: '維護到期',
+      maintenanceDueDescription: '需要維護時通知',
+    },
+    // Smart Plugs
+    smartPlug: {
+      title: '智慧插座',
+      add: '新增智慧插座',
+      edit: '編輯智慧插座',
+      name: '名稱',
+      ipAddress: 'IP 位址',
+      linkedPrinter: '連結印表機',
+      autoOn: '自動開啟',
+      autoOnDescription: '列印開始時開啟',
+      autoOff: '自動關閉',
+      autoOffDescription: '列印完成後關閉',
+      offDelay: '關閉延遲',
+      offDelayMinutes: '列印後分鐘數',
+      offDelayTemp: '當噴嘴溫度低於',
+      currentState: '目前狀態',
+      turnOn: '開啟',
+      turnOff: '關閉',
+    },
+    // Filament Tracking Mode
+    filamentTracking: '耗材追蹤',
+    filamentTrackingDesc: '選擇如何追蹤您的耗材。您可以使用內建庫存或連線外部 Spoolman 伺服器。',
+    filamentChecks: '耗材檢查',
+    disableFilamentWarnings: '停用耗材警告',
+    disableFilamentWarningsDesc: '在列印或加入佇列時不顯示耗材不足警告',
+    preferLowestFilament: '優先使用剩餘最少的耗材',
+    preferLowestFilamentDesc: '當多個料盤匹配時,使用剩餘耗材最少的那個',
+    trackingModeBuiltIn: '內建庫存',
+    trackingModeBuiltInDesc: '包含 RFID 自動匹配和用量追蹤',
+    trackingModeSpoolmanDesc: '外部耗材管理伺服器',
+    builtInFeatureRfid: '自動檢測 AMS 中的拓竹 RFID 耗材',
+    builtInFeatureUsage: '追蹤每次列印的耗材消耗',
+    builtInFeatureCatalog: '管理耗材、顏色和 K 值設定檔案',
+    builtInFeatureThirdParty: '第三方耗材可分配到庫存耗材',
+    amsSyncButton: '從 AMS 同步重量',
+    amsSyncTitle: '從 AMS 同步耗材重量',
+    amsSyncMessage: '這將使用已連線印表機的目前 AMS 剩餘百分比值覆蓋所有庫存耗材重量。用於從損壞的重量資料中恢復。印表機必須線上。',
+    amsSyncing: '同步中...',
+    amsSyncSuccess: '已同步 {{synced}} 個耗材,跳過 {{skipped}} 個',
+    amsSyncError: '從 AMS 同步重量失敗',
+    // Spoolman settings
+    spoolmanUrl: 'Spoolman URL',
+    spoolmanUrlHint: 'Spoolman 伺服器的 URL(例如 http://localhost:7912)',
+    spoolmanConnected: '已連線',
+    spoolmanDisconnected: '未連線',
+    status: '狀態',
+    connect: '連線',
+    disconnect: '斷開',
+    howSyncWorks: '同步工作原理',
+    syncInfoRfidOnly: '僅同步帶有 RFID 的官方拓竹耗材',
+    syncInfoAutoCreate: '首次同步時自動在 Spoolman 中建立新耗材',
+    syncInfoThirdPartySkipped: '非拓竹耗材(第三方、重新填充的)將被跳過',
+    linkingExistingSpools: '連結現有耗材',
+    linkingExistingSpoolsDesc: '要將現有的 Spoolman 耗材連結到您的 AMS,請將滑鼠懸停在 AMS 槽位上並點選"連結到 Spoolman"。',
+    syncMode: '同步模式',
+    syncModeAuto: '自動',
+    syncModeManual: '僅手動',
+    syncModeAutoDesc: '偵測到更改時自動同步 AMS 資料',
+    syncModeManualDesc: '僅在手動觸發時同步',
+    syncAmsData: '同步 AMS 資料',
+    syncAmsDataDesc: '手動將印表機 AMS 資料同步到 Spoolman',
+    allPrinters: '所有印表機',
+    // Default printer
+    noDefaultPrinter: '無預設(每次詢問)',
+    // Sidebar
+    sidebarOrder: '側邊欄順序',
+    // Camera
+    saveThumbnails: '儲存縮圖',
+    captureFinishPhoto: '拍攝完成照片',
+    noPrintersConfigured: '未設定印表機',
+    // Archive settings
+    archiveMode: {
+      always: '始終建立歸檔條目',
+      never: '從不建立歸檔條目',
+      ask: '每次詢問',
+    },
+    // Updates
+    checkForUpdatesLabel: '檢查更新',
+    checkPrinterFirmware: '檢查印表機韌體',
+    includeBetaUpdates: '包含測試版本',
+    includeBetaUpdatesDesc: '檢查更新時通知測試版和預發布版本',
+    // Queue
+    enableRetry: '啟用重試',
+    // Home Assistant
+    homeAssistantDescription: '透過 Home Assistant 控制智慧插座',
+    environmentManagedLabel: '(環境變數管理)',
+    autoEnabledViaEnv: '透過環境變數自動啟用',
+    urlFromEnvReadOnly: '值由 HA_URL 環境變數設定(只讀)',
+    tokenFromEnvReadOnly: '值由 HA_TOKEN 環境變數設定(只讀)',
+    // MQTT
+    mqttConnectedTo: '已連線到',
+    // Prometheus
+    prometheusDescription: '以 Prometheus 格式暴露印表機資料',
+    // Smart plugs empty state
+    noSmartPlugsTitle: '未設定智慧插座',
+    noSmartPlugsDescription: '新增基於 Tasmota 的智慧插座以追蹤能耗並自動化電源控制。',
+    // Notifications empty state
+    noProvidersTitle: '未設定提供者',
+    noProvidersDescription: '新增提供者以接收警報。',
+    noTemplatesAvailable: '無可用範本。重新啟動後端以載入預設範本。',
+    // API permissions
+    apiPermissionView: '檢視印表機狀態和佇列',
+    apiPermissionEdit: '新增和移除列印佇列中的項目',
+    // API keys
+    apiKeysEmptyTitle: '無 API 金鑰',
+    apiKeysEmptyDescription: '建立 API 金鑰以與外部服務整合。',
+    // Users
+    noUsersFound: '未找到使用者',
+    noGroupsFound: '未找到群組',
+    noGroupsAvailable: '無可用群組',
+    passwordsDoNotMatch: '密碼不符',
+    systemGroupWarning: '系統群組名稱不可更改',
+    // Auth disabled
+    authDisabledTitle: '身份驗證已停用',
+    authDisabledFeature1: '需要登入才能存取系統',
+    authDisabledFeature2: '建立多個使用者並基於群組的權限管理',
+    authDisabledFeature3: '使用 50+ 個細粒度權限控制存取',
+    // User deletion
+    userHasCreated: '此使用者已建立:',
+    userItemsQuestion: '您想如何處理這些項目?',
+    deleteUserConfirm: '確定要刪除此使用者嗎?',
+    actionCannotBeUndone: '此操作無法復原。',
+    // Smart plugs
+    addFirstSmartPlug: '新增您的第一個智慧插座',
+    // Notifications
+    providers: '提供者',
+    log: '日誌',
+    testAll: '全部測試',
+    testResults: '測試結果',
+    testPassedCount: '{{count}} 個透過',
+    testFailedCount: '{{count}} 個失敗',
+    messageTemplates: '訊息範本',
+    messageTemplatesDescription: '自訂每個事件的通知訊息。',
+    // API Keys section
+    apiKeys: 'API 金鑰',
+    apiKeysDescription: '建立 API 金鑰用於外部整合和 Webhook。',
+    createKey: '建立金鑰',
+    apiKeyCreated: 'API 金鑰建立成功',
+    apiKeyCopyWarning: '請立即複製此金鑰 - 它不會再次顯示!',
+    useInApiBrowser: '在 API 瀏覽器中使用',
+    createNewApiKey: '建立新 API 金鑰',
+    keyName: '金鑰名稱',
+    keyNamePlaceholder: '例如:Home Assistant、OctoPrint',
+    readStatus: '讀取狀態',
+    readStatusDescription: '檢視印表機狀態和佇列',
+    manageQueue: '管理佇列',
+    manageQueueDescription: '新增和移除列印佇列中的項目',
+    controlPrinter: '控制印表機',
+    controlPrinterDescription: '暫停、繼續和停止列印',
+    unnamedKey: '未命名金鑰',
+    lastUsed: '上次使用',
+    read: '讀取',
+    control: '控制',
+    createFirstKey: '建立您的第一個金鑰',
+    webhookEndpoints: 'Webhook 端點',
+    webhookApiKeyHint: '在 X-API-Key 請求頭中使用您的 API 金鑰。',
+    webhook: {
+      getAllStatus: '獲取所有印表機狀態',
+      getSpecificStatus: '獲取特定印表機狀態',
+      addToQueue: '新增到列印佇列',
+      pausePrint: '暫停列印',
+      resumePrint: '繼續列印',
+      stopPrint: '停止列印',
+    },
+    apiBrowser: 'API 瀏覽器',
+    apiBrowserDescription: '瀏覽和測試所有可用的 API 端點。',
+    apiKeyForTesting: '測試用 API 金鑰',
+    apiKeyPlaceholder: '在此貼上您的 API 金鑰以測試需要認證的端點...',
+    apiKeyHint: '此金鑰將作為 X-API-Key 請求頭隨請求傳送。',
+    deleteApiKeyTitle: '刪除 API 金鑰',
+    deleteApiKeyMessage: '確定要刪除此 API 金鑰嗎?使用此金鑰的所有整合將停止工作。',
+    deleteKey: '刪除金鑰',
+    // Filament tab
+    amsDisplayThresholds: 'AMS 顯示閾值',
+    amsThresholdsDescription: '設定 AMS 濕度和溫度指示器的顏色閾值。',
+    humidity: '濕度',
+    goodGreen: '良好(綠色)',
+    fairOrange: '一般(橙色)',
+    aboveFairBad: '超過一般閾值顯示為紅色(差)',
+    fairAlsoDryingThreshold: '此閾值也用於觸發自動乾燥',
+    temperature: '溫度',
+    goodBlue: '良好(藍色)',
+    aboveFairHot: '超過一般閾值顯示為紅色(熱)',
+    historyRetention: '歷史保留',
+    keepSensorHistory: '保留感測器歷史',
+    historyRetentionDescription: '較舊的濕度和溫度資料將被自動刪除',
+    defaultPrintOptions: '預設列印選項',
+    defaultPrintOptionsDescription: '設定新列印的預設選項值。可在列印對話方塊中逐次覆蓋。',
+    defaultBedLevelling: '熱床調平',
+    defaultBedLevellingDesc: '列印前自動調平熱床',
+    defaultFlowCali: '流量校準',
+    defaultFlowCaliDesc: '校準擠出流量',
+    defaultVibrationCali: '振動校準',
+    defaultVibrationCaliDesc: '減少振紋偽影',
+    defaultLayerInspect: '首層檢測',
+    defaultLayerInspectDesc: 'AI首層檢測',
+    defaultTimelapse: '縮時攝影',
+    defaultTimelapseDesc: '錄製縮時攝影影片',
+    staggeredStart: '錯開啟動',
+    staggeredStartDescription: '多台印表機批次啟動時的預設群組大小與間隔。可在列印對話框中逐批覆寫。',
+    plateClear: '熱床清空確認',
+    requirePlateClear: '需要熱床清空確認',
+    requirePlateClearDescription: '啟用後,排程器會在已完成列印的印表機上啟動佇列列印之前,等待每臺印表機的熱床清空確認。對於物理驗證熱床的農場工作流,請停用此選項。',
+    gcodeInjection: 'G-code注入',
+    gcodeInjectionDescription: '為Farmloop、SwapMod、AutoClear和Printflow 3D等自動列印系統設定自訂G-code,在列印開始和/或結束時注入。程式碼片段按印表機型號設定,在佇列項目上啟用"注入G-code"時套用。',
+    gcodeInjectionNoPrinters: '未找到印表機。新增印表機以設定G-code程式碼片段。',
+    gcodeStartLabel: '開始G-code',
+    gcodeEndLabel: '結束G-code',
+    gcodeStartPlaceholder: '在列印開始前插入的G-code...',
+    gcodeEndPlaceholder: '在列印結束後追加的G-code...',
+    staggerGroupSize: '群組大小',
+    staggerGroupSizeHelp: '每個群組要同時啟動的印表機數量',
+    staggerInterval: '間隔(分鐘)',
+    staggerIntervalHelp: '每個群組啟動之間的延遲',
+    queueDrying: '自動乾燥',
+    queueDryingDescription: '在佇列列印之間,印表機空閒時自動乾燥AMS耗材。使用上方的濕度閾值觸發乾燥。',
+    queueDryingEnabled: '啟用自動乾燥',
+    queueDryingEnabledDescription: '當印表機空閒且濕度超過閾值時,自動啟動AMS乾燥',
+    queueDryingBlock: '等待乾燥完成',
+    queueDryingBlockDescription: '阻止列印佇列直到乾燥完成。關閉時,列印優先於乾燥。',
+    ambientDryingEnabled: '環境乾燥',
+    ambientDryingEnabledDescription: '當空閒印表機的濕度超過閾值時自動乾燥耗材,無需佇列列印。',
+    dryingPresets: '乾燥預設',
+    dryingPresetsDescription: '每種耗材類型的溫度和時長。AMS 2 Pro使用較低溫度,AMS-HT支援較高溫度。',
+    dryingFilament: '耗材',
+    printModal: '列印對話方塊',
+    expandCustomMapping: '預設展開自訂對應',
+    expandCustomMappingDescription: '列印到多臺印表機時,預設展開顯示每臺印表機的 AMS 對應',
+    // User management
+    authentication: '身份驗證',
+    authEnabledDescription: '您的實例已透過使用者身份驗證保護',
+    authDisabledDescription: '啟用以要求登入並管理使用者存取',
+    authDisabledMessage: '啟用身份驗證以建立使用者帳戶、管理權限並保護您的 Bambuddy 實例。',
+    enableAuthentication: '啟用身份驗證',
+    currentUser: '目前使用者',
+    changePassword: '修改密碼',
+    admin: '管理員',
+    users: '使用者',
+    addUser: '新增使用者',
+    groups: '群組',
+    addGroup: '新增群組',
+    system: '系統',
+    noDescription: '無描述',
+    userCount: '{{count}} 個使用者',
+    permissionCount: '{{count}} 個權限',
+    createUser: '建立使用者',
+    username: '使用者名稱',
+    enterUsername: '輸入使用者名稱',
+    password: '密碼',
+    enterPassword: '輸入密碼(至少 6 個字元)',
+    confirmPassword: '確認密碼',
+    confirmPasswordPlaceholder: '確認密碼',
+    // Title tooltips
+    viewReleaseOnGitHub: '在 GitHub 上檢視發布',
+    turnAllPlugsOn: '開啟所有插座',
+    turnAllPlugsOff: '關閉所有插座',
+    // Modal: Clear logs
+    clearNotificationLogs: '清除通知日誌',
+    clearLogsMessage: '這將永久刪除所有 30 天前的通知日誌。此操作無法復原。',
+    clearLogs: '清除日誌',
+    // Modal: Reset UI
+    resetUiPreferences: '重設 UI 偏好',
+    resetUiPreferencesMessage: '這將重設所有 UI 偏好為預設值:側邊欄順序、主題、儀表板佈局、檢視模式和排序偏好。您的印表機、歸檔和伺服器設定不會受到影響。清除後頁面將重新載入。',
+    resetPreferences: '重設偏好',
+    // Modal: Delete group
+    deleteGroupTitle: '刪除群組',
+    deleteGroupMessage: '確定要刪除此群組嗎?此群組中的使用者將失去這些權限。',
+    deleteGroup: '刪除群組',
+    // Modal: Disable auth
+    disableAuthenticationTitle: '停用身份驗證',
+    disableAuthenticationMessage: '確定要停用身份驗證嗎?這將使您的 Bambuddy 實例無需登入即可存取。所有使用者將保留在資料庫中但身份驗證將被停用。',
+    disableAuthentication: '停用身份驗證',
+    // Additional settings
+    configureBambuddy: '設定 Bambuddy',
+    systemDefault: '系統預設',
+    archiveSettings: '歸檔設定',
+    newWindow: '新視窗',
+    embeddedOverlay: '嵌入式疊加層',
+    preferredSlicer: '首選切片軟體',
+    preferredSlicerDescription: '選擇要用於開啟檔案的切片軟體',
+    externalCameras: '外部攝影機',
+    costTracking: '成本追蹤',
+    printsOnly: '僅列印',
+    totalConsumption: '總消耗',
+    dataManagement: '資料管理',
+    storageUsage: '儲存使用情況',
+    storageUsageDescription: '按類別的資料使用情況明細',
+    storageUsageTotal: '總計',
+    storageUsageErrors: '錯誤',
+    storageUsageOtherBreakdown: '其他(包括靜態資源、腳本和設定檔案)',
+    storageUsageSystem: '系統',
+    storageUsageData: '資料',
+    storageUsageUnavailable: '儲存使用資訊不可用',
+    clearNotificationLogsDescription: '刪除 30 天前的通知日誌',
+    resetUiPreferencesDescription: '重設側邊欄順序、主題、檢視模式和佈局偏好。印表機、歸檔和設定不受影響。',
+    enableHomeAssistant: '啟用 Home Assistant',
+    enableMqtt: '啟用 MQTT',
+    useTls: '使用 TLS',
+    enableMetricsEndpoint: '啟用指標端點',
+    availableMetrics: '可用指標',
+    editUser: '編輯使用者',
+    deleteUserTitle: '刪除使用者',
+    groupName: '群組名稱',
+    // Placeholders
+    leaveEmptyForAnonymous: '留空為匿名',
+    leaveEmptyForNoAuth: '留空為無認證',
+    enterNewPassword: '輸入新密碼',
+    confirmNewPassword: '確認新密碼',
+    enterGroupName: '輸入群組名稱',
+    enterDescriptionOptional: '輸入描述(可選)',
+    enterCurrentPassword: '輸入目前密碼',
+    enterNewPasswordMin6: '輸入新密碼(至少 6 個字元)',
+    toast: {
+      keyCopied: '金鑰已複製到剪貼簿',
+      copyFailed: '複製金鑰失敗',
+      keyAddedToBrowser: '金鑰已新增到 API 瀏覽器',
+      clearLogsFailed: '清除日誌失敗',
+      uiPreferencesReset: 'UI 偏好已重設。重新整理中...',
+      authDisabled: '身份驗證已成功停用',
+      authDisableFailed: '停用身份驗證失敗',
+      apiKeyCreated: 'API 金鑰已建立',
+      apiKeyDeleted: 'API 金鑰已刪除',
+      userCreated: '使用者建立成功',
+      userUpdated: '使用者更新成功',
+      userDeleted: '使用者刪除成功',
+      groupCreated: '群組建立成功',
+      groupUpdated: '群組更新成功',
+      groupDeleted: '群組刪除成功',
+      fillRequiredFields: '請填寫所有必填欄位',
+      passwordsDoNotMatch: '密碼不符',
+      passwordTooShort: '密碼至少需要 6 個字元',
+      enterGroupName: '請輸入群組名稱',
+      settingsSaved: '設定已儲存',
+      cameraSettingsSaved: '攝影機設定已儲存',
+      enterCameraUrl: '請輸入攝影機 URL',
+      passwordChanged: '密碼修改成功',
+      connectionFailed: '連線失敗',
+      testFailed: '測試失敗',
+      cameraConnected: '攝影機已連線{{resolution}}',
+    },
+    testConnection: '測試連線',
+    catalog: {
+      spoolCatalog: '耗材目錄',
+      spoolCatalogDescription: '按品牌/類型的空耗材重量。用於新增耗材時的自動重量查詢。',
+      searchCatalog: '搜尋目錄...',
+      addNewEntry: '新增新條目',
+      namePlaceholder: '名稱(例如:Bambu Lab - 塑膠)',
+      weight: '重量',
+      type: '類型',
+      default: '預設',
+      custom: '自訂',
+      noMatch: '沒有條目匹配您的搜尋',
+      empty: '目錄中沒有條目',
+      deleteEntry: '刪除條目',
+      deleteConfirm: '確定要刪除"{{name}}"嗎?',
+      resetCatalog: '重設目錄',
+      resetConfirm: '重設目錄為預設值?這將移除所有自訂條目。',
+      loadFailed: '載入耗材目錄失敗',
+      nameWeightRequired: '名稱和重量為必填項',
+      entryAdded: '條目已新增',
+      addFailed: '新增條目失敗',
+      entryUpdated: '條目已更新',
+      updateFailed: '更新條目失敗',
+      entryDeleted: '條目已刪除',
+      deleteFailed: '刪除條目失敗',
+      resetSuccess: '目錄已重設為預設值',
+      resetFailed: '重設目錄失敗',
+      exported: '已匯出 {{count}} 條',
+      imported: '已匯入 {{added}} 條(跳過 {{skipped}} 條)',
+      importFailed: '匯入失敗:無效的 JSON 格式',
+      exportTooltip: '匯出目錄為 JSON',
+      importTooltip: '從 JSON 匯入目錄',
+      resetTooltip: '重設為預設值',
+      selectedCount: '已選擇 {{count}} 項',
+      deleteSelected: '刪除所選',
+      bulkDeleteConfirm: '確定要刪除 {{count}} 個條目嗎?',
+      bulkDeleted: '已刪除 {{count}} 個條目',
+      bulkDeleteFailed: '刪除條目失敗',
+    },
+    colorCatalog: {
+      title: '顏色目錄',
+      description: '按製造商/材料的耗材顏色。用於新增耗材時的自動顏色查詢。',
+      searchColors: '搜尋顏色...',
+      allManufacturers: '所有製造商',
+      addNewColor: '新增新顏色',
+      manufacturer: '製造商',
+      colorName: '顏色名稱',
+      hex: '十六進位',
+      materialOptional: '材料(可選)',
+      showing: '顯示 {{filtered}} / {{total}} 種顏色',
+      noMatch: '沒有顏色匹配您的搜尋',
+      empty: '目錄中沒有顏色',
+      deleteColor: '刪除顏色',
+      deleteConfirm: '確定要刪除"{{name}}"嗎?',
+      resetCatalog: '重設顏色目錄',
+      resetConfirm: '重設目錄為預設值?這將移除所有自訂顏色。',
+      sync: '同步',
+      starting: '啟動中...',
+      syncTooltip: '從 FilamentColors.xyz 同步(2000+ 種顏色,可能需要一分鐘)',
+      loadFailed: '載入顏色目錄失敗',
+      fieldsRequired: '製造商、顏色名稱和十六進位顏色為必填項',
+      colorAdded: '顏色已新增',
+      addFailed: '新增顏色失敗',
+      colorUpdated: '顏色已更新',
+      updateFailed: '更新顏色失敗',
+      colorDeleted: '顏色已刪除',
+      deleteFailed: '刪除顏色失敗',
+      resetSuccess: '顏色目錄已重設為預設值',
+      resetFailed: '重設目錄失敗',
+      syncUpToDate: '已是最新(檢查了 {{count}} 種顏色)',
+      syncComplete: '新增了 {{added}} 種新顏色({{skipped}} 種已存在)',
+      syncError: '同步錯誤',
+      syncFailed: '從 FilamentColors.xyz 同步失敗',
+      exported: '已匯出 {{count}} 種顏色',
+      imported: '已匯入 {{added}} 種顏色(跳過 {{skipped}} 種)',
+      importFailed: '匯入失敗:無效的 JSON 格式',
+      selectedCount: '已選擇 {{count}} 項',
+      deleteSelected: '刪除所選',
+      bulkDeleteConfirm: '確定要刪除 {{count}} 種顏色嗎?',
+      bulkDeleted: '已刪除 {{count}} 種顏色',
+      bulkDeleteFailed: '刪除顏色失敗',
+    },
+    dateFormat: '日期格式',
+    dateFormatUs: '美式 (MM/DD/YYYY)',
+    dateFormatEu: '歐式 (DD/MM/YYYY)',
+    dateFormatIso: 'ISO (YYYY-MM-DD)',
+    timeFormat: '時間格式',
+    timeFormat12: '12小時制 (3:30 PM)',
+    timeFormat24: '24小時制 (15:30)',
+    defaultPrinter: '預設印表機',
+    defaultPrinterDescription: '為上傳、重印和其他操作預選此印表機。',
+    slicerBambuStudio: 'Bambu Studio',
+    slicerOrcaSlicer: 'OrcaSlicer',
+    sidebarOrderDescription: '拖曳側邊欄項目以重新排序。在此處重設為預設順序。',
+    setDefault: '設為預設',
+    sidebarOrderSetDefaultHint: '設為預設將目前選單順序套用於尚未自訂的使用者。',
+    sidebarDefaultSet: '已設定預設選單順序。',
+    sidebarDefaultCleared: '已清除預設選單順序。',
+    sidebarDefaultFailed: '設定預設選單順序失敗。',
+    reset: '重設',
+    darkMode: '深色模式',
+    lightMode: '淺色模式',
+    active: '(目前)',
+    background: '背景',
+    accent: '強調色',
+    style: '樣式',
+    bgNeutral: '中性',
+    bgWarm: '暖色',
+    bgCool: '冷色',
+    bgOled: 'OLED 純黑',
+    bgSlate: '石板藍',
+    bgForest: '森林綠',
+    accentGreen: '綠色',
+    accentTeal: '青色',
+    accentBlue: '藍色',
+    accentOrange: '橙色',
+    accentPurple: '紫色',
+    accentRed: '紅色',
+    styleClassic: '經典',
+    styleGlow: '發光',
+    styleVibrant: '鮮豔',
+    themeToggleHint: '使用側邊欄中的太陽/月亮圖示在深色和淺色模式之間切換。',
+    autoArchivePrints: '自動歸檔列印',
+    autoArchiveDescription: '列印完成時自動儲存3MF檔案',
+    saveThumbnailsDescription: '從3MF檔案中提取並儲存預覽影像',
+    captureFinishPhotoDescription: '列印完成時從印表機攝影機拍照',
+    ffmpegNotInstalled: '未安裝ffmpeg',
+    ffmpegRequired: '攝影機捕獲需要ffmpeg。透過 <brew>brew install ffmpeg</brew>(macOS)或 <apt>apt install ffmpeg</apt>(Linux)安裝。',
+    camera: '攝影機',
+    cameraViewMode: '攝影機檢視模式',
+    cameraOverlayDescription: '攝影機在主螢幕上以可調大小的覆蓋層開啟',
+    cameraWindowDescription: '攝影機在單獨的瀏覽器視窗中開啟',
+    externalCamerasDescription: '設定外部攝影機以替換內建印表機攝影機。支援MJPEG流、RTSP、HTTP快照和USB攝影機(V4L2)。啟用後,外部攝影機將用於即時檢視和完成照片。',
+    cameraPlaceholderUsb: '裝置路徑 (/dev/video0)',
+    cameraPlaceholderUrl: '攝影機URL (rtsp://... 或 http://...)',
+    cameraTypeMjpeg: 'MJPEG 流',
+    cameraTypeRtsp: 'RTSP 流',
+    cameraTypeSnapshot: 'HTTP 快照',
+    cameraTypeUsb: 'USB 攝影機 (V4L2)',
+    cameraRotation: '旋轉',
+    test: '測試',
+    connected: '已連線',
+    disconnected: '未連線',
+    currency: '貨幣',
+    defaultFilamentCost: '預設耗材成本(每公斤)',
+    electricityCost: '每千瓦時電費',
+    energyDisplayMode: '能源顯示模式',
+    energyModePrintDescription: '儀表板顯示列印期間使用的能源總和',
+    energyModeTotalDescription: '儀表板顯示智慧插座的累計能源',
+    fileManager: '檔案管理器',
+    createArchiveEntry: '列印時建立歸檔條目',
+    createArchiveEntryDescription: '從檔案管理器列印時,可選擇建立歸檔條目',
+    lowDiskSpaceWarning: '磁碟空間不足警告',
+    lowDiskSpaceDescription: '當可用磁碟空間低於此閾值時顯示警告',
+    printerFirmware: '印表機韌體',
+    checkFirmwareDescription: '檢查Bambu Lab的印表機韌體更新',
+    bambuddySoftware: 'Bambuddy 軟體',
+    autoCheckDescription: '啟動時自動檢查新版本',
+    checkNow: '立即檢查',
+    updateAvailableVersion: '可用更新:v{{version}}',
+    releaseNotes: '發布說明',
+    updateViaDocker: '透過 Docker Compose 更新:',
+    installUpdate: '安裝更新',
+    latestVersionRunning: '您正在執行最新版本',
+    failedToCheckUpdates: '檢查更新失敗:{{error}}',
+    backupRestore: '備份與恢復',
+    backupRestoreDescription: '匯出/匯入設定並設定GitHub 備份',
+    goToBackup: '前往備份',
+    externalUrl: '外部URL',
+    externalUrlDescription: 'Bambuddy可存取的外部URL。用於通知影像和外部整合。',
+    bambuddyUrl: 'Bambuddy URL',
+    externalUrlHint: '包含協定和連接埠(例如:http://192.168.1.100:8000)',
+    ftpRetry: 'FTP重試',
+    ftpRetryDescription: '當印表機Wi-Fi 不穩定時重試FTP操作。適用於3MF下載、列印上傳、縮時攝影下載和韌體更新。',
+    autoRetryDescription: '自動重試失敗的FTP操作',
+    retryAttempts: '重試次數',
+    retryDelay: '重試延遲',
+    connectionTimeout: '連線超時',
+    time_one: '{{count}} 次',
+    time_other: '{{count}} 次',
+    second_one: '{{count}} 秒',
+    second_other: '{{count}} 秒',
+    nSeconds: '{{count}} 秒',
+    increaseForWeakWifi: '對Wi-Fi 訊號弱的印表機增加此值',
+    homeAssistant: 'Home Assistant',
+    homeAssistantFullDescription: '連線到Home Assistant,透過HA REST API控制智慧插座。支援switch、light、input_boolean和script實體。',
+    homeAssistantUrl: 'Home Assistant URL',
+    longLivedAccessToken: '長期存取權杖',
+    haTokenHint: '在HA中建立權杖:個人資料 → 長期存取權杖 → 建立權杖',
+    connectionSuccessful: '連線成功',
+    connectionFailed: '連線失敗',
+    haConnectionSuccess: '已成功連線到Home Assistant。',
+    haConnectionFailed: '連線Home Assistant失敗。',
+    mqttPublishing: 'MQTT發布',
+    mqttDescription: '將BamBuddy事件發布到外部MQTT代理,用於與Node-RED、Home Assistant和其他自動化系統整合。',
+    mqttEnableDescription: '向外部MQTT代理發布事件',
+    brokerHostname: '代理主機名稱',
+    port: '連接埠',
+    usernameOptional: '使用者名稱(可選)',
+    passwordOptional: '密碼(可選)',
+    topicPrefix: '主題前綴',
+    topicPrefixHint: '主題格式:{{prefix}}/printers/<serial>/status 等',
+    prometheusMetrics: 'Prometheus 指標',
+    prometheusEndpointDescription: '在 <code>/api/v1/metrics</code> 公開印表機指標,用於Prometheus/Grafana監控。',
+    bearerTokenOptional: 'Bearer權杖(可選)',
+    bearerTokenHint: '設定後,請求必須包含 <code>Authorization: Bearer <token></code>',
+    metricsConnectionStatus: '連線狀態',
+    metricsPrinterState: '印表機狀態(空閒/列印中等)',
+    metricsPrintProgress: '列印進度 0-100%',
+    metricsBedTemp: '熱床溫度',
+    metricsNozzleTemp: '噴嘴溫度',
+    metricsPrintsTotal: '按結果分類的總列印數',
+    metricsMore: '...以及更多(層數、風扇、佇列、耗材用量)',
+    smartPlugsDescription: '連線智慧插座(Tasmota或Home Assistant)以自動化電源控制並追蹤印表機的能源使用情況。',
+    allOn: '全部開啟',
+    allOff: '全部關閉',
+    addSmartPlug: '新增智慧插座',
+    energySummary: '能源概要',
+    currentPower: '目前功率',
+    plugsOnline: '{{reachable}}/{{total}} 個插座線上',
+    today: '今天',
+    yesterday: '昨天',
+    total: '總計',
+    enablePlugsForSummary: '啟用插座以檢視能源概要',
+    addNotificationProvider: '新增',
+    systemBadge: '(系統)',
+    creating: '建立中...',
+    changing: '修改中...',
+    deleteUserAndItems: '刪除使用者及其所有項目',
+    deleteUserKeepItems: '刪除使用者,保留項目(將變為無主項目)',
+    ok: '確定',
+
+    // 2FA settings
+    twoFa: {
+      totpTitle: '身份驗證器 App (TOTP)',
+      totpDesc: '使用 Google Authenticator、Aegis 或 Authy 等 App。',
+      emailOtpTitle: '郵件 OTP',
+      emailOtpDesc: '登入時向 {{email}} 傳送一次性驗證碼。',
+      emailOtpNoEmail: '請先為帳戶新增信箱地址以啟用此方式。',
+      addEmailFirst: '您的帳戶沒有信箱地址,請聯絡管理員新增。',
+      setupTotp: '設定身份驗證器 App',
+      setupAuthApp: '設定身份驗證器 App',
+      setupInstructions: '使用身份驗證器 App 掃描QR Code,然後輸入驗證碼確認。',
+      manualEntry: '無法掃描?請手動輸入此金鑰:',
+      scannedContinue: '已掃描 — 繼續',
+      enterCodeToConfirm: '請輸入身份驗證器 App 中的6位驗證碼以確認設定。',
+      activate: '啟用',
+      disableTotp: '停用身份驗證器',
+      disableConfirmHint: '請輸入有效的 TOTP 碼或備用碼來停用身份驗證器。',
+      totpDisabled: '身份驗證器 App 已停用。',
+      emailOtpEnabled: '郵件 OTP 已啟用。',
+      emailOtpDisabled: '郵件 OTP 已停用。',
+      smtpRequired: '請先設定並測試SMTP設定。',
+      invalidCode: '無效驗證碼,請重試。',
+      enableEmailOtp: '啟用郵件 OTP',
+      disableEmailOtp: '停用郵件 OTP',
+      emailSetupEnterCode: '驗證碼已傳送至您的信箱地址。請在下方輸入以確認您擁有此信箱。',
+      verifyAndEnable: '驗證並啟用',
+      emailDisablePasswordHint: '請輸入您的帳戶密碼以確認停用郵件 OTP。',
+      passwordPlaceholder: '輸入您的密碼',
+      backupCodesTitle: '儲存備用碼',
+      backupCodesWarning: '請將這些碼儲存在安全的地方。每個碼只能使用一次,且不會再次顯示。',
+      backupCodesRemaining: '剩餘 {{count}} 個備用碼',
+      savedCodes: '已儲存',
+      regenBackup: '重新產生備用碼',
+      regenBackupHint: '輸入目前 TOTP 碼以產生 10 個新備用碼,所有現有備用碼將失效。',
+      newBackupCodes: '新備用碼',
+      linkedAccounts: '已連結的 SSO 帳戶',
+      linkedAccountsDesc: '以下外部身份提供者已與您的帳戶連結。',
+      oidcUnlinked: '帳戶已解除連結。',
+    },
+
+    // OIDC provider settings
+    oidc: {
+      title: 'SSO / OIDC 提供者',
+      desc: '設定 OpenID Connect 提供者以實現單點登入。',
+      addProvider: '新增提供者',
+      newProvider: '新提供者',
+      empty: '尚未設定 OIDC 提供者。',
+      created: '提供者已建立。',
+      updated: '提供者已更新。',
+      deleted: '提供者已刪除。',
+      deleteTitle: '刪除提供者',
+      deleteMessage: '刪除"{{name}}"?所有連結帳戶將斷開連線。',
+      form: {
+        name: '顯示名稱',
+        issuerUrl: '頒發者 URL',
+        clientId: '客戶端 ID',
+        clientSecret: '客戶端金鑰',
+        scopes: '作用域',
+        iconUrl: '圖示 URL(可選)',
+        enabled: '已啟用',
+        autoCreate: '自動建立使用者',
+        autoCreateDesc: '首次登入時自動建立本機帳戶。',
+        autoLink: '自動連結已有帳戶',
+        autoLinkDesc: '首次登入時透過信箱匹配現有本機帳戶並自動連結。',
+        secretHint: '留空以保留目前',
+        secretPlaceholder: '新金鑰',
+      },
+    },
+
+  },
+
+  // Notifications (for push notifications)
+  notification: {
+    printStarted: {
+      title: '列印已開始',
+      body: '{{printer}}:{{filename}} 已開始列印',
+    },
+    printCompleted: {
+      title: '列印已完成',
+      body: '{{printer}}:{{filename}} 已成功完成',
+    },
+    printFailed: {
+      title: '列印失敗',
+      body: '{{printer}}:{{filename}} 列印失敗',
+    },
+    printStopped: {
+      title: '列印已停止',
+      body: '{{printer}}:{{filename}} 已停止',
+    },
+    printProgress: {
+      title: '列印進度',
+      body: '{{printer}}:{{filename}} 已完成 {{percent}}%',
+    },
+    printerOffline: {
+      title: '印表機離線',
+      body: '{{printer}} 已離線',
+    },
+    printerError: {
+      title: '印表機錯誤',
+      body: '{{printer}}:{{error}}',
+    },
+    filamentLow: {
+      title: '耗材不足',
+      body: '{{printer}}:耗材即將用完',
+    },
+    maintenanceDue: {
+      title: '維護到期',
+      body: '{{printer}}:{{items}} 需要關注',
+    },
+  },
+
+  // Errors
+  errors: {
+    generic: '出了點問題',
+    networkError: '網路錯誤。請檢查您的連線。',
+    notFound: '未找到',
+    unauthorized: '未授權',
+    serverError: '伺服器錯誤',
+    validationError: '請檢查您的輸入',
+    printerConnectionFailed: '連線印表機失敗',
+    saveFailed: '儲存更改失敗',
+    deleteFailed: '刪除失敗',
+    loadFailed: '載入資料失敗',
+  },
+
+  // HMS Errors modal
+  hmsErrors: {
+    title: '錯誤 - {{name}}',
+    noErrors: '無錯誤',
+    viewOnWiki: '在拓竹 Wiki 上檢視',
+    clearInstructions: '在印表機上清除錯誤以在此處消除它們。',
+    clearErrors: '清除錯誤',
+    clearSuccess: 'HMS 錯誤已清除',
+    clearFailed: '清除 HMS 錯誤失敗',
+  },
+
+  // MQTT Debug modal
+  mqttDebug: {
+    title: 'MQTT 偵錯日誌',
+    searchPlaceholder: '搜尋主題或負載...',
+    noMessages: '尚未紀錄訊息',
+    startLoggingHint: '點選"開始紀錄"以開始捕獲 MQTT 訊息',
+    noMessagesMatch: '沒有訊息匹配您的篩選條件',
+    adjustFilterHint: '嘗試調整您的搜尋或篩選條件',
+    incoming: '傳入',
+    outgoing: '傳出',
+    loggingStopped: '紀錄已停止',
+    loggingActive: '紀錄中 - 訊息將自動重新整理',
+    startLogging: '開始紀錄',
+    stopLogging: '停止紀錄',
+    clearLog: '清除日誌',
+    topic: '主題',
+    timestamp: '時間戳',
+    direction: '方向',
+    all: '全部',
+  },
+
+  // Printer File Manager modal (printer internal storage)
+  printerFiles: {
+    title: '檔案管理器',
+    storageUsed: '已用:',
+    storageFree: '剩餘:',
+    filterPlaceholder: '篩選檔案...',
+    deleteButton: '刪除',
+    deleteFiles: '刪除 {{count}} 個檔案',
+    deleteFileConfirm: '刪除"{{name}}"?此操作無法復原。',
+    deleteFilesConfirm: '刪除 {{count}} 個選中的檔案?此操作無法復原。',
+    noFiles: '印表機上沒有檔案',
+    loadingFiles: '載入檔案中...',
+    failedToLoad: '載入檔案失敗',
+    toast: {
+      filesDeleted: '已刪除 {{count}} 個檔案',
+      deleteFailed: '刪除失敗:{{error}}',
+    },
+  },
+
+  // Confirmations
+  confirm: {
+    delete: '確定要刪除嗎?',
+    unsavedChanges: '您有未儲存的更改。確定要離開嗎?',
+    clearQueue: '確定要清空佇列嗎?',
+  },
+
+  // Login page
+  login: {
+    title: 'Bambuddy 登入',
+    subtitle: '登入您的帳戶',
+    username: '使用者名稱',
+    usernamePlaceholder: '輸入您的使用者名稱',
+    usernameOrEmail: '使用者名稱或信箱',
+    usernameOrEmailPlaceholder: '使用者名稱或 @ 信箱',
+    password: '密碼',
+    passwordPlaceholder: '輸入您的密碼',
+    signIn: '登入',
+    signingIn: '登入中...',
+    forgotPassword: '忘記密碼?',
+    loginSuccess: '登入成功',
+    loginFailed: '登入失敗',
+    enterCredentials: '請輸入使用者名稱和密碼',
+    enterEmail: '請輸入您的電子郵件地址',
+    oidcLoginFailed: 'OIDC 登入失敗',
+    oidcErrors: {
+      providerError: '身份提供者返回了一個錯誤',
+      missingParameters: 'OIDC 回呼缺少必要引數',
+      invalidState: 'OIDC 狀態無效或已被使用',
+      stateExpired: 'OIDC 登入會話已過期,請重試',
+      providerNotFound: '未找到 OIDC 提供者',
+      discoveryFailed: '無法獲取 OIDC 探索文件',
+      invalidDiscovery: 'OIDC 探索文件無效',
+      networkError: 'OIDC 權杖交換時出現網路錯誤',
+      badResponse: 'OIDC 權杖交換時收到意外回應',
+      noIdToken: 'OIDC 提供者未返回 ID 權杖',
+      validationFailed: 'OIDC 權杖驗證失敗',
+      nonceMismatch: 'OIDC nonce 不符,可能存在重放攻擊',
+      missingSubClaim: 'OIDC 權杖缺少 sub 宣告',
+      noLinkedAccount: '沒有與此 OIDC 身份連結的本機帳戶',
+      accountInactive: '您的帳戶已被停用',
+      userResolutionFailed: '無法解析您的帳戶',
+      internalError: 'OIDC 登入過程中發生內部錯誤',
+      tokenExchangeFailed: 'OIDC 權杖交換失敗',
+    },
+    forgotPasswordTitle: '忘記密碼',
+    forgotPasswordMessage: '如果您忘記了密碼,請聯絡系統管理員進行重設。',
+    forgotPasswordEmailMessage: '輸入您的信箱地址,我們將向您傳送新密碼。',
+    emailAddress: '信箱地址',
+    emailPlaceholder: 'your.email@example.com',
+    cancel: '取消',
+    sending: '傳送中...',
+    sendResetEmail: '傳送重設郵件',
+    howToReset: '如何重設密碼:',
+    resetStep1: '聯絡您的 Bambuddy 管理員',
+    resetStep2: '請他們在使用者管理中重設您的密碼',
+    resetStep3: '他們可以為您設定一個臨時密碼',
+    resetStep4: '使用新密碼登入並在設定中修改密碼',
+    gotIt: '知道了',
+    resetPassword: {
+      title: '設定新密碼',
+      subtitle: '請在下方輸入並確認您的新密碼。',
+      newPassword: '新密碼',
+      newPasswordPlaceholder: '至少 8 個字元',
+      confirmPassword: '確認密碼',
+      confirmPasswordPlaceholder: '重複輸入新密碼',
+      saving: '儲存中…',
+      submit: '設定新密碼',
+      backToLogin: '回到登入',
+      passwordsDoNotMatch: '密碼不符',
+      passwordTooShort: '密碼至少需要 8 個字元',
+      resetFailed: '密碼重設失敗。連結可能已過期。',
+    },
+    twoFA: {
+      title: '兩步驗證',
+      subtitle: '您的帳戶已啟用兩步驗證。請在下方輸入驗證碼。',
+      methodAuthenticator: '身份驗證器 App',
+      methodEmail: '信箱驗證碼',
+      methodBackup: '備用恢復碼',
+      instructionsTotp: '請開啟您的身份驗證器 App,輸入 Bambuddy 的 6 位驗證碼。',
+      instructionsEmail: '6 位驗證碼已傳送至您的信箱,有效期為 10 分鐘。',
+      instructionsEmailNotSent: '點選下方按鈕,透過郵件獲取驗證碼。',
+      instructionsBackup: '請輸入您的一個 8 位備用恢復碼。每個恢復碼只能使用一次。',
+      sendCodeButton: '傳送信箱驗證碼',
+      sendingCode: '傳送中...',
+      resendCode: '重新傳送驗證碼',
+      codeLabel: '驗證碼',
+      backupCodeLabel: '備用恢復碼',
+      codePlaceholder: '000000',
+      backupCodePlaceholder: 'XXXXXXXX',
+      verifyButton: '驗證',
+      verifyingButton: '驗證中...',
+      backToLogin: '← 回到登入頁面',
+      orContinueWith: '或透過以下方式登入',
+      signInWith: '使用 {{provider}} 登入',
+      enterCode: '請輸入驗證碼',
+      sendCodeFailed: '驗證碼傳送失敗',
+      invalidCode: '無效驗證碼,請重試。',
+    },
+
+  },
+
+  // Setup page
+  setup: {
+    title: 'Bambuddy 設定',
+    subtitle: '為您的 Bambuddy 實例設定身份驗證',
+    enableAuth: '啟用身份驗證',
+    adminAccount: '管理員帳戶',
+    adminAccountDesc: '如果管理員使用者已存在,將使用現有管理員帳戶啟用身份驗證。如需使用現有管理員,請將下方欄位留空,或輸入新憑據建立新管理員使用者。',
+    adminUsername: '管理員使用者名稱',
+    adminPassword: '管理員密碼',
+    optionalIfAdminExists: '(如管理員使用者已存在則為可選)',
+    adminUsernamePlaceholder: '輸入管理員使用者名稱(可選)',
+    adminPasswordPlaceholder: '輸入管理員密碼(可選)',
+    confirmPassword: '確認密碼',
+    confirmPasswordPlaceholder: '確認管理員密碼',
+    settingUp: '設定中...',
+    completeSetup: '完成設定',
+    toast: {
+      authEnabledAdminCreated: '身份驗證已啟用並建立了管理員使用者',
+      authEnabledExistingAdmins: '使用現有管理員使用者啟用了身份驗證',
+      setupCompleted: '設定完成',
+      enterBothCredentials: '請輸入管理員使用者名稱和密碼,或將兩者留空以使用現有管理員使用者',
+      passwordsDoNotMatch: '密碼不符',
+      passwordTooShort: '密碼至少需要 6 個字元',
+    },
+  },
+
+  // Password change
+  changePassword: {
+    title: '修改密碼',
+    currentPassword: '目前密碼',
+    currentPasswordPlaceholder: '輸入目前密碼',
+    newPassword: '新密碼',
+    newPasswordPlaceholder: '輸入新密碼(至少 6 個字元)',
+    confirmPassword: '確認新密碼',
+    confirmPasswordPlaceholder: '確認新密碼',
+    passwordsDoNotMatch: '密碼不符',
+    passwordTooShort: '密碼至少需要 6 個字元',
+    changing: '修改中...',
+    success: '密碼修改成功',
+    failed: '密碼修改失敗',
+  },
+
+  // Plate detection alert
+  plateAlert: {
+    title: '列印已暫停!',
+    message: '在列印板上偵測到物體。列印已自動暫停。請清理列印板並繼續列印。',
+    understand: '我知道了',
+  },
+
+  // Camera page
+  camera: {
+    title: '攝影機檢視',
+    invalidPrinterId: '無效的印表機 ID',
+    live: '即時',
+    snapshot: '快照',
+    restartStream: '重新啟動流',
+    refreshSnapshot: '重新整理快照',
+    fullscreen: '全螢幕',
+    exitFullscreen: '離開全螢幕',
+    connectingToCamera: '連線攝影機中...',
+    capturingSnapshot: '拍攝快照中...',
+    connectionLost: '連線已斷開',
+    connectionFailed: '攝影機連線失敗',
+    reconnecting: '{{countdown}} 秒後重新連線...(第 {{attempt}}/{{max}} 次嘗試)',
+    reconnectNow: '立即重新連線',
+    cameraUnavailable: '攝影機不可用',
+    cameraUnavailableDesc: '請確保印表機已通電並已連線。',
+    noCamera: '無可用攝影機',
+    retry: '重試',
+    cameraStream: '攝影機流',
+    zoomOut: '縮小',
+    zoomIn: '放大',
+    resetZoom: '重設縮放',
+    recording: '錄製中',
+    startRecording: '開始錄製',
+    stopRecording: '停止錄製',
+    chamberLight: '切換腔室燈',
+  },
+
+  // Groups management
+  groups: {
+    title: '群組管理',
+    subtitle: '管理存取控制的權限群組',
+    backToSettings: '返回設定',
+    createGroup: '建立群組',
+    noPermission: '您沒有存取此頁面的權限。',
+    system: '系統',
+    noDescription: '無描述',
+    usersCount: '{{count}} 個使用者',
+    permissionsCount: '{{count}} 個權限',
+    edit: '編輯',
+    delete: '刪除',
+    toast: {
+      created: '群組建立成功',
+      updated: '群組更新成功',
+      deleted: '群組刪除成功',
+      enterGroupName: '請輸入群組名稱',
+    },
+    modal: {
+      editGroup: '編輯群組',
+      createGroup: '建立群組',
+      cancel: '取消',
+      saving: '儲存中...',
+      creating: '建立中...',
+      saveChanges: '儲存更改',
+    },
+    form: {
+      groupName: '群組名稱',
+      groupNamePlaceholder: '輸入群組名稱',
+      systemGroupWarning: '系統群組名稱不可更改',
+      description: '描述',
+      descriptionPlaceholder: '輸入描述(可選)',
+      permissions: '權限(已選 {{count}} 個)',
+    },
+    deleteModal: {
+      title: '刪除群組',
+      message: '確定要刪除此群組嗎?此群組中的使用者將失去這些權限。',
+      confirm: '刪除群組',
+    },
+    editor: {
+      title: '編輯群組',
+      createTitle: '建立群組',
+      search: '搜尋權限...',
+      selectAll: '全選',
+      clearAll: '清除全部',
+      permissionsSelected: '已選 {{count}} 個',
+      noResults: '沒有權限匹配您的搜尋',
+    },
+  },
+
+  // Users management
+  users: {
+    title: '使用者管理',
+    subtitle: '管理使用者及其對 Bambuddy 實例的存取',
+    backToSettings: '返回設定',
+    createUser: '建立使用者',
+    noPermission: '您沒有存取此頁面的權限。',
+    admin: '管理員',
+    noGroups: '無群組',
+    active: '活躍',
+    inactive: '非活躍',
+    edit: '編輯',
+    delete: '刪除',
+    system: '系統',
+    noGroupsAvailable: '無可用群組',
+    table: {
+      username: '使用者名稱',
+      groups: '群組',
+      status: '狀態',
+      actions: '操作',
+    },
+    toast: {
+      created: '使用者建立成功',
+      updated: '使用者更新成功',
+      deleted: '使用者刪除成功',
+      fillRequired: '請填寫所有必填欄位',
+      passwordsDoNotMatch: '密碼不符',
+      passwordTooShort: '密碼至少需要 6 個字元',
+    },
+    modal: {
+      createUser: '建立使用者',
+      editUser: '編輯使用者',
+      cancel: '取消',
+      creating: '建立中...',
+      saving: '儲存中...',
+      saveChanges: '儲存更改',
+      advancedAuthSubtitle: '使用進階認證',
+    },
+    form: {
+      username: '使用者名稱',
+      usernamePlaceholder: '輸入使用者名稱',
+      email: '信箱',
+      emailPlaceholder: 'user@example.com',
+      password: '密碼',
+      passwordPlaceholder: '輸入密碼',
+      confirmPassword: '確認密碼',
+      confirmPasswordPlaceholder: '確認密碼',
+      newPasswordPlaceholder: '輸入新密碼',
+      confirmNewPasswordPlaceholder: '確認新密碼',
+      leaveBlankToKeep: '留空以保持目前值',
+      groups: '群組',
+      optional: '可選',
+      autoGeneratedPassword: '將自動產生安全密碼並透過郵件傳送給使用者。',
+      passwordManagedByAdvancedAuth: '密碼由進階認證管理。使用"重設密碼"透過郵件向使用者傳送新密碼。',
+      resetPassword: '重設密碼',
+      resettingPassword: '重設密碼中...',
+    },
+    deleteModal: {
+      title: '刪除使用者',
+      message: '確定要刪除此使用者嗎?此操作無法復原。',
+      confirm: '刪除使用者',
+    },
+  },
+
+  // Stream overlay
+  streamOverlay: {
+    title: '流疊加層',
+    invalidPrinterId: '無效的印表機 ID',
+    cameraStream: '攝影機流',
+    progress: '進度',
+    eta: '預計完成時間',
+    printerIdle: '印表機空閒',
+    printerOffline: '印表機離線',
+    status: {
+      printing: '列印中',
+      paused: '已暫停',
+      finished: '已完成',
+      failed: '失敗',
+      idle: '空閒',
+      unknown: '未知',
+    },
+  },
+
+  // Profiles
+  profiles: {
+    title: '設定檔案',
+    subtitle: '管理您的切片預設和壓力推進校準',
+    tabs: {
+      cloud: '雲端設定檔案',
+      local: '本機設定檔案',
+      kprofiles: 'K 值設定',
+    },
+    localProfiles: {
+      title: '本機設定檔案',
+      subtitle: '從 OrcaSlicer 匯入和管理切片預設',
+      import: '匯入設定檔案',
+      importDesc: '將 .bbscfg、.bbsflmt、.orca_filament、.zip 或 .json 檔案拖放到此處',
+      importing: '匯入中...',
+      search: '搜尋本機預設...',
+      noPresets: '尚無本機預設',
+      badge: '本機',
+      edit: '編輯',
+      delete: '刪除',
+      cancel: '取消',
+      deleteConfirmTitle: '刪除預設',
+      deleteConfirm: '確定要刪除此預設嗎?此操作無法復原。',
+      source: '來源',
+      inheritsFrom: '繼承自',
+      filamentType: '類型',
+      vendor: '廠商',
+      compatiblePrinters: '相容印表機',
+      nozzleTemp: '噴嘴溫度',
+      cost: '成本',
+      density: '密度',
+      pressureAdvance: '壓力推進',
+      filament: '耗材',
+      process: '工藝',
+      printer: '印表機',
+      toast: {
+        importSuccess: '已匯入 {{count}} 個預設',
+        importSkipped: '跳過 {{count}} 個預設(重複)',
+        importError: '匯入時出現 {{count}} 個錯誤',
+        deleted: '預設已刪除',
+        updated: '預設已更新',
+      },
+    },
+    connectedAs: '已連線為',
+    logout: '登出',
+    noLogoutPermission: '您沒有登出的權限',
+    failedToLoad: '載入設定檔案失敗',
+    retry: '重試',
+    time: {
+      justNow: '剛剛',
+      minsAgo: '{{count}} 分鐘前',
+      hoursAgo: '{{count}} 小時前',
+      daysAgo: '{{count}} 天前',
+    },
+    toast: {
+      loggedOut: '已登出',
+    },
+    login: {
+      title: '連線到拓竹雲',
+      subtitle: '跨裝置同步您的切片預設',
+      email: '信箱',
+      password: '密碼',
+      region: '地區',
+      regionGlobal: '全球',
+      regionChina: '中國',
+      verificationCode: '驗證碼',
+      totpCode: '驗證器驗證碼',
+      checkEmail: '檢查您的信箱 ({{email}}) 獲取 6 位驗證碼',
+      enterTotpHint: '輸入驗證器 App 中的 6 位驗證碼',
+      accessToken: '存取權杖',
+      accessTokenHint: '貼上您的拓竹存取權杖(來自 Bambu Studio)',
+      back: '返回',
+      loginButton: '登入',
+      verifyButton: '驗證',
+      setTokenButton: '設定權杖',
+      useToken: '改用存取權杖',
+      useEmail: '改用信箱登入',
+      toast: {
+        loggedIn: '登入成功',
+        codeSent: '驗證碼已傳送到您的信箱',
+        enterTotp: '輸入驗證器 App 中的程式碼',
+        tokenSet: '權杖設定成功',
+      },
+    },
+    presets: {
+      myPreset: '我的預設(可編輯)',
+      duplicate: '複製',
+      editable: '可編輯',
+      failedToLoadDetails: '載入預設詳情失敗',
+      deleteConfirm: '刪除此預設?',
+      deleteWarning: '這將從拓竹雲中永久刪除"{{name}}"。此操作無法復原。',
+      noDuplicatePermission: '您沒有複製預設的權限',
+      noEditPermission: '您沒有編輯預設的權限',
+      noDeletePermission: '您沒有刪除預設的權限',
+      types: {
+        filament: '耗材預設',
+        printer: '印表機預設',
+        process: '工藝預設',
+      },
+      toast: {
+        deleted: '預設已刪除',
+        created: '預設已建立',
+        updated: '預設已更新',
+        duplicated: '預設已複製',
+        fieldAdded: '欄位"{{key}}"已新增',
+        exported: '預設已匯出',
+      },
+      baseLabel: '基礎:{{name}}',
+      currentLabel: '目前:{{name}}',
+      newPreset: '新增預設',
+      editPreset: '編輯預設',
+      duplicatePreset: '複製預設',
+      createNewPreset: '建立新預設',
+      customizeSettings: '自訂新預設的設定',
+      compareWithBase: '與基礎預設比較',
+      compare: '比較',
+      // CreatePresetModal - Basic Info
+      basePreset: '基礎預設',
+      selectBasePreset: '選擇基礎預設...',
+      presetName: '預設名稱',
+      myCustomPreset: '我的自訂預設',
+      inheritsFrom: '繼承自',
+      dropJsonToImport: '拖放 JSON 以匯入',
+      // CreatePresetModal - Tabs
+      tabs: {
+        common: '常用',
+        allFields: '所有欄位',
+      },
+      // CreatePresetModal - All Fields Tab
+      availableFields: '可用欄位',
+      searchFieldsPlaceholder: '搜尋欄位...',
+      noMatchingFields: '沒有匹配的欄位',
+      allFieldsAdded: '所有欄位已新增',
+      addCustomField: '新增自訂欄位',
+      yourOverrides: '您的覆蓋值',
+      noOverridesYet: '尚無覆蓋值',
+      clickFieldsToAdd: '點選左側的欄位進行新增',
+      saveAsTemplate: '儲存為範本',
+      jsonTip: '提示:將 .json 檔案拖放到此對話方塊的任意位置以匯入設定',
+    },
+    cloudView: {
+      searchPlaceholder: '搜尋預設...',
+      templates: '範本',
+      refresh: '重新整理',
+      newPreset: '新增預設',
+      clearFilters: '清除篩選',
+      // Compare mode
+      compareMode: '比較模式',
+      selectAnotherPreset: '選擇另一個 {{type}} 預設',
+      clickTwoPresets: '點選兩個相同類型的預設進行比較',
+      selectFirst: '1. 選擇第一個',
+      selectSecond: '2. 選擇第二個',
+      compareNow: '立即比較',
+      // Status row
+      lastSynced: '上次同步:',
+      showingCount: '顯示 {{showing}} / {{total}} 個預設',
+      noPresetsFound: '未找到預設',
+      // Column headers
+      columns: {
+        filament: '耗材',
+        process: '工藝',
+        printer: '印表機',
+      },
+      noFilamentPresets: '無耗材預設',
+      noProcessPresets: '無工藝預設',
+      noPrinterPresets: '無印表機預設',
+      // Filters
+      filters: {
+        type: '類型',
+        owner: '所有者',
+        printer: '印表機',
+        nozzle: '噴嘴',
+        filament: '耗材',
+        layer: '層',
+        all: '全部',
+        myPresets: '我的預設',
+        builtIn: '內建',
+        process: '工藝',
+      },
+      // Permissions
+      noTemplatesPermission: '您沒有管理範本的權限',
+      noRefreshPermission: '您沒有重新整理設定檔案的權限',
+      noCreatePermission: '您沒有建立預設的權限',
+    },
+    templates: {
+      title: '快速範本',
+      noTemplates: '尚無範本',
+      createFirst: '從預設編輯器建立範本',
+      typeFilter: '類型:',
+      deleteTitle: '刪除範本',
+      deleteWarning: '此操作無法復原',
+      deleteConfirm: '確定要刪除"{{name}}"嗎?',
+      namePlaceholder: '範本名稱',
+      descriptionPlaceholder: '描述',
+      settingsJson: '設定 (JSON)',
+      fieldsCount: '{{count}} 個欄位',
+      shownInModals: '在對話方塊中顯示',
+      hiddenInModals: '在對話方塊中隱藏',
+      apply: '套用',
+      toast: {
+        deleted: '範本已刪除',
+        updated: '範本已更新',
+        created: '範本已建立',
+        applied: '範本已套用',
+      },
+    },
+  },
+
+  // Support/Debug
+  support: {
+    debugLoggingActive: '偵錯日誌紀錄已啟用',
+    manageLogs: '管理',
+    collectItem7: '印表機連線和韌體版本',
+    collectItem8: '整合狀態(Spoolman、MQTT、HA)',
+    collectItem9: '網路介面(僅子網)',
+    collectItem10: 'Python 套件版本',
+    collectItem11: '資料庫健康檢查',
+    collectItem12: 'Docker 環境詳情',
+  },
+
+  // File manager
+  fileManager: {
+    title: '檔案管理器',
+    subtitle: '組織和管理您的列印檔案',
+    uploadFiles: '上傳檔案',
+    newFolder: '新增資料夾',
+    folderName: '資料夾名稱',
+    folderNamePlaceholder: '例如:功能零件',
+    renameFile: '重新命名檔案',
+    renameFolder: '重新命名資料夾',
+    moveFiles: '移動 {{count}} 個檔案',
+    rootNoFolder: '根目錄(無資料夾)',
+    current: '目前',
+    linkFolder: '連結資料夾',
+    linkFolderDescription: '將"{{name}}"連結到專案或歸檔以便快速存取。',
+    project: '專案',
+    archive: '歸檔',
+    noProjectsFound: '未找到專案',
+    noArchivesFound: '未找到歸檔',
+    unlink: '取消連結',
+    link: '連結',
+    dragDropFiles: '將檔案拖放到此處',
+    dropFilesHere: '將檔案放在此處',
+    orClickToBrowse: '或點選瀏覽',
+    allFileTypesSupported: '支援所有檔案類型。ZIP 檔案將被解壓。',
+    zipFilesDetected: '偵測到 ZIP 檔案',
+    zipExtractOptions: 'ZIP 檔案將被解壓。選擇如何處理資料夾結構:',
+    preserveZipStructure: '保留 ZIP 中的資料夾結構',
+    createFolderFromZip: '從 ZIP 檔名建立資料夾',
+    stlThumbnailGeneration: 'STL 縮圖產生',
+    zipMayContainStl: 'ZIP 檔案可能包含 STL 檔案。可以在解壓時產生縮圖。',
+    thumbnailsCanBeGenerated: '可以為 STL 檔案產生縮圖。大型模型可能需要更長時間處理。',
+    generateThumbnailsForStl: '為 STL 檔案產生縮圖',
+    threemfDetected: '偵測到 3MF 檔案',
+    threemfExtractionInfo: '將自動從 3MF 檔案中提取印表機型號、材料、顏色和列印設定。',
+    willBeExtracted: '將被解壓',
+    filesExtracted: '已解壓 {{count}} 個檔案',
+    uploadComplete: '上傳完成:{{succeeded}} 個成功',
+    uploadFailed: '上傳失敗',
+    zipFilesFailed: '{{count}} 個檔案失敗',
+    uploading: '上傳中...',
+    changeLink: '更改連結...',
+    linkTo: '連結到...',
+    linkToProjectOrArchive: '連結到專案或歸檔',
+    addToQueue: '新增到佇列',
+    schedulePrint: '排程',
+    generateThumbnail: '產生縮圖',
+    generateThumbnails: '產生縮圖',
+    generateThumbnailsForMissing: '為缺少縮圖的 STL 檔案產生縮圖',
+    gridView: '網格檢視',
+    listView: '列表檢視',
+    lowDiskSpaceWarning: '磁碟空間不足警告',
+    lowDiskSpaceDetails: '僅剩 {{free}}(總共 {{total}})。閾值設定為 {{threshold}} GB。',
+    files: '檔案',
+    folders: '資料夾',
+    size: '大小',
+    free: '剩餘',
+    allFiles: '所有檔案',
+    wrap: '換行',
+    enableTextWrapping: '啟用文字換行',
+    disableTextWrapping: '停用文字換行',
+    dragToResizeTooltip: '拖曳調整大小,雙擊重設',
+    searchFiles: '搜尋檔案...',
+    allTypes: '所有類型',
+    prints: '列印',
+    ascending: '升序',
+    descending: '降序',
+    resultsCount: '{{showing}} / {{total}} 個檔案',
+    selectAll: '全選',
+    deselectAll: '取消全選',
+    selected: '已選擇 {{count}} 個',
+    adding: '新增中...',
+    loadingFiles: '載入檔案中...',
+    folderIsEmpty: '資料夾為空',
+    noFilesYet: '尚無檔案',
+    folderEmptyDescription: '上傳檔案或將檔案移入此資料夾以開始使用。',
+    noFilesDescription: '上傳檔案以開始組織您的列印相關檔案。',
+    noMatchingFiles: '沒有匹配的檔案',
+    noMatchingFilesDescription: '沒有檔案匹配您目前的搜尋或篩選條件。',
+    clearFilters: '清除篩選',
+    printedCount: '已列印 {{count}} 次',
+    uploadedBy: '上傳者',
+    deleteFolder: '刪除資料夾',
+    deleteFile: '刪除檔案',
+    deleteFilesCount: '刪除 {{count}} 個檔案',
+    deleteFolderConfirm: '確定要刪除此資料夾嗎?其中的所有檔案也將被刪除。',
+    deleteFileConfirm: '確定要刪除此檔案嗎?',
+    deleteFilesConfirm: '確定要刪除 {{count}} 個選中的檔案嗎?此操作無法復原。',
+    deleting: '刪除中...',
+    noPermissionRenameFolder: '您沒有重新命名資料夾的權限',
+    noPermissionLinkFolder: '您沒有連結資料夾的權限',
+    noPermissionDeleteFolder: '您沒有刪除資料夾的權限',
+    noPermissionPrint: '您沒有列印的權限',
+    noPermissionAddToQueue: '您沒有新增到佇列的權限',
+    noPermissionDownload: '您沒有下載檔案的權限',
+    noPermissionRenameFile: '您沒有重新命名此檔案的權限',
+    noPermissionGenerateThumbnail: '您沒有產生縮圖的權限',
+    noPermissionDeleteFile: '您沒有刪除此檔案的權限',
+    noPermissionCreateFolder: '您沒有建立資料夾的權限',
+    noPermissionUpload: '您沒有上傳檔案的權限',
+    noPermissionMoveFiles: '您沒有移動檔案的權限',
+    noPermissionDeleteFiles: '您沒有刪除檔案的權限',
+    // External folder
+    linkExternal: '連結外部',
+    linkExternalFolder: '連結外部資料夾',
+    linkExternalFolderDescription: '將主機目錄(NAS、USB、網路共享)掛載到檔案管理器中。檔案不會被複制——直接從原始路徑存取。',
+    externalFolderNamePlaceholder: '例如:NAS列印檔案',
+    externalPath: '主機路徑',
+    externalPathHelp: 'Docker主機上目錄的絕對路徑。必須以繫結掛載方式掛載到容器中。',
+    readOnly: '只讀',
+    readOnlyHelp: '防止上傳和刪除',
+    showHiddenFiles: '顯示隱藏檔案(點檔案)',
+    externalFolder: '外部資料夾',
+    scanFolder: '掃描',
+    toast: {
+      folderCreated: '資料夾已建立',
+      folderDeleted: '資料夾已刪除',
+      fileDeleted: '檔案已刪除',
+      filesDeleted: '已刪除 {{count}} 個檔案',
+      filesMoved: '檔案已移動',
+      folderLinked: '資料夾已連結',
+      folderUnlinked: '資料夾已取消連結',
+      externalFolderLinked: '外部資料夾已連結並掃描',
+      folderScanned: '掃描完成:新增 {{added}} 個,移除 {{removed}} 個',
+      addedToQueue: '已將 {{count}} 個檔案新增到佇列',
+      addedToQueuePartial: '已新增 {{added}} 個檔案,{{failed}} 個失敗',
+      failedToAddToQueue: '新增檔案失敗:{{error}}',
+      fileRenamed: '檔案已重新命名',
+      folderRenamed: '資料夾已重新命名',
+      thumbnailsGenerated: '已產生 {{count}} 個縮圖',
+      thumbnailsGeneratedPartial: '已產生 {{succeeded}} 個縮圖,{{failed}} 個失敗',
+      noStlMissingThumbnails: '沒有缺少縮圖的 STL 檔案',
+      failedToGenerateThumbnails: '產生縮圖失敗:{{error}}',
+      thumbnailGenerated: '縮圖已產生',
+      failedToGenerateThumbnail: '產生縮圖失敗:{{error}}',
+    },
+  },
+
+  // Projects
+  projects: {
+    title: '專案',
+    subtitle: '組織和追蹤您的 3D 列印專案',
+    newProject: '新增專案',
+    editProject: '編輯專案',
+    deleteProject: '刪除專案',
+    projectName: '專案名稱',
+    description: '描述',
+    noProjects: '尚無專案',
+    noProjectsFiltered: '沒有{{status}}專案',
+    noProjectsFilteredHelp: '您沒有任何{{status}}專案。當專案狀態更改時,它們將出現在這裡。',
+    createFirst: '建立您的第一個項目以開始組織相關列印、追蹤進度和管理構建。',
+    createFirstButton: '建立您的第一個項目',
+    create: '建立',
+    files: '檔案',
+    prints: '列印',
+    plates: '板',
+    parts: '零件',
+    lastModified: '最後修改',
+    deleteConfirm: '確定要刪除此項目嗎?歸檔和佇列項目將被取消連結但不會被刪除。',
+    addFiles: '新增檔案',
+    removeFile: '移除檔案',
+    viewDetails: '檢視詳情',
+    // Modal fields
+    namePlaceholder: '例如:Voron 2.4 構建',
+    descriptionPlaceholder: '可選描述...',
+    color: '顏色',
+    targetPlates: '目標板數',
+    targetPlatesPlaceholder: '例如:25',
+    targetPlatesHelp: '列印任務數量',
+    targetParts: '目標零件數',
+    targetPartsPlaceholder: '例如:150',
+    targetPartsHelp: '所需零件總數',
+    tagsLabel: '標籤(逗號分隔)',
+    tagsPlaceholder: '例如:voron、功能件、禮物',
+    dueDate: '截止日期',
+    priority: '優先順序',
+    priorityLow: '低',
+    priorityNormal: '普通',
+    priorityHigh: '高',
+    priorityUrgent: '緊急',
+    // Status
+    statusActive: '進行中',
+    statusCompleted: '已完成',
+    statusArchived: '已歸檔',
+    done: '完成',
+    completed: '已完成',
+    failed: '失敗',
+    inQueue: '佇列中',
+    noPrintsYet: '尚無列印',
+    // Footer stats
+    printJobs: '列印任務(板)',
+    partsPrinted: '已列印零件',
+    failedParts: '失敗零件',
+    // Actions
+    import: '匯入',
+    export: '匯出',
+    importProject: '匯入專案',
+    exportAll: '匯出所有專案',
+    loading: '載入專案中...',
+    // Permissions
+    noEditPermission: '您沒有編輯專案的權限',
+    noDeletePermission: '您沒有刪除專案的權限',
+    noCreatePermission: '您沒有建立專案的權限',
+    noImportPermission: '您沒有匯入專案的權限',
+    noExportPermission: '您沒有匯出專案的權限',
+    // Toast
+    toast: {
+      created: '專案已建立',
+      updated: '專案已更新',
+      deleted: '專案已刪除',
+      imported: '專案已匯入',
+      multipleImported: '已匯入 {{count}} 個項目',
+      importFailed: '匯入失敗',
+      exported: '專案已匯出(僅中繼資料)',
+    },
+  },
+
+  // Project detail page
+  projectDetail: {
+    notFound: '未找到專案',
+    backToProjects: '返回專案',
+    export: '匯出',
+    exportProject: '匯出專案',
+    noExportPermission: '您沒有匯出專案的權限',
+    noEditPermission: '您沒有編輯專案的權限',
+    partOf: '屬於:',
+    priorityLabel: '優先順序:',
+    noPrints: '此項目尚無列印',
+    status: {
+      active: '進行中',
+      completed: '已完成',
+      archived: '已歸檔',
+    },
+    priority: {
+      low: '低',
+      normal: '普通',
+      high: '高',
+      urgent: '緊急',
+    },
+    dueDate: {
+      overdue: '已逾期',
+      today: '今天到期',
+      daysLeft: '還有 {{count}} 天',
+    },
+    progress: {
+      platesProgress: '板進度',
+      partsProgress: '零件進度',
+      printJobs: '列印任務',
+      parts: '零件',
+      percentComplete: '{{percent}}% 完成',
+      remaining: '剩餘 {{count}} 個',
+    },
+    stats: {
+      printJobs: '列印任務',
+      total: '總計',
+      failed: '{{count}} 個失敗',
+      partsPrinted: '已列印 {{count}} 個零件',
+      printTime: '列印時間',
+      filamentUsed: '耗材用量',
+    },
+    cost: {
+      title: '成本追蹤',
+      filamentCost: '耗材成本',
+      energy: '能源',
+      totalCost: '總成本',
+      total: '總計',
+      includesBom: '含物料清單',
+      budget: '預算',
+      remaining: '剩餘',
+    },
+    subProjects: {
+      title: '子專案 ({{count}})',
+    },
+    notes: {
+      title: '備註',
+      noEditPermission: '您沒有編輯備註的權限',
+      placeholder: '新增關於此項目的備註...',
+      empty: '尚無備註。點選編輯新增備註。',
+    },
+    files: {
+      title: '檔案',
+      linkFolders: '從檔案管理器連結資料夾',
+      forQuickAccess: '到此項目以便快速存取。',
+      fileCount: '{{count}} 個檔案',
+      empty: '未連結資料夾。前往檔案管理器將資料夾連結到此項目。',
+      noFiles: '此資料夾中沒有檔案。',
+      print: '立即列印',
+      addToQueue: '加入佇列',
+    },
+    bom: {
+      title: '材料清單',
+      acquired: '已獲取 {{completed}}/{{total}}',
+      showAll: '顯示全部',
+      hideDone: '隱藏已完成',
+      addPart: '新增零件',
+      noAddPermission: '您沒有新增零件的權限',
+      partNamePlaceholder: '零件名稱(例如:M3x8 螺絲)',
+      partName: '零件名稱',
+      qty: '數量',
+      price: '價格 ({{currency}})',
+      sourcingUrlPlaceholder: '採購連結(可選)',
+      remarksPlaceholder: '備註(可選)',
+      deletePart: '刪除零件',
+      deleteConfirm: '確定要刪除"{{name}}"嗎?',
+      noUpdatePermission: '您沒有更新零件的權限',
+      noEditPermission: '您沒有編輯零件的權限',
+      noDeletePermission: '您沒有刪除零件的權限',
+      totalCost: '總成本:',
+      empty: '材料清單中沒有零件。新增硬體、電子元件或其他元件以追蹤需要採購的物品。',
+    },
+    timeline: {
+      title: '活動時間線',
+      empty: '尚無活動。',
+    },
+    template: {
+      saveAsTemplate: '儲存為範本',
+      noCreatePermission: '您沒有建立範本的權限',
+    },
+    queue: {
+      title: '佇列',
+      viewAll: '檢視全部',
+      printing: '{{count}} 個列印中',
+      queued: '{{count}} 個佇列中',
+    },
+    prints: {
+      title: '列印 ({{count}})',
+    },
+    toast: {
+      projectUpdated: '專案已更新',
+      partAdded: '零件已新增',
+      partRemoved: '零件已移除',
+      exportFailed: '匯出失敗',
+      projectExported: '專案已匯出',
+      templateCreated: '範本已建立',
+    },
+  },
+
+  // System info
+  system: {
+    title: '系統資訊',
+    version: '版本',
+    uptime: '執行時間',
+    cpuUsage: 'CPU 使用率',
+    memoryUsage: '記憶體使用率',
+    diskUsage: '磁碟使用率',
+    networkInfo: '網路資訊',
+    logs: '日誌',
+    debugMode: '偵錯模式',
+    enableDebug: '啟用偵錯日誌',
+    disableDebug: '停用偵錯日誌',
+    downloadLogs: '下載日誌',
+    clearLogs: '清除日誌',
+    dockerInfo: 'Docker 資訊',
+    containerName: '容器名稱',
+    imageName: '映象名稱',
+    platform: '平臺',
+    architecture: '架構',
+  },
+
+  // Library (K Profiles)
+  library: {
+    title: '耗材庫',
+    addFilament: '新增耗材',
+    editFilament: '編輯耗材',
+    deleteFilament: '刪除耗材',
+    vendor: '廠商',
+    material: '材料',
+    color: '顏色',
+    kFactor: 'K 值',
+    temperature: '溫度',
+    noFilaments: '耗材庫中沒有耗材',
+    deleteConfirm: '確定要刪除此耗材嗎?',
+    importFromPrinter: '從印表機匯入',
+    exportToFile: '匯出到檔案',
+  },
+
+  // Spoolman
+  spoolman: {
+    title: 'Spoolman 整合',
+    enabled: 'Spoolman 已啟用',
+    url: 'Spoolman URL',
+    connected: '已連線',
+    disconnected: '未連線',
+    testConnection: '測試連線',
+    sync: '同步',
+    syncing: '同步中...',
+    lastSync: '上次同步',
+    linkToSpoolman: '連結到 Spoolman',
+    openInSpoolman: '在 Spoolman 中開啟',
+    unlinkSpool: '取消連結耗材',
+    unlinkConfirmTitle: '解開料盤?',
+    unlinkConfirmMessage: '這將斷開卷軸與 Spoolman 的連線。Spoolman 中的卷軸資料將保持不變。',
+    selectSpool: '選擇耗材',
+    noUnlinkedSpools: '無未連結的耗材',
+    linkSuccess: '耗材已成功連結到 Spoolman',
+    linkFailed: '連結耗材失敗',
+    unlinkSuccess: '已成功從 Spoolman 取消連結耗材',
+    unlinkFailed: '取消連結耗材失敗',
+    spoolId: '耗材 ID',
+    fillSourceLabel: '(Spoolman)',
+    weight: '重量',
+    remaining: '剩餘',
+    disableWeightSync: '停用 AMS 估計重量同步',
+    disableWeightSyncDesc: '不從 AMS 估計值更新剩餘容量。如果您更喜歡 Spoolman 的用量追蹤而非 AMS 百分比估計,請使用此選項。新耗材仍將使用 AMS 估計值作為初始重量。',
+    reportPartialUsage: '報告失敗列印的部分用量',
+    reportPartialUsageDesc: '當列印失敗或被取消時,根據層進度報告估計的耗材使用量。',
+  },
+
+  // Inventory
+  inventory: {
+    title: '耗材庫存',
+    addSpool: '新增耗材',
+    editSpool: '編輯耗材',
+    material: '材料',
+    selectMaterial: '選擇材料...',
+    subtype: '子類型',
+    brand: '品牌',
+    searchBrand: '搜尋品牌...',
+    useCustomBrand: '使用"{{brand}}"',
+    useCustomMaterial: '使用自訂材料:{{material}}',
+    colorName: '顏色名稱',
+    colorNamePlaceholder: '翡翠白、烈焰紅...',
+    color: '顏色',
+    hexColor: '十六進位顏色',
+    pickColor: '選擇自訂顏色',
+    labelWeight: '標籤重量',
+    coreWeight: '空盤重量',
+    searchSpoolWeight: '搜尋耗材重量...',
+    weightUsed: '已使用',
+    currentWeight: '剩餘重量',
+    measuredWeight: '稱量重量',
+    spoolName: '料盤',
+    costPerKg: '每公斤成本',
+    measuredWeightError: '稱量重量必須在 {{min}}g 到 {{max}}g 之間。',
+    slicerFilament: '切片耗材',
+    slicerFilamentName: '切片預設名稱',
+    slicerPreset: '切片預設',
+    searchPresets: '搜尋耗材預設...',
+    selectedPreset: '已選擇',
+    noPresetsFound: '未找到預設',
+    tempOverrides: '溫度覆蓋',
+    note: '備註',
+    notePlaceholder: '關於此耗材的任何備註...',
+    archive: '歸檔',
+    restore: '恢復',
+    noSpools: '尚無耗材。新增您的第一個耗材開始使用。',
+    noManualSpools: '沒有手動新增的耗材。請先向庫存中新增耗材。',
+    kProfiles: 'K 值設定',
+    addKProfile: '新增 K 值設定',
+    assignSpool: '分配耗材',
+    unassignSpool: '取消分配',
+    assignSuccess: '耗材已分配,AMS 槽位已設定',
+    assignFailed: '分配耗材失敗',
+    assignMismatchTitle: '材料不符',
+    assignMismatchMessage: '所選料盤材料 "{{spoolMaterial}}" 與 {{location}} 的料槽材料 "{{trayMaterial}}" 不符。仍要分配嗎?',
+    assignMismatchConfirm: '仍然分配',
+    assignPartialMismatchMessage: '料盤材料 "{{spoolMaterial}}" 與 {{location}} 的 "{{trayMaterial}}" 相近但不完全一致。是否繼續?',
+    assignProfileMismatchMessage: '料盤設定 "{{spoolProfile}}" 與 {{location}} 的料槽設定 "{{trayProfile}}" 不一致。是否繼續?',
+    selectSpool: '選擇要分配到此槽位的耗材',
+    assigned: '已分配',
+    assigning: '分配中...',
+    searchSpools: '搜尋耗材...',
+    showAllSpools: '顯示所有耗材',
+    allMaterials: '所有材料',
+    filterByBrand: '按品牌篩選...',
+    showArchived: '顯示已歸檔',
+    quickAdd: '快速新增(庫存)',
+    quantity: '數量',
+    stock: '庫存',
+    configured: '已設定',
+    spoolsCreated: '已建立 {{count}} 個耗材',
+    spoolCreated: '耗材已建立',
+    spoolUpdated: '耗材已更新',
+    spoolDeleted: '耗材已刪除',
+    spoolArchived: '耗材已歸檔',
+    spoolRestored: '耗材已恢復',
+    deleteConfirm: '確定要刪除此耗材嗎?此操作無法復原。',
+    archiveConfirm: '確定要歸檔此耗材嗎?',
+    advancedSettings: '進階設定',
+    // Tabs
+    filamentInfoTab: '耗材資訊',
+    paProfileTab: 'PA 設定',
+    filamentInfo: '耗材',
+    additional: '附加',
+    // Cloud
+    loadingPresets: '載入雲端預設中...',
+    cloudConnected: '雲端已連線',
+    cloudNotConnected: '雲端未連線(使用預設值)',
+    // Colors
+    recentColors: '最近',
+    searchColors: '搜尋顏色...',
+    searchResults: '搜尋結果',
+    allColors: '所有顏色',
+    commonColors: '常用顏色',
+    showLess: '顯示更少',
+    showAll: '顯示全部',
+    noColorsFound: '沒有顏色匹配您的搜尋',
+    noResults: '未找到匹配項',
+    // PA Profiles
+    selectMaterialFirst: '請先在耗材資訊分頁中選擇材料。',
+    noPrintersConfigured: '未設定印表機。新增印表機以使用 PA 設定。',
+    matchingFilter: '匹配',
+    anyBrand: '任何品牌',
+    anyVariant: '任何變體',
+    autoSelect: '自動選擇',
+    matches: '匹配',
+    match: '匹配',
+    noMatches: '無匹配',
+    connected: '已連線',
+    offline: '離線',
+    printerOffline: '印表機離線。連線後檢視校準設定。',
+    noKProfilesMatch: '沒有 K 值設定匹配所選耗材。',
+    leftNozzle: '左噴嘴',
+    rightNozzle: '右噴嘴',
+    profilesSelected: '個校準設定已選擇',
+    // Stats & enhanced table
+    totalInventory: '總庫存',
+    totalConsumed: '總消耗',
+    byMaterial: '按材料',
+    inPrinter: '在印表機中',
+    lowStock: '庫存不足',
+    sinceTracking: '自開始追蹤',
+    loadedInAms: '已裝載到 AMS/外接',
+    remaining: '剩餘',
+    weightCheck: '重量檢查',
+    lastWeighed: '上次稱量',
+    neverWeighed: '從未稱量',
+    search: '搜尋耗材...',
+    showing: '顯示',
+    to: '到',
+    of: '共',
+    show: '顯示',
+    spools: '個耗材',
+    spool: '個耗材',
+    page: '頁',
+    noSpoolsMatch: '未找到結果',
+    noSpoolsMatchDesc: '嘗試調整您的搜尋或篩選條件。',
+    active: '活躍',
+    archived: '已歸檔',
+    all: '全部',
+    used: '已使用',
+    new: '新的',
+    clearFilters: '清除篩選',
+    table: '表格',
+    cards: '卡片',
+    net: '淨重',
+    // Grouping
+    groupSimilar: '分組',
+    groupedSpools: '{{count}} 個相同耗材',
+    groupedRows: '行',
+    // Column config
+    columns: '列',
+    configureColumns: '設定列',
+    configureColumnsDesc: '拖曳以重新排序列或使用箭頭。使用眼睛圖示切換可見性。',
+    visible: '可見',
+    reset: '重設',
+    cancel: '取消',
+    applyChanges: '套用更改',
+    moveUp: '上移',
+    moveDown: '下移',
+    hideColumn: '隱藏列',
+    showColumn: '顯示列',
+    // Tag linking
+    linkToSpool: '連結到耗材',
+    tagLinked: '標籤已連結到耗材',
+    tagLinkFailed: '連結標籤失敗',
+    tagAlreadyLinked: '標籤已連結到其他耗材',
+    unknownTag: '偵測到未知 RFID 標籤',
+    // Usage history
+    usageHistory: '使用歷史',
+    noUsageHistory: '尚無使用紀錄',
+    printName: '列印名稱',
+    weightConsumed: '消耗重量',
+    clearHistory: '清除',
+    historyCleared: '使用歷史已清除',
+    fillSourceLabel: '(庫存)',
+    lowStockThresholdError: '閾值必須在 0.1 到 99.9 之間',
+  },
+
+  // Timelapse
+  timelapse: {
+    title: '縮時攝影',
+    create: '建立縮時攝影',
+    download: '下載',
+    delete: '刪除',
+    preview: '預覽',
+    frameRate: '幀率',
+    quality: '品質',
+    processing: '處理中...',
+    noTimelapses: '無可用縮時攝影',
+  },
+
+  // AMS
+  ams: {
+    title: 'AMS',
+    slot: '槽位',
+    empty: '空',
+    emptySlot: '空槽位',
+    unknown: '未知',
+    humidity: '濕度',
+    temperature: '溫度',
+    filamentType: '耗材類型',
+    filamentColor: '顏色',
+    remaining: '剩餘',
+    history: 'AMS 歷史',
+    noHistory: '無可用歷史',
+    configureSlot: '設定槽位',
+    externalSpool: '外接耗材',
+    profile: '設定',
+    kFactor: 'K 值',
+    fill: '填充',
+    configure: '設定',
+    used: '已使用',
+    remainingUnit: '剩餘',
+  },
+
+  // Print modal
+  printModal: {
+    title: '開始列印',
+    selectPrinter: '選擇印表機',
+    selectPlate: '選擇板',
+    filamentMapping: '耗材對應',
+    totalCost: '總成本:',
+    slotRemainingShort: ' - 剩餘 {{grams}}g',
+    printSettings: '列印設定',
+    bedLeveling: '熱床調平',
+    flowCalibration: '流量校準',
+    vibrationCalibration: '振動校準',
+    layerInspection: '首層檢查',
+    timelapse: '縮時攝影',
+    startPrint: '開始列印',
+    addToQueue: '新增到佇列',
+    cancel: '取消',
+    noPrintersAvailable: '無可用印表機',
+    printerBusy: '印表機忙碌',
+    printerOffline: '印表機離線',
+    sameTypeDifferentColor: '相同類型,不同顏色',
+    filamentTypeNotLoaded: '耗材類型未裝載',
+    openCalendar: '開啟日曆',
+    leftNozzle: '左',
+    rightNozzle: '右',
+    leftNozzleTooltip: '左噴嘴',
+    rightNozzleTooltip: '右噴嘴',
+    filamentOverride: '耗材覆蓋',
+    filamentOverrideHint: '可選覆蓋用於基於模型的耗材分配。排程器將使用您選擇的耗材而不是原始 3MF 值進行匹配。',
+    originalFilament: '原始',
+    overrideWith: '覆蓋為',
+    resetToOriginal: '恢復為原始',
+    insufficientFilamentTitle: '耗材不足',
+    insufficientFilamentMessage: '部分已分配料盤的剩餘耗材少於本次列印所需:',
+    insufficientFilamentLine: '{{printer}} - {{slot}}:需要 {{required}}g,剩餘 {{remaining}}g',
+    printAnyway: '仍然列印',
+    forceColorMatch: '強制顏色匹配',
+    staggerPrinterStarts: '錯開印表機啟動',
+    staggerGroupSize: '群組大小',
+    staggerInterval: '間隔(分鐘)',
+    staggerPreview: '{{printers}} 台印表機 → 分成 {{groups}} 組,每組 {{size}} 台,每 {{interval}} 分鐘啟動一組',
+    staggerLastGroup: '最後一組:{{count}}',
+    staggerTotal: '總計:{{minutes}} 分鐘',
+    staggerToPrinters: '分批傳送到 {{count}} 臺印表機',
+    gcodeInjection: '注入自動列印G-code',
+  },
+
+  // Backup
+  backup: {
+    title: '備份與恢復',
+    createBackup: '建立備份',
+    restoreBackup: '恢復備份',
+    restoreDescription: '從備份檔案替換所有資料',
+    downloadBackup: '下載備份',
+    uploadBackup: '上傳備份',
+    lastBackup: '上次備份',
+    autoBackup: '自動備份',
+    backupNow: '立即備份',
+    restoreWarning: '警告:恢復備份將覆蓋所有目前資料。',
+    includeArchives: '包含歸檔',
+    includeSettings: '包含設定',
+    includeProfiles: '包含設定檔案',
+    backupSuccess: '備份建立成功',
+    restoreSuccess: '備份恢復成功',
+    backupFailed: '備份失敗',
+    restoreFailed: '恢復失敗',
+    restoreNote: '恢復期間虛擬印表機將停止',
+
+    // GitHub Backup
+    githubBackup: 'GitHub 備份',
+    enabled: '已啟用',
+    cloudLoginRequired: '需要登入 Bambu Cloud。請在 設定檔案 → 雲設定檔案 中登入以啟用 GitHub 備份。',
+    cloudLoginRequiredShort: '需要雲端登入',
+    githubDescription: '自動將您的設定檔案同步到私有 GitHub 倉庫以進行備份和版本歷史紀錄。',
+    repositoryUrl: '倉庫 URL',
+    personalAccessToken: '個人存取權杖',
+    tokenSaved: '(已儲存)',
+    enterNewToken: '輸入新權杖以更新',
+    tokenHint: '具有內容讀寫權限的細粒度權杖',
+    branch: '分支',
+    manualOnly: '僅手動',
+    hourly: '每小時',
+    daily: '每天',
+    weekly: '每週',
+    includeInBackup: '包含在備份中',
+    kProfiles: 'K 設定檔案',
+    kProfilesDescription: '來自已連線印表機的壓力推進校準',
+    noPrintersConnected: '沒有印表機連線',
+    printersConnected: '{{connected}}/{{total}} 已連線',
+    cloudProfiles: '雲設定檔案',
+    cloudProfilesDescription: '來自 Bambu Cloud 的耗材、印表機和工藝預設',
+    appSettings: '應用程式設定',
+    appSettingsDescription: 'Bambuddy 設定(完整資料庫)',
+    spoolInventory: '耗材庫存',
+    spoolInventoryDescription: '耗材卷軸、使用紀錄和成本追蹤',
+    printArchives: '列印檔案',
+    printArchivesDescription: '列印歷史中繼資料(不含 gcode/3MF 檔案)',
+    lastBackupAt: '上次備份:',
+    noBackupsYet: '尚無備份',
+    next: '下次:',
+    startingBackup: '正在啟動備份...',
+    test: '測試',
+    enableBackup: '啟用備份',
+    testConnection: '測試連線',
+    enterRepoUrl: '請輸入倉庫 URL',
+    enterRepoAndToken: '請輸入倉庫 URL 和存取權杖',
+    repoRequired: '倉庫 URL 為必填項',
+    tokenRequired: '存取權杖為必填項',
+    githubBackupEnabled: 'GitHub 備份已啟用',
+    tokenUpdated: '權杖已更新',
+    settingsSaved: '設定已儲存',
+    failedToSave: '儲存失敗:{{message}}',
+    backupCompleteFiles: '備份完成 - {{count}} 個檔案已更新',
+    backupSkippedNoChanges: '備份已跳過 - 無更改',
+    backupFailed2: '備份失敗:{{message}}',
+    clearedLogs: '已清除 {{count}} 條日誌',
+    failedToClearLogs: '清除日誌失敗:{{message}}',
+
+    // History
+    history: '歷史紀錄',
+    clear: '清除',
+    date: '日期',
+    status: '狀態',
+    commit: '提交',
+
+    // Local Backup
+    localBackup: '本機備份',
+    localBackupDescription: '建立 Bambuddy 資料的完整備份,包括資料庫、檔案、上傳和所有檔案。',
+    downloadBackupLabel: '下載備份',
+    completeBackupZip: '完整備份:資料庫 + 所有檔案(ZIP)',
+    download: '下載',
+    preparingBackup: '正在準備備份...',
+    creatingArchive: '正在建立備份歸檔...對於大型歸檔可能需要一些時間。',
+    downloadingFile: '正在下載備份檔案...',
+    backupDownloaded: '備份下載成功',
+    failedToCreateBackup: '建立備份失敗:{{message}}',
+    restore: '恢復',
+    restoreReplacesAll: '恢復將替換所有資料。',
+    restoreReplacesAllDetail: '您目前的資料庫和檔案將被完全替換。恢復後需要重新啟動。',
+    restoreConfirmTitle: '恢復備份',
+    restoreConfirmMessage: '您確定要從"{{filename}}"恢復嗎?這將完全替換您目前的資料庫和所有檔案。恢復後需要重新啟動應用程式。',
+    restoreConfirmButton: '恢復備份',
+    uploadingFile: '正在上傳備份檔案...',
+    backupRestoredRestart: '備份已恢復。請重新啟動 Bambuddy。',
+    failedToRestore: '恢復備份失敗。請檢查檔案格式。',
+    reloadNow: '立即重新載入',
+    creatingBackup: '正在建立備份',
+    restoringBackup: '正在恢復備份',
+    preparing: '準備中...',
+    processing: '處理中...',
+    doNotClosePage: '請不要關閉此頁面或離開頁面。對於大型備份,此操作可能需要幾分鐘。',
+
+    // RestoreModal
+    restoring: '恢復中...',
+    restoreComplete: '恢復完成',
+    restoreFailed2: '恢復失敗',
+    importSettings: '從備份檔案匯入設定',
+    pleaseWaitRestoring: '請等待資料恢復中',
+    selectBackupFile: '點選選擇備份檔案(.json 或 .zip)',
+    duplicateHandling: '重複項處理方式:',
+    matchPrinters: '印表機',
+    matchPrintersBy: '按序列號匹配',
+    matchSmartPlugs: '智慧插座',
+    matchSmartPlugsBy: '按 IP 位址匹配',
+    matchNotificationProviders: '通知提供者',
+    matchNotificationProvidersBy: '按名稱匹配',
+    matchFilaments: '耗材',
+    matchFilamentsBy: '按名稱 + 類型 + 品牌匹配',
+    matchArchives: '檔案',
+    matchArchivesBy: '按內容雜湊匹配(始終跳過)',
+    matchPendingUploads: '待上傳',
+    matchPendingUploadsBy: '按檔名匹配',
+    matchSettingsTemplates: '設定和範本',
+    matchSettingsTemplatesBy: '始終覆蓋',
+    replaceExisting: '替換現有資料',
+    keepExisting: '保留現有資料',
+    overwriteDescription: '用備份資料覆蓋已存在的項目',
+    keepDescription: '僅恢復尚不存在的項目',
+    overwriteCaution: '注意:',
+    overwriteWarning: '覆蓋將用備份資料替換您目前的設定。出於安全考慮,印表機存取碼永遠不會被覆蓋。',
+    cancel: '取消',
+    processingBackup: '正在處理備份檔案...',
+    itemsRestored: '已恢復項目',
+    itemsSkipped: '已跳過項目',
+    restored: '已恢復',
+    skippedAlreadyExist: '已跳過(已存在)',
+    filesCategory: '檔案(3MF、縮圖等)',
+    andMore: '...還有 {{count}} 項',
+    newApiKeysGenerated: '已產生新的 API 金鑰',
+    keysShownOnce: '這些金鑰僅顯示一次。請立即複製!',
+    copy: '複製',
+    noDataFound: '在備份檔案中未找到可恢復的資料。',
+    close: '關閉',
+
+    // Scheduled local backups (#884)
+    scheduledBackup: '排程備份',
+    scheduledBackupDescription: '依排程自動建立備份快照。輸出目錄可掛載到 NAS 或外部儲存。',
+    frequency: '頻率',
+    backupTime: '時間',
+    retention: '保留',
+    retentionDescription: '保留的備份數量',
+    outputPath: '輸出路徑',
+    outputPathPlaceholder: '預設:{{path}}',
+    outputPathDescription: '留空以使用預設位置',
+    runNow: '立即執行',
+    backupFiles: '備份檔案',
+    noScheduledBackups: '尚無備份',
+    deleteBackup: '刪除',
+    deleteBackupConfirm: '要刪除此備份檔案嗎?',
+    backupRunning: '備份進行中…',
+    scheduledBackupComplete: '備份已成功完成',
+    scheduledBackupFailed: '備份失敗',
+    nextBackup: '下次備份',
+    backupSize: '大小',
+    utc: 'UTC',
+    defaultPathLabel: '預設:',
+
+    // Category labels
+    categories: {
+      settings: '設定',
+      notification_providers: '通知提供者',
+      notification_templates: '通知範本',
+      smart_plugs: '智慧插座',
+      printers: '印表機',
+      filaments: '耗材',
+      maintenance_types: '維護類型',
+      archives: '檔案',
+      projects: '專案',
+      pending_uploads: '待上傳',
+      external_links: '外部連結',
+      api_keys: 'API 金鑰',
+    },
+  },
+
+  // Tags
+  tags: {
+    title: '標籤',
+    addTag: '新增標籤',
+    editTag: '編輯標籤',
+    deleteTag: '刪除標籤',
+    tagName: '標籤名稱',
+    tagColor: '標籤顏色',
+    noTags: '無標籤',
+    deleteConfirm: '確定要刪除此標籤嗎?',
+    manageTags: '管理標籤',
+  },
+
+  // Upload modal (archives)
+  uploadModal: {
+    title: '上傳 3MF 檔案',
+    dragDrop: '將 .3mf 檔案拖放到此處',
+    or: '或',
+    browseFiles: '瀏覽檔案',
+    extractionInfo: '將從 3MF 檔案中繼資料中自動提取印表機型號。',
+    uploaded: '已上傳',
+    failed: '失敗',
+    uploading: '上傳中...',
+    upload: '上傳',
+    uploadFailed: '上傳失敗',
+  },
+
+  // Edit archive modal
+  // Edit Archive Modal
+  editArchive: {
+    title: '編輯歸檔',
+    name: '名稱',
+    namePlaceholder: '列印名稱',
+    printer: '印表機',
+    noPrinter: '無印表機',
+    project: '專案',
+    noProject: '無專案',
+    itemsPrinted: '列印數量',
+    itemsPrintedHelp: '此列印任務中生產的物品數量',
+    notes: '備註',
+    notesPlaceholder: '新增關於此列印的備註...',
+    externalLink: '外部連結',
+    externalLinkPlaceholder: 'https://printables.com/model/...',
+    externalLinkHelp: '連結到 Printables、Thingiverse 或其他來源',
+    tags: '標籤',
+    tagsPlaceholder: '新增標籤...',
+    addMoreTags: '新增更多標籤...',
+    matchingTags: '匹配"{{query}}"',
+    existingTags: '現有標籤',
+    clickToAdd: '(點選新增)',
+    status: '狀態',
+    failureReason: '失敗原因',
+    selectReason: '選擇原因...',
+    photos: '列印成品照片',
+    photosHelp: '點選 + 新增列印成品照片',
+    printResult: '列印成品',
+    saving: '儲存中...',
+    // Failure reasons
+    failureReasons: {
+      adhesionFailure: '附著力失敗',
+      spaghettiDetached: '拉絲 / 脫落',
+      layerShift: '層偏移',
+      cloggedNozzle: '噴嘴堵塞',
+      filamentRunout: '耗材用完',
+      warping: '翹曲',
+      stringing: '拉絲',
+      underExtrusion: '擠出不足',
+      powerFailure: '斷電',
+      userCancelled: '使用者取消',
+      other: '其他',
+    },
+    // Archive statuses
+    statuses: {
+      completed: '已完成',
+      failed: '失敗',
+      aborted: '已取消',
+      printing: '列印中',
+    },
+  },
+
+  // K-Profiles
+  kProfiles: {
+    title: 'K 值設定',
+    noPrintersConfigured: '未設定印表機',
+    addPrinterInSettings: '在設定中新增印表機以管理 K 值設定',
+    noActivePrinters: '無活躍印表機',
+    enablePrinterConnection: '啟用印表機連線以檢視其 K 值設定',
+    loadingProfiles: '載入 K 值設定中...',
+    printerOffline: '印表機離線',
+    printerOfflineDesc: '所選印表機未連線。開啟電源以檢視 K 值設定。',
+    noMatchingProfiles: '無匹配的設定',
+    noMatchingProfilesDesc: '沒有設定匹配您的搜尋條件',
+    noKProfiles: '無 K 值設定',
+    noKProfilesDesc: '未找到 {{diameter}}mm 噴嘴的壓力推進設定',
+    createFirstProfile: '建立第一個設定',
+    // Controls
+    printer: '印表機',
+    nozzle: '噴嘴',
+    refresh: '重新整理',
+    addProfile: '新增設定',
+    export: '匯出',
+    import: '匯入',
+    select: '選擇',
+    selectAll: '全選',
+    delete: '刪除',
+    // Filters
+    searchPlaceholder: '按名稱或耗材搜尋...',
+    allExtruders: '所有擠出機',
+    leftOnly: '僅左側',
+    rightOnly: '僅右側',
+    allFlow: '所有流量',
+    hfOnly: '僅高流量',
+    sOnly: '僅標準',
+    sortName: '排序:名稱',
+    sortKValue: '排序:K 值',
+    sortFilament: '排序:耗材',
+    // Dual extruder labels
+    leftExtruder: '左擠出機',
+    rightExtruder: '右擠出機',
+    // Modal
+    modal: {
+      addTitle: '新增 K 值設定',
+      editTitle: '編輯 K 值設定',
+      profileName: '設定名稱',
+      profileNamePlaceholder: '我的 PLA 設定',
+      kValue: 'K 值',
+      kValuePlaceholder: '0.020',
+      kValueHelp: '典型範圍:PLA 0.01 - 0.06,PETG 0.02 - 0.10',
+      filament: '耗材',
+      selectFilament: '選擇耗材...',
+      noFilamentsHelp: '未找到耗材。請先在 Bambu Studio 中建立 K 值設定。',
+      flowType: '流量類型',
+      highFlow: '高流量',
+      standard: '標準',
+      nozzleSize: '噴嘴尺寸',
+      extruder: '擠出機',
+      extruders: '擠出機',
+      left: '左',
+      right: '右',
+      notes: '備註(本機儲存)',
+      notesPlaceholder: '新增關於此設定的備註...',
+      notesHelp: '備註儲存在 Bambuddy 中,不在印表機上',
+      syncing: '與印表機同步中...',
+      savingExtruder: '儲存到擠出機 {{current}}/{{total}}...',
+      pleaseWait: '請稍候',
+    },
+    // Delete confirmation
+    deleteConfirm: {
+      title: '刪除設定',
+      cannotUndo: '此操作無法復原',
+      message: '確定要從印表機刪除"{{name}}"嗎?',
+    },
+    // Bulk delete
+    bulkDelete: {
+      title: '刪除設定',
+      cannotUndo: '此操作無法復原',
+      message: '確定要從印表機刪除 {{count}} 個選中的設定嗎?',
+    },
+    // Toast
+    toast: {
+      profileSaved: 'K 值設定已儲存',
+      profilesSaved: 'K 值設定已儲存到 {{count}} 個擠出機',
+      selectAtLeastOneExtruder: '請至少選擇一個擠出機',
+      profileDeleted: 'K 值設定已刪除',
+      profilesDeleted: '已刪除 {{count}} 個設定',
+      exportedProfiles: '已匯出 {{count}} 個設定',
+      importedProfiles: '已匯入 {{count}} / {{total}} 個設定',
+      noProfilesToExport: '無可匯出的設定',
+      invalidFileFormat: '無效的檔案格式',
+      failedToParseImport: '解析匯入檔案失敗',
+      failedToSaveBatch: '批次儲存 K 值設定失敗',
+      noteSaved: '備註已儲存',
+      failedToSaveNote: '儲存備註失敗',
+    },
+    // Permissions
+    permission: {
+      noRead: '您沒有重新整理設定的權限',
+      noCreate: '您沒有新增設定的權限',
+      noUpdate: '您沒有更新 K 值設定的權限',
+      noDelete: '您沒有刪除 K 值設定的權限',
+      noExport: '您沒有匯出設定的權限',
+      noImport: '您沒有匯入設定的權限',
+    },
+  },
+
+  // Virtual Printer
+  virtualPrinter: {
+    title: '虛擬印表機',
+    running: '執行中',
+    stopped: '已停止',
+    description: {
+      default: '啟用虛擬印表機,使其在 Bambu Studio 和 OrcaSlicer 中可見。傳送到此印表機的檔案將直接歸檔而不列印。',
+      proxy: '啟用代理,將切片軟體流量中繼到真實印表機,允許在任何網路上遠端列印。',
+    },
+    enable: {
+      title: '啟用虛擬印表機',
+      visibleInSlicer: '在切片軟體發現中顯示為"Bambuddy"',
+      proxyingTo: '代理到 {{name}}',
+      notActive: '未啟用',
+    },
+    model: {
+      title: '印表機型號',
+      description: '選擇要模擬的印表機型號。',
+      restartWarning: '更改型號將重新啟動虛擬印表機',
+    },
+    accessCode: {
+      title: '存取碼',
+      isSet: '存取碼已設定',
+      notSet: '未設定存取碼 - 需要設定才能啟用',
+      placeholder: '輸入 8 位字元程式碼',
+      placeholderChange: '輸入新程式碼以更改',
+      hint: '必須恰好 8 個字元。切片軟體使用此程式碼進行認證。',
+      charCount: '({{count}}/8)',
+    },
+    targetPrinter: {
+      title: '目標印表機',
+      configured: '代理目標已設定',
+      notConfigured: '未選擇目標印表機 - 代理模式需要設定',
+      placeholder: '選擇印表機...',
+      hint: '選擇要將切片軟體流量代理到的印表機。印表機必須處於區域網路模式。',
+      noPrinters: '未設定印表機。請先新增印表機以使用代理模式。',
+    },
+    remoteInterface: {
+      title: '網路介面覆蓋',
+      configured: '介面覆蓋已啟用',
+      optional: '可選 - 當自動檢測的 IP 不正確時使用(例如多網路卡、Docker、VPN)',
+      placeholder: '自動檢測(預設)...',
+      hint: '覆蓋透過 SSDP 廣播並在 TLS 憑證中使用的 IP 位址。在 Bambuddy 有多個網路介面時很有用。',
+    },
+    mode: {
+      title: '模式',
+      archive: '歸檔',
+      archiveDesc: '立即歸檔檔案',
+      review: '審核',
+      reviewDesc: '歸檔前審核',
+      queue: '佇列',
+      queueDesc: '歸檔並新增到佇列',
+      proxy: '代理',
+      proxyDesc: '中繼到真實印表機',
+    },
+    autoDispatch: {
+      title: '自動派發',
+      description: '新增到佇列時自動開始列印。關閉後,列印任務等待手動派發。',
+    },
+    setupRequired: {
+      title: '需要設定',
+      description: '虛擬印表機功能需要額外的系統設定才能工作。包括埠轉發、防火牆規則和平臺特定設定。',
+      readGuide: '啟用前請閱讀設定指南',
+    },
+    howItWorks: {
+      title: '工作原理',
+      step1: '在同一區域網路中,虛擬印表機會透過發現機制自動出現在您的切片軟體(Bambu Studio / OrcaSlicer)中。從其他網路,透過 IP 位址和存取碼手動新增。',
+      step2: '在歸檔、審核和佇列模式下,使用切片軟體中的"傳送"按鈕將 3MF 檔案上傳到 Bambuddy。切片軟體會顯示"列印成功"— 檔案已儲存,未列印。',
+      step3: '在代理模式下,虛擬印表機將所有流量中繼到真實印表機 — 列印會立即開始,就像直接連線一樣。',
+    },
+    status: {
+      title: '狀態詳情',
+      printerName: '印表機名稱',
+      model: '型號',
+      serialNumber: '序列號',
+      mode: '模式',
+      pendingFiles: '待處理檔案',
+      targetPrinter: '目標印表機',
+      ftpPort: 'FTP 連接埠',
+      mqttPort: 'MQTT 連接埠',
+      ftpConnections: 'FTP 連線',
+      mqttConnections: 'MQTT 連線',
+    },
+    toast: {
+      updated: '虛擬印表機設定已更新',
+      failedToUpdate: '更新設定失敗',
+      accessCodeRequired: '請先設定存取碼',
+      targetPrinterRequired: '請先選擇目標印表機',
+      bindIpRequired: '請先設定繫結 IP',
+      accessCodeEmpty: '存取碼不能為空',
+      accessCodeLength: '存取碼必須恰好 8 個字元',
+      created: '虛擬印表機已建立',
+      failedToCreate: '建立虛擬印表機失敗',
+      deleted: '虛擬印表機已刪除',
+      failedToDelete: '刪除虛擬印表機失敗',
+    },
+    list: {
+      title: '虛擬印表機',
+      add: '新增',
+      addFirst: '新增虛擬印表機',
+      empty: '未設定虛擬印表機。新增一個以開始使用。',
+    },
+    bindIp: {
+      title: '繫結介面',
+      placeholder: '選擇介面...',
+      hint: '此虛擬印表機繫結的網路介面。每臺印表機必須唯一。',
+    },
+    proxy: {
+      accessCodeHint: '在代理模式下,在切片軟體中使用目標印表機的存取碼。連線會透明轉發到真實印表機。',
+    },
+    addDialog: {
+      title: '新增虛擬印表機',
+      name: '名稱',
+      hint: '建立後可以設定存取碼、目標印表機和其他設定。',
+      create: '建立',
+    },
+    deleteConfirm: {
+      title: '刪除虛擬印表機',
+      message: '確定要刪除"{{name}}"嗎?這將停止此印表機的所有服務。',
+    },
+  },
+
+  // Model Viewer
+  modelViewer: {
+    openInSlicer: '在切片軟體中開啟',
+    tabs: {
+      model: '3D 模型',
+      gcode: 'G-code 預覽',
+    },
+    notAvailable: '不可用',
+    notSliced: '未切片',
+    plates: '板',
+    allPlates: '所有板',
+    plateNumber: '板 {{number}}',
+    plateCount: '{{count}} 個板',
+    plateCount_other: '{{count}} 個板',
+    objectCount: '{{count}} 個物件',
+    objectCount_other: '{{count}} 個物件',
+    filamentCount: '{{count}} 種耗材',
+    filamentCount_other: '{{count}} 種耗材',
+    eta: '預計 {{minutes}} 分鐘',
+    noPreview: '此檔案無可用預覽',
+    pagination: {
+      pageOf: '第 {{current}} / {{total}} 頁',
+      prev: '上一頁',
+      next: '下一頁',
+    },
+    errors: {
+      failedToLoad: '載入檔案失敗',
+      noMeshes: '3MF 檔案中未找到網格',
+      unsupportedFormat: '不支援的檔案格式',
+    },
+  },
+
+  // Maintenance type descriptions (built-in)
+  maintenanceDescriptions: {
+    lubricateCarbonRods: '在碳纖維杆上塗抹潤滑劑以確保順暢運動',
+    lubricateRails: '在線性導軌上塗抹潤滑劑以確保順暢運動',
+    cleanNozzle: '清潔熱端和噴嘴以防止堵塞',
+    checkBelts: '檢查皮帶張力以確保列印精度',
+    cleanBuildPlate: '清潔列印板以獲得更好的附著力',
+    checkExtruder: '檢查擠出機齒輪磨損情況',
+    checkCooling: '確保冷卻風扇正常工作',
+    generalInspection: '印表機綜合檢查',
+    cleanCarbonRods: '清潔碳纖維杆以減少摩擦',
+    lubricateSteelRods: '在鋼杆上塗抹潤滑劑以確保順暢運動',
+    cleanSteelRods: '清潔鋼杆以減少摩擦',
+    cleanLinearRails: '擦拭線性導軌以清除灰塵和碎屑',
+    checkPtfeTube: '檢查 PTFE 管的磨損或損壞',
+    replaceHepaFilter: '更換 HEPA 過濾器以保證空氣品質',
+    replaceCarbonFilter: '更換活性炭過濾器',
+    lubricateLeftNozzleRail: '潤滑左噴嘴導軌(H2 系列)',
+  },
+
+  // Smart Plugs
+  smartPlugs: {
+    offline: '離線',
+    admin: '管理',
+    openPlugAdminPage: '開啟插座管理頁面',
+    deleteSmartPlug: '刪除智慧插座',
+    turnOnSmartPlug: '開啟智慧插座',
+    turnOffSmartPlug: '關閉智慧插座',
+    turnOn: '開啟',
+    turnOff: '關閉',
+    addSmartPlug: {
+      scanningNetwork: '掃描網路中...',
+      chooseEntity: '選擇實體...',
+      connectionFailed: '連線失敗',
+      searchEntities: '搜尋實體...',
+      searchPowerSensors: '搜尋功率感測器...',
+      searchEnergySensors: '搜尋能量感測器...',
+      placeholders: {
+        plugName: '客廳插座',
+        mqttStateOnValue: 'ON、true、1',
+        mqttSameAsPower: '與功率主題相同,或不同',
+      },
+    },
+    // SmartPlugCard
+    linkedTo: '連結到:',
+    monitorOnly: '僅監控',
+    alerts: '警報',
+    scheduleOn: '開啟 {{time}}',
+    scheduleOff: '關閉 {{time}}',
+    on: '開啟',
+    off: '關閉',
+    power: '功率',
+    kwhToday: '今日kWh',
+    settings: '設定',
+    automationSettings: '自動化設定',
+    showInSwitchbar: '在開關欄顯示',
+    quickAccessSidebar: '從側邊欄快速存取',
+    enabled: '已啟用',
+    enableAutomation: '為此插座啟用自動化',
+    autoOn: '自動開啟',
+    autoOnDescription: '列印開始時開啟',
+    autoOff: '自動關閉',
+    autoOffDescription: '列印完成時關閉(一次性)',
+    autoOffPersistent: '保持啟用',
+    autoOffPersistentDescription: '在列印之間保持啟用而非一次性',
+    turnOffDelayMode: '關閉延遲模式',
+    time: '時間',
+    temp: '溫度',
+    delayMinutes: '延遲(分鐘)',
+    tempThreshold: '溫度閾值(°C)',
+    tempThresholdDescription: '當噴嘴冷卻到此溫度以下時關閉',
+    edit: '編輯',
+    deleteConfirm: '確定要刪除"{{name}}"嗎?此操作無法復原。',
+    turnOnConfirm: '確定要開啟"{{name}}"嗎?',
+    turnOffConfirm: '確定要關閉"{{name}}"嗎?這將切斷連線裝置的電源。',
+    failedToTurn: '無法{{action}}"{{name}}"',
+    unknown: '未知',
+    // AddSmartPlugModal
+    addTitle: '新增智慧插座',
+    editTitle: '編輯智慧插座',
+    stopScanning: '停止掃描',
+    discoverTasmota: '發現Tasmota裝置',
+    foundDevices: '找到{{count}}個裝置 - 點選選擇:',
+    noDevicesFound: '未在您的網路中找到Tasmota裝置',
+    haNotConfigured: 'Home Assistant未設定。請在以下位置設定',
+    haSettingsPath: '設定 → 網路 → Home Assistant',
+    selectEntity: '選擇實體 *',
+    ipAddress: 'IP 位址 *',
+    nameLabel: '名稱 *',
+    username: '使用者名稱',
+    password: '密碼',
+    authHint: '如果您的Tasmota裝置不需要認證,請留空',
+    linkToPrinter: '連結印表機',
+    noPrinter: '無印表機(僅手動控制)',
+    linkingDescription: '連結後可在列印開始/完成時自動開關',
+    powerAlerts: '功率警報',
+    alertAbove: '高於時警報(W)',
+    alertBelow: '低於時警報(W)',
+    alertDescription: '當電力消耗超過這些閾值時收到通知。留空以停用該方向。',
+    dailySchedule: '每日計畫',
+    turnOnAt: '開啟時間',
+    turnOffAt: '關閉時間',
+    scheduleDescription: '每天在這些時間自動開關插座。留空以跳過該操作。',
+    showOnPrinterCard: '在印表機卡片上顯示',
+    displayOnPrinterCard: '在印表機卡片上顯示按鈕',
+    connectedResult: '已連線!',
+    deviceLabel: '裝置:{{name}} - ',
+    stateLabel: '狀態:{{state}}',
+    test: '測試',
+    delete: '刪除',
+    save: '儲存',
+    add: '新增',
+    cancel: '取消',
+    failedToStartScan: '無法開始掃描',
+    nameRequired: '名稱為必填項',
+    entityRequired: 'Home Assistant插座需要實體',
+    mqttTopicRequired: '必須為功率、能源或狀態監控設定至少一個MQTT主題',
+    loadingEntities: '正在載入實體...',
+    loading: '載入中...',
+    failedToLoadEntities: '載入實體失敗:{{error}}',
+    noEntitiesMatching: '未找到匹配"{{search}}"的實體',
+    noEntitiesAvailable: '無可用實體',
+    searchingEntities: '搜尋所有實體(找到{{count}}個)',
+    showingEntities: '顯示 switch、light、input_boolean({{count}}個可用)',
+    energyMonitoringOptional: '能源監控(可選)',
+    energyMonitoringHint: '搜尋並選擇提供功率/能源資料的感測器。',
+    powerSensorW: '功率感測器(W)',
+    energyTodayKwh: '今日能源(kWh)',
+    totalEnergyKwh: '總能源(kWh)',
+    noMatchingSensors: '無匹配的感測器',
+    none: '無',
+    mqttNotConfigured: 'MQTT代理未設定。請在以下位置設定代理地址',
+    mqttSettingsPath: '設定 → 網路 → MQTT發布',
+    mqttNotConfiguredSuffix: '(您不需要啟用發布,只需填寫代理詳細資訊)。',
+    mqttMonitorOnlyDescription: 'MQTT插座透過MQTT訂閱接收功率/能源資料。開關控制不可用 - 請使用您的MQTT代理或家庭自動化系統。',
+    powerMonitoring: '功率監控',
+    energyMonitoring: '能源監控',
+    stateMonitoring: '狀態監控',
+    optional: '可選',
+    topic: '主題',
+    jsonPath: 'JSON路徑',
+    multiplier: '乘數',
+    onValue: 'ON值',
+    mqttPowerHint: 'JSON路徑從JSON負載中提取值(例如"power_l1")。如果主題發布原始數值,請留空。\n乘數:mW→W使用0.001,kW→W使用1000。',
+    mqttEnergyHint: 'JSON路徑從JSON負載中提取值。原始值請留空。\n乘數:Wh→kWh使用0.001,MWh→kWh使用1000。',
+    mqttStateHint: 'JSON路徑從JSON負載中提取值。原始值請留空。\nON值:表示"ON"的確切字串。留空以自動檢測(ON、true、1)。',
+    // REST smart plug
+    restControl: '控制',
+    restOnUrl: '開啟 URL',
+    restOffUrl: '關閉 URL',
+    restOnBody: '開啟請求內容',
+    restOffBody: '關閉請求內容',
+    restMethod: 'HTTP 方法',
+    restHeaders: '自訂標頭(JSON)',
+    restStatusUrl: '狀態 URL',
+    restStatusPath: '狀態 JSON 路徑',
+    restStatusOnValue: 'ON 值',
+    restPowerUrl: '功率URL',
+    restPowerPath: '功率 JSON 路徑',
+    restPowerMultiplier: '功率乘數',
+    restEnergyUrl: '能耗URL',
+    restEnergyPath: '能耗 JSON 路徑',
+    restEnergyMultiplier: '能耗乘數',
+    restUrlRequired: 'REST 插座至少需要一個 URL(ON 或 OFF)',
+    restHeadersHint: '例如:{"Authorization": "Bearer your-token"}',
+    restBodyHint: '例如:ON、{"state": "on"}',
+    restStatusHint: '用於輪詢目前狀態的 URL',
+    restPathHint: '例如:state 或 data.power.status',
+    restPowerUrlHint: '功率資料的獨立URL(留空則使用狀態URL)',
+    restEnergyUrlHint: '能耗資料的獨立URL(留空則使用狀態URL)',
+    restEnergyHint: '每個值可以使用獨立的URL,或回退到狀態 URL。使用乘數進行單位轉換(例如:0.001 將 Wh 轉換為 kWh)。',
+    testConnection: '測試連線',
+    connectionSuccess: '連線成功',
+    noSwitchesInSwitchbar: '開關欄中沒有開關',
+    enableSwitchbarHint: '在設定 > 智慧插座中啟用"在開關欄顯示"',
+  },
+
+  // Notifications
+  notifications: {
+    // Provider types
+    providerTypes: {
+      callmebot: 'CallMeBot/WhatsApp',
+      ntfy: 'ntfy',
+      pushover: 'Pushover',
+      telegram: 'Telegram',
+      email: '電子郵件',
+      discord: 'Discord',
+      webhook: 'Webhook',
+      homeassistant: 'Home Assistant',
+    },
+    // Provider descriptions
+    providerDescriptions: {
+      email: 'SMTP 電子郵件通知',
+      telegram: '透過 Telegram 機器人傳送通知',
+      discord: '透過 Webhook 傳送到 Discord 頻道',
+      ntfy: '免費、可自託管的推送通知',
+      pushover: '簡單、可靠的推送通知',
+      callmebot: '透過 CallMeBot 免費傳送 WhatsApp 通知',
+      webhook: '通用 HTTP POST 到任意 URL',
+      homeassistant: 'Home Assistant 儀表板中的持久通知',
+    },
+    // NotificationProviderCard
+    lastSuccess: '上次:{{date}}',
+    error: '錯誤',
+    printer: '印表機:',
+    allPrinters: '所有印表機',
+    sendTestNotification: '傳送測試通知',
+    eventSettings: '事件設定',
+    enabled: '已啟用',
+    sendFromProvider: '從此提供者傳送通知',
+    // Event categories
+    printEvents: '列印事件',
+    printerStatus: '印表機狀態',
+    amsAlarms: 'AMS 警報',
+    amsHtAlarms: 'AMS-HT 警報',
+    printQueue: '列印佇列',
+    // Event tags (badges)
+    start: '開始',
+    plateCheck: '熱床檢測',
+    complete: '完成',
+    failed: '失敗',
+    stopped: '已停止',
+    progress: '進度',
+    offline: '離線',
+    lowFilament: '耗材不足',
+    maintenance: '維護',
+    amsHumidity: 'AMS 濕度',
+    amsTemp: 'AMS 溫度',
+    amsHtHumidity: 'AMS-HT 濕度',
+    amsHtTemp: 'AMS-HT 溫度',
+    bedCooled: '熱床已冷卻',
+    firstLayer: '首層完成',
+    quiet: '免打擾',
+    digest: '摘要 {{time}}',
+    // Event labels (expanded settings)
+    printStarted: '列印已開始',
+    plateNotEmpty: '熱床非空',
+    plateNotEmptyDescription: '列印前偵測到物體',
+    printCompleted: '列印已完成',
+    bedCooledLabel: '熱床已冷卻',
+    bedCooledDescription: '列印後熱床溫度降至閾值以下',
+    firstLayerCompleteLabel: '首層列印完成',
+    firstLayerCompleteDescription: '首層完成時傳送帶照片的通知',
+    missingSpoolAssignmentLabel: '缺少料卷分配',
+    missingSpoolAssignmentDescription: '當列印開始且所需料盤沒有分配料卷時傳送通知',
+    printFailed: '列印失敗',
+    printStopped: '列印已停止',
+    progressMilestones: '進度里程碑',
+    progressMilestonesDescription: '在 25%、50%、75% 時通知',
+    printerOffline: '印表機離線',
+    printerError: '印表機錯誤',
+    lowFilamentLabel: '耗材不足',
+    maintenanceDue: '需要維護',
+    maintenanceDueDescription: '需要維護時通知',
+    amsHumidityHigh: 'AMS 濕度過高',
+    amsHumidityHighDescription: '普通 AMS 濕度超過閾值',
+    amsTemperatureHigh: 'AMS 溫度過高',
+    amsTemperatureHighDescription: '普通 AMS 溫度超過閾值',
+    amsHtHumidityHigh: 'AMS-HT 濕度過高',
+    amsHtHumidityHighDescription: 'AMS-HT 濕度超過閾值',
+    amsHtTemperatureHigh: 'AMS-HT 溫度過高',
+    amsHtTemperatureHighDescription: 'AMS-HT 溫度超過閾值',
+    // Queue events
+    jobAdded: '任務已新增',
+    jobAddedDescription: '任務已新增到佇列',
+    jobAssigned: '任務已分配',
+    jobAssignedDescription: '基於模型的任務已分配給印表機',
+    jobStarted: '任務已開始',
+    jobStartedDescription: '佇列任務已開始列印',
+    jobWaiting: '任務等待中',
+    jobWaitingDescription: '任務正在等待耗材或印表機',
+    jobSkipped: '任務已跳過',
+    jobSkippedDescription: '任務已跳過(上一個失敗)',
+    jobFailed: '任務失敗',
+    jobFailedDescription: '任務啟動失敗',
+    queueComplete: '佇列已完成',
+    queueCompleteDescription: '所有佇列任務已完成',
+    // Quiet hours
+    quietHours: '免打擾時段',
+    noNotificationsDuring: '在此時段內不傳送通知',
+    editProviderToChangeQuietHours: '編輯提供者以更改免打擾時段',
+    // Daily digest
+    dailyDigest: '每日摘要',
+    batchNotifications: '將通知彙總為每日摘要',
+    sendAt: '傳送於 {{time}}',
+    editProviderToChangeDigestTime: '編輯提供者以更改摘要時間',
+    // Actions
+    edit: '編輯',
+    deleteProvider: '刪除通知提供者',
+    deleteConfirm: '確定要刪除"{{name}}"嗎?此操作無法復原。',
+    delete: '刪除',
+    // AddNotificationModal
+    addTitle: '新增通知提供者',
+    editTitle: '編輯通知提供者',
+    nameLabel: '名稱 *',
+    namePlaceholder: '我的通知',
+    providerTypeLabel: '提供者類型 *',
+    configuration: '設定',
+    testConfiguration: '測試設定',
+    printerFilter: '印表機篩選',
+    onlyFromPrinter: '僅傳送來自此印表機的事件通知',
+    quietHoursDnd: '免打擾時段',
+    quietStart: '開始',
+    quietEnd: '結束',
+    dailyDigestLabel: '每日摘要',
+    sendDigestAt: '傳送摘要於',
+    digestCollected: '事件將被收集並在此時間作為單條摘要傳送',
+    notificationEvents: '通知事件',
+    progressPercent: '(25%、50%、75%)',
+    bedCooledAfterPrint: '(列印完成後)',
+    cancel: '取消',
+    save: '儲存',
+    add: '新增',
+    nameRequired: '名稱為必填項',
+    fieldRequired: '{{field}}為必填項',
+    // Config field labels
+    phoneNumber: '電話號碼',
+    apiKey: 'API 金鑰',
+    serverUrl: '伺服器 URL',
+    topic: '主題',
+    authToken: '認證權杖',
+    userKey: '使用者金鑰',
+    appToken: '應用程式權杖',
+    priority: '優先順序',
+    botToken: '機器人權杖',
+    chatId: '聊天 ID',
+    smtpServer: 'SMTP 伺服器',
+    smtpPort: 'SMTP 連接埠',
+    security: '安全',
+    authentication: '認證',
+    username: '使用者名稱',
+    password: '密碼',
+    fromEmail: '寄件人信箱',
+    toEmail: '收件人信箱',
+    webhookUrl: 'Webhook URL',
+    payloadFormat: '負載格式',
+    authorization: '授權',
+    titleFieldName: '標題欄位名',
+    messageFieldName: '訊息欄位名',
+    // NotificationTemplateEditor
+    editTemplate: '編輯範本:{{name}}',
+    titleLabel: '標題',
+    bodyLabel: '正文',
+    titlePlaceholder: '通知標題...',
+    bodyPlaceholder: '通知正文...',
+    availableVariables: '可用變數',
+    clickToInsert: '點選插入到正文游標位置',
+    livePreview: '即時預覽',
+    hide: '隱藏',
+    show: '顯示',
+    loadingPreview: '載入預覽中...',
+    enterTemplateContent: '輸入範本內容以檢視預覽',
+    titlePreview: '標題:',
+    bodyPreview: '正文:',
+    resetToDefault: '恢復預設',
+    titleRequired: '標題為必填項',
+    bodyRequired: '正文為必填項',
+    // NotificationLogViewer
+    notificationLog: '通知日誌',
+    showFailedOnly: '僅顯示失敗',
+    last24Hours: '最近 24 小時',
+    last7Days: '最近 7 天',
+    last30Days: '最近 30 天',
+    last90Days: '最近 90 天',
+    justNow: '剛剛',
+    noFailedNotifications: '沒有失敗的通知',
+    noNotificationsLogged: '沒有通知紀錄',
+    unknownProvider: '未知提供者',
+    logTitle: '標題',
+    logMessage: '訊息',
+    logError: '錯誤',
+    logProvider: '提供者:{{type}}',
+    logTime: '時間:{{time}}',
+    refresh: '重新整理',
+    clearOld: '清除舊紀錄',
+    statsSummary: '最近 {{days}} 天:',
+    statsNotifications: '條通知',
+    statsSent: '{{count}} 條已傳送',
+    statsFailed: '{{count}} 條失敗',
+    // Event type labels (for log viewer)
+    eventTypes: {
+      print_start: '列印已開始',
+      print_complete: '列印完成',
+      print_failed: '列印失敗',
+      print_stopped: '列印已停止',
+      print_progress: '進度',
+      printer_offline: '印表機離線',
+      printer_error: '印表機錯誤',
+      filament_low: '耗材不足',
+      maintenance_due: '需要維護',
+      test: '測試',
+    },
+    userEmail: {
+      title: '通知',
+      emailNotifications: '郵件通知',
+      emailNotificationsDesc: '接收您自己列印任務的郵件通知。郵件將透過進階身份驗證中設定的 SMTP 設定傳送。',
+      sendingTo: '通知將傳送至',
+      noEmailWarning: '您的帳戶沒有郵件地址。請聯絡管理員新增。',
+      printJobNotifications: '列印任務通知',
+      printJobNotificationsDesc: '選擇哪些事件會觸發您提交的列印任務的郵件通知。',
+      printJobStarts: '列印任務開始',
+      printJobStartsDesc: '當您的列印任務開始時收到通知。',
+      printJobFinishes: '列印任務完成',
+      printJobFinishesDesc: '當您的列印任務成功完成時收到通知。',
+      printErrors: '列印錯誤',
+      printErrorsDesc: '當您的列印任務失敗或遇到錯誤時收到通知。',
+      printJobStops: '列印任務停止',
+      printJobStopsDesc: '當您的列印任務被取消或停止時收到通知。',
+      saveSuccess: '通知偏好設定已儲存。',
+      saveError: '儲存通知偏好設定失敗。',
+    },
+  },
+
+  // Rich Text Editor
+  richTextEditor: {
+    bold: '粗體',
+    italic: '斜體',
+    underline: '底線',
+    bulletList: '無序列表',
+    numberedList: '有序列表',
+    alignLeft: '左對齊',
+    alignCenter: '居中對齊',
+    alignRight: '右對齊',
+    addLink: '新增連結',
+    removeLink: '移除連結',
+  },
+
+  // External Links
+  externalLinks: {
+    noLinksConfigured: '未設定外部連結',
+    deleteLink: '刪除連結',
+    removeCustomIcon: '移除自訂圖示',
+    openInNewTab: '在新標籤頁中開啟',
+    placeholders: {
+      linkName: '我的連結',
+    },
+  },
+
+  // Keyboard Shortcuts Modal
+  keyboardShortcuts: {
+    title: '鍵盤快捷鍵',
+    navigation: '導航',
+    archivesSection: '歸檔',
+    kProfilesSection: 'K 值設定',
+    generalSection: '通用',
+    shortcuts: {
+      goToPrinters: '前往印表機',
+      goToArchives: '前往歸檔',
+      goToQueue: '前往佇列',
+      goToStats: '前往統計',
+      goToProfiles: '前往雲端設定',
+      goToSettings: '前往設定',
+      focusSearch: '聚焦搜尋',
+      openUploadModal: '開啟上傳對話方塊',
+      clearSelection: '清除選擇 / 取消焦點',
+      contextMenu: '卡片右鍵選單',
+      refreshProfiles: '重新整理設定',
+      newProfile: '新增設定',
+      exitSelectionMode: '結束選擇模式',
+      showHelp: '顯示此協助',
+    },
+    footer: '按 Esc 或點選外部關閉',
+  },
+
+  // Notification Log
+  notificationLog: {
+    title: '通知日誌',
+    events: {
+      printStarted: '列印開始',
+      printComplete: '列印完成',
+      printFailed: '列印失敗',
+      printStopped: '列印停止',
+      progress: '進度',
+      printerOffline: '印表機離線',
+      printerError: '印表機錯誤',
+      lowFilament: '耗材不足',
+      maintenanceDue: '維護到期',
+      test: '測試',
+    },
+    timeAgo: {
+      justNow: '剛剛',
+      minutesAgo: '{{minutes}} 分鐘前',
+      hoursAgo: '{{hours}} 小時前',
+    },
+  },
+
+  // Restore/Backup Modal
+  restoreBackup: {
+    title: '恢復備份',
+    restoring: '恢復中...',
+    restoreComplete: '恢復完成',
+    restoreFailed: '恢復失敗',
+    importSettings: '從備份檔案匯入設定',
+    pleaseWait: '請稍候,正在恢復您的資料',
+    clickToSelect: '點選選擇備份檔案(.json 或 .zip)',
+    howDuplicateHandling: '重複處理方式:',
+    categories: {
+      printers: '印表機',
+      smartPlugs: '智慧插座',
+      notificationProviders: '通知提供者',
+      filaments: '耗材',
+      archives: '歸檔',
+      pendingUploads: '待處理上傳',
+      settingsTemplates: '設定和範本',
+    },
+    matchingInfo: {
+      printers: '按序列號匹配',
+      smartPlugs: '按 IP 位址匹配',
+      notificationProviders: '按名稱匹配',
+      filaments: '按名稱 + 類型 + 品牌匹配',
+      archives: '按內容雜湊匹配',
+      pendingUploads: '按檔名匹配',
+      settingsTemplates: '始終覆蓋',
+    },
+    replaceExisting: '替換現有資料',
+    keepExisting: '保留現有資料',
+    replaceDescription: '用備份資料覆蓋已存在的項目',
+    keepDescription: '僅恢復不存在的項目',
+    caution: '注意:',
+    cautionText: '覆蓋將用備份資料替換您目前的設定。出於安全考慮,印表機存取碼永遠不會被覆蓋。',
+    itemsRestored: '已恢復項目',
+    itemsSkipped: '已跳過項目',
+    restored: '已恢復',
+    skipped: '已跳過(已存在)',
+    filesLabel: '檔案(3MF、縮圖等)',
+    newApiKeysGenerated: '已產生新 API 金鑰',
+    newApiKeysWarning: '這些金鑰僅顯示一次。請立即複製!',
+    processingBackup: '處理備份檔案中...',
+    noDataFound: '備份檔案中未找到可恢復的資料。',
+    failedToRestore: '恢復備份失敗。請檢查檔案格式。',
+  },
+
+  // Backup Export Modal
+  backupExport: {
+    title: '匯出備份',
+    selectData: '選擇要包含的資料',
+    selectAll: '全選',
+    selectNone: '全不選',
+    categoryDescriptions: {
+      settings: '語言、主題、更新偏好',
+      notifications: 'ntfy、Pushover、Discord 等',
+      templates: '自訂訊息範本',
+      smartPlugs: 'Tasmota 插座設定',
+      externalLinks: '側邊欄外部服務連結',
+      printers: '印表機資訊(不含存取碼)',
+      plateDetection: '空列印板參考影像',
+      filaments: '耗材類型和成本',
+      maintenance: '自訂維護計畫',
+      archives: '所有列印資料 + 檔案(3MF、縮圖、照片)',
+      projects: '專案、材料清單和附件',
+      pendingUploads: '虛擬印表機待審核的上傳',
+      apiKeys: 'Webhook API 金鑰(匯入時產生新金鑰)',
+    },
+    requiresPrinters: '需要選擇印表機',
+    zipFileWarning: '將建立 ZIP 檔案。',
+    zipFileDescription: '包括所有 3MF 檔案、縮圖、縮時攝影和照片。這可能需要一些時間並產生較大的檔案。',
+    includeAccessCodes: '包含存取碼',
+    includeAccessCodesDescription: '用於轉移到另一臺機器',
+    includeAccessCodesWarning: '存取碼將以明文形式包含。請妥善保管此備份檔案!',
+    categoriesSelected: '已選擇 {{selectedCount}} 個類別',
+  },
+
+  // Pending Uploads Panel
+  pendingUploads: {
+    placeholders: {
+      notes: '新增關於此列印的備註...',
+    },
+    discardUpload: '丟棄上傳',
+    archiveAllUploads: '歸檔所有上傳',
+    discardAllUploads: '丟棄所有上傳',
+    archive: '歸檔',
+    timeAgo: {
+      justNow: '剛剛',
+      minutesAgo: '{{minutes}} 分鐘前',
+      hoursAgo: '{{hours}} 小時前',
+      daysAgo: '{{days}} 天前',
+    },
+  },
+
+  // API Browser
+  apiBrowser: {
+    placeholders: {
+      requestBody: 'JSON 請求體...',
+      searchEndpoints: '搜尋端點...',
+    },
+  },
+
+  // Configure AMS Slot Modal
+  configureAmsSlot: {
+    title: '設定 AMS 槽位',
+    slotConfigured: '槽位已設定!',
+    configuringSlot: '正在設定槽位:',
+    slotLabel: '{{ams}} 槽位 {{slot}}',
+    searchPresets: '搜尋預設...',
+    colorPlaceholder: '顏色名稱或十六進位(例如:棕色、FF8800)',
+    clearCustomColor: '清除自訂顏色',
+    noCloudPresets: '無雲端預設。登入拓竹雲以同步。',
+    noPresetsAvailable: '無可用預設。登入拓竹雲或匯入本機設定。',
+    noMatchingPresets: '未找到匹配的預設。',
+    custom: '自訂',
+    builtin: '內建',
+    settingsSentToPrinter: '設定已傳送到印表機',
+    filamentProfile: '耗材設定',
+    kProfileLabel: 'K 值設定(壓力推進)',
+    filteringFor: '篩選:{{material}}',
+    noKProfile: '無 K 值設定(使用預設值 0.020)',
+    noMatchingKProfiles: '未找到匹配的 K 值設定。將使用預設 K=0.020。',
+    selectFilamentFirst: '請先選擇耗材設定',
+    kFromCalibration: 'K={{value}}(來自印表機校準)',
+    customColorLabel: '自訂顏色(可選)',
+    presetColors: '{{name}} 顏色:',
+    showLessColors: '顯示更少顏色',
+    showMoreColors: '顯示更多顏色',
+    clear: '清除',
+    hexLabel: '十六進位:#{{hex}}',
+    resetting: '重設中...',
+    resetSlot: '重設槽位',
+    cancel: '取消',
+    configuring: '設定中...',
+    configureSlot: '設定槽位',
+  },
+
+  // GitHub Backup Settings
+  githubBackup: {
+    title: 'GitHub 備份',
+    history: '歷史',
+    downloadBackup: '下載備份',
+    restoreBackup: '恢復備份',
+    noBackupsYet: '尚無備份',
+  },
+
+  // Email Settings
+  emailSettings: {
+    placeholders: {
+      fromName: 'BamBuddy',
+    },
+  },
+
+  // Tag Management Modal
+  tagManagement: {
+    searchTags: '搜尋標籤...',
+    renameTag: '重新命名標籤',
+    deleteTag: '刪除標籤',
+  },
+
+  // Notification Template Editor
+  notificationTemplates: {
+    placeholders: {
+      title: '通知標題...',
+      body: '通知正文...',
+    },
+  },
+
+  // Batch Tag Modal
+  batchTag: {
+    placeholders: {
+      newTag: '輸入新標籤...',
+    },
+  },
+
+  // Photo Gallery Modal
+  photoGallery: {
+    deletePhoto: '刪除照片',
+  },
+
+  // Filament Hover Card
+  filamentHoverCard: {
+    copySpoolUuid: '複製耗材 UUID',
+  },
+
+  // K Profiles View
+  kProfilesView: {
+    hasNote: '有備註',
+    copyProfile: '複製設定',
+  },
+
+  // Layout/Navigation
+  layout: {
+    openMenu: '開啟選單',
+    noPermissionSystemInfo: '您沒有檢視系統資訊的權限',
+  },
+
+  // Dashboard
+  dashboard: {
+    dragToReorder: '拖曳以重新排列',
+    hideWidget: '隱藏小工具',
+  },
+
+  // Notification Provider Card
+  notificationProviderCard: {
+    deleteNotificationProvider: '刪除通知提供者',
+  },
+
+  // File Manager Modal
+  fileManagerModal: {
+    closeFileManager: '關閉檔案管理器',
+    sortFiles: '排序檔案',
+    goToParentFolder: '返回上級資料夾',
+    threeView: '3D 檢視',
+  },
+
+  // Embedded Camera Viewer
+  embeddedCameraViewer: {
+    refreshStream: '重新整理流',
+    close: '關閉',
+    zoomOut: '縮小',
+    resetZoom: '重設縮放',
+    zoomIn: '放大',
+    dragToResize: '拖曳調整大小',
+  },
+
+  // Timelapse Viewer
+  timelapseViewer: {
+    skipBack5s: '後退 5 秒',
+    skipForward5s: '前進 5 秒',
+  },
+
+  // Notification Providers
+  notificationProviders: {
+    descriptions: {
+      email: 'SMTP 郵件通知',
+      telegram: '透過 Telegram 機器人通知',
+      discord: '透過 Webhook 傳送到 Discord 頻道',
+      ntfy: '免費、可自託管的推送通知',
+      pushover: '簡單、可靠的推送通知',
+      callmebot: '透過 CallMeBot 的免費 WhatsApp 通知',
+      webhook: '通用 HTTP POST 到任意 URL',
+    },
+  },
+
+  // Log Viewer
+  logViewer: {
+    searchPlaceholder: '搜尋訊息或日誌名稱...',
+    noLogEntries: '未找到日誌條目',
+  },
+
+  // Switchbar Popover
+  switchbarPopover: {
+    noSwitchesInSwitchbar: '切換欄中沒有開關',
+  },
+
+  // Project Page Modal
+  projectPageModal: {
+    placeholders: {
+      title: '標題',
+      designer: '設計師',
+      license: '許可證',
+      description: '輸入描述...',
+      profileTitle: '設定標題',
+      profileDescription: '設定描述...',
+    },
+  },
+
+  // Spoolman Settings
+  spoolmanSettings: {},
+
+  // Time
+  time: {
+    unknown: '-',
+    waiting: '等待中',
+    justNow: '剛剛',
+    now: '現在',
+    minsAgo: '{{count}} 分鐘前',
+    inMins: '{{count}} 分鐘後',
+    hoursAgo: '{{count}} 小時前',
+    inHours: '{{count}} 小時後',
+    daysAgo: '{{count}} 天前',
+    inDays: '{{count}} 天後',
+  },
+
+  // SpoolBuddy Kiosk
+  spoolbuddy: {
+    nav: {
+      dashboard: '儀表板',
+      ams: 'AMS',
+      inventory: '庫存',
+      writeTag: '寫入',
+      settings: '設定',
+    },
+    status: {
+      nfcReady: 'NFC 就緒',
+      nfcOff: 'NFC 關閉',
+      offline: '離線',
+      online: '線上',
+      noPrinters: '無印表機',
+      deviceOffline: '裝置離線',
+      waitingConnection: '等待裝置連線...',
+      systemReady: '系統就緒',
+      status: '狀態',
+    },
+    dashboard: {
+      readyToScan: '準備掃描',
+      idleMessage: '將耗材放在磅秤上以識別',
+      nfcHint: 'NFC 標籤將自動讀取',
+      device: '裝置',
+      syncWeight: '同步重量',
+      weightSynced: '已同步!',
+      unknownTag: '未知標籤',
+      newTag: '偵測到新標籤',
+      onScale: '在磅秤上',
+      linkSpool: '連結到耗材',
+      linkTagTitle: '將標籤連結到耗材',
+      linkTag: '連結標籤',
+      selectSpool: '選擇要連結此標籤的耗材:',
+      noUntagged: '未找到沒有標籤的耗材',
+      tagDetected: '偵測到標籤',
+      noTag: '無標籤',
+      tagId: '標籤',
+      grossWeight: '毛重',
+      spoolSize: '耗材盤尺寸',
+      close: '關閉',
+      currentSpool: '目前耗材',
+    },
+    modal: {
+      spoolDetected: '偵測到耗材',
+      assignToAms: '分配到 AMS',
+      syncWeight: '同步重量',
+      weightSynced: '已同步!',
+      syncing: '同步中...',
+      newTagDetected: '偵測到新標籤',
+      addToInventory: '新增到庫存',
+      assignToAmsTitle: '分配到 AMS',
+      selectSlot: '選擇槽位',
+      assign: '分配',
+      assigning: '分配中...',
+      assignSuccess: '已分配!',
+      assignError: '分配耗材失敗。請重試。',
+      noPrinterSelected: '選擇印表機...',
+      noAmsDetected: '此印表機未偵測到 AMS',
+      slot: '槽位',
+    },
+    weight: {
+      noReading: '無讀數',
+      stable: '穩定',
+      measuring: '測量中...',
+      tare: '去皮',
+      calibrate: '校準',
+    },
+    spool: {
+      remaining: '剩餘',
+      material: '材料',
+      brand: '品牌',
+      color: '顏色',
+      coreWeight: '軸心重',
+      labelWeight: '標籤重',
+      scaleWeight: '磅秤重',
+      netWeight: '淨重',
+      lastUsed: '上次使用',
+    },
+    ams: {
+      noData: '未偵測到 AMS',
+      connectAms: '連線 AMS 以檢視耗材槽位',
+      noPrinter: '未選擇印表機',
+      selectPrinter: '從頂部欄選擇印表機',
+      printerDisconnected: '印表機已斷開',
+      humidity: '濕度',
+      level: '餘量',
+      active: '活躍',
+      slot: '槽位',
+      empty: '空',
+    },
+    inventory: {
+      search: '搜尋耗材...',
+      empty: '庫存中沒有耗材',
+      noResults: '沒有匹配的耗材',
+      spools: '個耗材',
+      addSpool: '新增耗材',
+    },
+    settings: {
+      // Tabs
+      tabDevice: '裝置',
+      tabDisplay: '顯示',
+      tabScale: '磅秤',
+      tabUpdates: '更新',
+      // Device tab
+      nfcReader: 'NFC 讀卡器',
+      type: '類型',
+      connection: '連線',
+      notConnected: '不適用',
+      deviceInfo: '裝置資訊',
+      hostname: '主機',
+      uptime: '執行時間',
+      systemConfig: '後端與認證',
+      backendUrl: 'Bambuddy 後端 URL',
+      apiToken: 'API 權杖',
+      apiTokenPlaceholder: '輸入 API 權杖',
+      saveConfig: '儲存設定',
+      systemQueued: '設定已加入佇列。',
+      nfcDiagnostic: 'NFC 診斷',
+      scaleDiagnostic: '磅秤診斷',
+      readTagDiagnostic: '讀取標籤診斷',
+      testNfc: '測試讀卡器',
+      testScale: '測試精度',
+      testReadTag: '讀取標籤',
+      systemFieldsRequired: '後端 URL 為必填項。',
+      // Display tab
+      brightness: '亮度',
+      saved: '已儲存',
+      noBacklight: '未偵測到 DSI 背光。亮度控制需要 DSI 螢幕。',
+      screenBlank: '螢幕熄滅超時',
+      screenBlankDesc: '不活動後螢幕關閉。觸控喚醒。',
+      displayNote: '亮度作為軟體濾鏡套用。',
+      // Scale tab
+      scaleCalibration: '磅秤校準',
+      currentWeight: '目前重量',
+      tareOffset: '去皮',
+      calFactor: '係數',
+      knownWeight: '已知重量',
+      calStep1: '移除磅秤上所有物品並按設定零點。',
+      calStep2: '將已知重量放在磅秤上。',
+      setZero: '設定零點',
+      calibrateNow: '校準',
+      calibrated: '已校準',
+      tareSet: '去皮命令已傳送。等待裝置回應...',
+      tareFailed: '傳送去皮命令失敗',
+      zeroSet: '零點已設定。將已知重量放在磅秤上。',
+      calibrationDone: '校準完成!',
+      calibrationFailed: '校準失敗',
+      lastCalibrated: '上次校準',
+      stable: '穩定',
+      settling: '穩定中...',
+      firmware: '韌體',
+      scale: '磅秤',
+      noDevice: '未找到 SpoolBuddy 裝置',
+      // Updates tab
+      daemonVersion: '守護程式版本',
+      currentVersion: '目前',
+      versionPending: '等待守護程式...',
+      checking: '檢查中...',
+      checkUpdates: '檢查更新',
+      updateAvailable: '有可用更新',
+      updateInstructions: '透過 SSH 更新:執行 SpoolBuddy 安裝腳本進行升級。',
+      upToDate: '已是最新',
+      includeBeta: '包含測試版本',
+    },
+    writeTag: {
+      tabExisting: '現有耗材',
+      tabNew: '新耗材',
+      tabReplace: '替換標籤',
+      searchPlaceholder: '按材料、顏色、品牌搜尋...',
+      noUntaggedSpools: '沒有無標籤的耗材',
+      noTaggedSpools: '沒有有標籤的耗材',
+      selectSpool: '選擇一個耗材,然後將空白 NTAG 放在讀卡器上',
+      placeTag: '將 NTAG 放在讀卡器上',
+      tagReady: '偵測到標籤 — 準備寫入',
+      writeTag: '寫入標籤',
+      replaceTag: '替換標籤',
+      writing: '寫入標籤中...',
+      waiting: '等待 SpoolBuddy...',
+      writeSuccess: '標籤寫入成功!',
+      writeFailed: '寫入失敗',
+      queueFailed: '佇列寫入命令失敗',
+      tryAgain: '重試',
+      cancel: '取消',
+      replaceWarning: '舊標籤將被取消連結。新標籤將替換它。',
+      deviceOffline: 'SpoolBuddy 離線',
+      material: '材料',
+      colorName: '顏色名稱',
+      color: '顏色',
+      brand: '品牌',
+      weight: '重量 (g)',
+      createSpool: '建立耗材',
+      creating: '建立中...',
+      spoolCreated: '耗材已建立!準備寫入。',
+      createFailed: '建立耗材失敗',
+    },
+    quickMenu: {
+      printerPower: '印表機電源',
+      systemControls: '系統',
+      restartDaemon: '重新啟動守護程式',
+      restartBrowser: '重新啟動瀏覽器',
+      reboot: '重新開機',
+      shutdown: '關機',
+      swipeToClose: '向下滑動關閉',
+      confirmTitle: '確認',
+      confirmShutdown: '確定要關閉 SpoolBuddy 嗎?您需要實體存取才能重新開啟。',
+      confirmReboot: '確定要重新開機 SpoolBuddy 嗎?',
+      confirmRestartDaemon: '重新啟動 SpoolBuddy 守護程式?NFC 和磅秤將暫時不可用。',
+      confirmRestartBrowser: '重新啟動kiosk瀏覽器?螢幕將短暫變黑。',
+      confirm: '確認',
+      confirmPlugOn: '開啟 {{name}}?',
+      confirmPlugOff: '關閉 {{name}}?',
+      turnOn: '開啟',
+      turnOff: '關閉',
+    },
+  },
+
+  bugReport: {
+    title: '報告錯誤',
+    description: '描述',
+    descriptionPlaceholder: '出了什麼問題?請描述問題...',
+    email: '信箱(可選)',
+    emailPlaceholder: 'your@email.com',
+    emailPrivacy: '如果提供,您的信箱將包含在GitHub Issue的摺疊部分中,以便維護者後續跟進。',
+    screenshot: '截圖',
+    uploadOrPaste: '上傳、貼上或拖曳圖片',
+    dataCollectedSummary: '報告中包含哪些資料?',
+    dataIncluded: '包含:',
+    dataIncludedList: '應用程式版本、作業系統、架構、Python版本、資料庫統計(僅計數)、印表機型號、噴嘴數量、韌體版本、連線狀態、整合狀態(Spoolman、MQTT、HA)、非敏感設定、網路介面數量、Docker詳情、依賴版本。',
+    dataNeverIncluded: '絕不包含:',
+    dataNeverIncludedList: '印表機名稱、序列號、存取碼、密碼、IP 位址、信箱地址、API金鑰、權杖、Webhook URL、主機名稱或使用者名稱。',
+    submit: '提交',
+    startLogging: '開始偵錯日誌',
+    stepEnableLogging: '偵錯日誌已啟用',
+    stepReproduce: '請現在重現問題',
+    stepStopLogging: '停止並提交報告',
+    stopAndSubmit: '停止並提交',
+    maxDuration: '{{minutes}} 分鐘後自動停止',
+    stoppingLogs: '正在收集日誌並提交...',
+    submitting: '正在提交錯誤報告...',
+    submitSuccess: '錯誤報告提交成功!',
+    submitFailed: '提交錯誤報告失敗',
+    thankYou: '謝謝!',
+    submitted: '您的錯誤報告已提交。',
+    viewIssue: '檢視 Issue',
+    unexpectedError: '發生了意外錯誤',
+  },
+  failureDetection: {
+    title: 'AI 故障檢測',
+    description: '透過自託管的 Obico ML API 監控列印,並對偵測到的故障自動採取行動。',
+    mlUrl: 'Obico ML API 地址',
+    mlUrlHint: '您自託管的 Obico ml_api 容器的基礎 URL(例如 http://192.168.1.10:3333)。',
+    test: '測試',
+    testSuccess: 'ML API 可存取且正常。',
+    testFailed: '無法存取 ML API。',
+    sensitivity: '靈敏度',
+    sensitivityLow: '低(減少誤報)',
+    sensitivityMedium: '中(平衡)',
+    sensitivityHigh: '高(更早檢測,更多誤報)',
+    sensitivityHint: '調整觸發警告和故障的置信度閾值。',
+    action: '偵測到故障時的操作',
+    actionNotify: '僅通知',
+    actionPause: '暫停列印',
+    actionPauseOff: '暫停並切斷電源',
+    pollInterval: '檢查間隔(秒)',
+    pollIntervalHint: '列印過程中每臺印表機的檢查頻率。最小 5 秒,最大 120 秒。',
+    externalUrlMissing: '尚未設定外部 URL。',
+    externalUrlHint: 'ML API 透過 URL 擷取攝影機快照。請在一般設定中設定外部 URL,讓 ML API 容器可以連線到 Bambuddy。',
+    perPrinterTitle: '監控的印表機',
+    perPrinterHint: '選擇檢測服務要監視哪些印表機。',
+    monitorAll: '監控所有已連線的印表機',
+    statusTitle: '狀態',
+    serviceRunning: '服務執行中',
+    thresholds: '低 / 高閾值',
+    activePrinters: '活動列印',
+    noActivePrints: '目前沒有正在進行的列印。',
+    historyTitle: '最近檢測',
+    noHistory: '尚無檢測紀錄。',
+  },
+};