From 779442fe2978ae37973f6acccd120c973480df8a Mon Sep 17 00:00:00 2001 From: smryyyyy <1619500656@qq.com> Date: Thu, 28 May 2026 15:59:17 +0800 Subject: [PATCH] feat: add i18n infrastructure and Chinese (zh-CN) localization - Add lightweight translation system (src/language/) with t() function, zero external dependencies - Add en.json and zh-CN.json covering ~900 UI strings - Convert 60+ source files from hardcoded English to t() calls - Add language selector on welcome page and in Settings - Fix module-level t() timing issues with lazy initialization - Translate welcome page, sidebar, settings, dialogs, commands, toasts, shortcuts, extensions hub, file dialogs, and status bar - Add cn_README.md with full Chinese documentation - Update HTML lang attributes to zh-CN - Fix pre-commit lint errors in settings-overlay.ts - Default language is English, switchable to Chinese --- README.md | 16 + cn_README.md | 288 ++++++ package-lock.json | 8 +- package.json | 2 +- public/architecture/index.html | 2 +- public/index.html | 2 +- public/oauth-callback.html | 2 +- src/commands/builtins/addons.ts | 3 +- src/commands/builtins/clipboard.ts | 9 +- .../builtins/custom-gateway-settings.ts | 63 +- src/commands/builtins/debug.ts | 13 +- src/commands/builtins/experimental-overlay.ts | 19 +- src/commands/builtins/experimental.ts | 9 +- src/commands/builtins/export.ts | 55 +- .../builtins/extensions-hub-connections.ts | 93 +- .../extensions-hub-extension-connections.ts | 8 +- .../builtins/extensions-hub-overlay.ts | 13 +- .../builtins/extensions-hub-plugins.ts | 19 +- .../builtins/extensions-hub-skills.ts | 25 +- src/commands/builtins/files.ts | 3 +- src/commands/builtins/help.ts | 3 +- src/commands/builtins/model.ts | 7 +- .../builtins/overlay-relative-date.ts | 10 +- src/commands/builtins/recovery-overlay.ts | 63 +- src/commands/builtins/resume-overlay.ts | 41 +- src/commands/builtins/resume-target.ts | 10 +- src/commands/builtins/rules-overlay.ts | 58 +- src/commands/builtins/session.ts | 49 +- src/commands/builtins/settings-overlay.ts | 144 ++- src/commands/builtins/settings.ts | 9 +- src/commands/builtins/shortcuts-overlay.ts | 55 +- src/commands/builtins/skills.ts | 3 +- src/commands/builtins/tools.ts | 3 +- src/compat/thinking-duration.ts | 6 +- src/execution/controller.ts | 3 +- src/execution/mode.ts | 4 +- src/experiments/flags.ts | 111 +-- src/extensions/permissions.ts | 291 ++---- src/extensions/runtime-manager.ts | 3 +- src/files/backend.ts | 3 +- src/files/workspace.ts | 11 +- src/language/index.ts | 39 + src/language/locales/en.json | 911 ++++++++++++++++++ src/language/locales/zh-CN.json | 911 ++++++++++++++++++ src/taskpane.html | 2 +- src/taskpane/bootstrap.ts | 9 +- src/taskpane/init.ts | 91 +- src/taskpane/keyboard-shortcuts.ts | 3 +- .../keyboard-shortcuts/editor-actions.ts | 3 +- src/taskpane/queue-display.ts | 5 +- src/taskpane/session-title.ts | 4 +- src/taskpane/status-bar.ts | 27 +- src/taskpane/status-context.ts | 30 +- src/taskpane/status-popovers.ts | 53 +- src/taskpane/welcome-login.ts | 93 +- src/tools/get-workbook-overview.ts | 5 +- src/ui-gallery.html | 2 +- src/ui/bridge-setup-card.ts | 39 +- src/ui/confirm-dialog.ts | 14 +- src/ui/disclosure-bar.ts | 25 +- src/ui/files-dialog-actions.ts | 20 +- src/ui/files-dialog-filtering.ts | 7 +- src/ui/files-dialog-status.ts | 3 +- src/ui/files-dialog.ts | 49 +- src/ui/loading.ts | 3 +- src/ui/message-renderers.ts | 13 +- src/ui/pi-input.ts | 23 +- src/ui/pi-sidebar.ts | 83 +- src/ui/provider-login.ts | 123 ++- src/ui/proxy-banner.ts | 23 +- src/ui/render-csv-table.ts | 2 +- src/ui/text-input-dialog.ts | 14 +- src/ui/toast.ts | 1 + src/ui/tool-renderers.ts | 18 +- src/ui/web-search-setup-card.ts | 43 +- src/ui/whimsical-messages.ts | 258 ++--- src/ui/working-indicator.ts | 17 +- src/workbook/context.ts | 4 +- 78 files changed, 3365 insertions(+), 1144 deletions(-) create mode 100644 cn_README.md create mode 100644 src/language/index.ts create mode 100644 src/language/locales/en.json create mode 100644 src/language/locales/zh-CN.json diff --git a/README.md b/README.md index 8df571f8..579ac846 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,25 @@ # Pi for Excel +> **🌐 [English](README.md) | [中文](cn_README.md)** + Open-source, multi-model AI sidebar add-in for Microsoft Excel. Powered by [Pi](https://pi.dev). Pi for Excel is an AI agent that lives inside Excel. It reads your workbook, makes changes, and does research — using any model you choose. Bring your own API key or OAuth login for Anthropic, OpenAI, Google Gemini, or GitHub Copilot. +--- + +## 🌍 Language Support + +This project now supports **Simplified Chinese (zh-CN)** alongside English: + +- **Chinese UI translation**: Most interface text is translated into Chinese. Switch language in **Settings → Advanced → Language** or use the language toggle on the welcome screen. +- **Translation system**: All user-facing UI strings use a lightweight `t()` translation function with zero third-party dependencies. Translations are stored in `src/language/locales/` as JSON files. +- **Adding new languages**: Create a new `{lang}.json` file in `src/language/locales/` and import it in `src/language/index.ts`. + +See [cn_README.md](cn_README.md) for the Chinese version of this document. + +--- + ## Features **Core spreadsheet tools** — 16 built-in tools that the AI can call to interact with your workbook: diff --git a/cn_README.md b/cn_README.md new file mode 100644 index 00000000..e7aeaf8b --- /dev/null +++ b/cn_README.md @@ -0,0 +1,288 @@ +# Pi for Excel + +> **🌐 [English](README.md) | [中文](cn_README.md)** + +开源、多模型的 Excel 侧边栏 AI 加载项。由 [Pi](https://pi.dev) 驱动。 + +Pi for Excel 是一个驻留在 Excel 中的 AI 智能体。它能读取你的工作簿、执行修改、进行调研——使用你选择的任意模型。自带 API 密钥或通过 OAuth 登录 Anthropic、OpenAI、Google Gemini 或 GitHub Copilot。 + +--- + +## 功能特性 + +**核心电子表格工具** — 16 个内置工具,AI 可调用它们与你的工作簿交互: + +| 工具 | 功能 | +|------|------| +| `get_workbook_overview` | 结构蓝图——工作表、表头、命名区域、表格、图表、数据透视表 | +| `read_range` | 以紧凑(markdown)、CSV 或详细(含格式)模式读取单元格 | +| `write_cells` | 写入值/公式,带覆盖保护和自动验证 | +| `fill_formula` | 跨区域自动填充公式(相对引用自动调整) | +| `search_workbook` | 跨所有工作表查找文本、值或公式引用 | +| `modify_structure` | 插入/删除行/列、添加/重命名/删除/隐藏工作表 | +| `format_cells` | 应用格式——字体、颜色、数字格式、边框、命名样式 | +| `conditional_format` | 添加或清除条件格式规则 | +| `trace_dependencies` | 追踪公式血统(前导引用或后续依赖) | +| `explain_formula` | 用通俗语言解释公式,附带单元格引用 | +| `view_settings` | 网格线、标题、冻结窗格、选项卡颜色、工作表可见性 | +| `comments` | 读取、添加、更新、回复、解决/重新打开单元格批注 | +| `workbook_history` | 列出/恢复工作簿修改的自动备份(保存间隔间) | +| `instructions` | AI 的持久用户级和工作簿级指导 | +| `conventions` | 可配置的格式默认值(货币、负数、零、小数位) | +| `skills` | 用于特定任务工作流的捆绑 Agent 技能 | + +**多模型支持** — 使用任何支持的提供商;会话中可切换模型: +- **Anthropic**(Claude)— API 密钥或 OAuth +- **OpenAI** / **OpenAI Codex** — API 密钥 +- **Google Gemini** — API 密钥 +- **GitHub Copilot** — OAuth +- **自定义 OpenAI 兼容网关** — 在 `/settings` 中配置端点 + 模型 + API 密钥 + +**会话管理** — 每个工作簿多个会话标签页、自动保存/恢复、会话历史、使用 `/resume` 从上次中断处继续。 + +**自动上下文注入** — AI 在每次对话前自动接收工作簿蓝图、当前选择区域和最近的单元格变更。无需手动描述你正在查看的内容。 + +**工作簿恢复** — 每次修改前自动创建检查点。如果出错,可从侧边栏一键撤销。 + +**格式约定** — 一次性定义你的风格(货币符号、负号样式、小数位数),AI 自动遵循。 + +**斜杠命令** — `/model`、`/login`、`/settings`、`/rules`、`/extensions`、`/tools`、`/export`、`/compact`、`/new`、`/resume`、`/history`、`/shortcuts` 等。 + +**扩展** — 通过聊天安装侧边栏扩展(迷你应用)。AI 可直接通过 `extensions_manager` 工具生成并安装扩展代码。扩展默认在 iframe 沙盒中运行。 + +**集成** — 可选的外部工具集成: +- **网络搜索**(默认 Jina,可选 Serper/Tavily/Brave)+ `fetch_page` — 无需离开 Excel 即可查找和阅读外部来源 +- **MCP 网关** — 连接到用户配置的 MCP 服务器以访问自定义工具 + +**桥接 + 高级控制**(通过 `/experimental` 管理): +- Tmux 桥接设置——配置桥接 URL/令牌并运行健康检查 +- Python / LibreOffice 桥接设置——配置桥接 URL/令牌 +- 文件工作区写入/删除门控——跨会话共享工件存储(内置助手文档在 `assistant-docs/` 下始终可读) +- 高级扩展控制——远程 URL 选择加入、权限强制执行、沙盒回滚和 Widget API v2 + +(网络搜索和 MCP 在 `/tools` 或 `/extensions` → 连接中管理。) + +--- + +## 🌍 语言支持 + +本项目在英文基础上增加了对**简体中文(zh-CN)** 的支持: + +- **中文界面翻译**:大部分界面文本已翻译为中文。可在 **设置 → 高级 → 语言** 中切换,或在欢迎页使用语言选择按钮。 +- **翻译系统**:所有面向用户的 UI 字符串使用轻量级 `t()` 翻译函数,零第三方依赖。翻译文件存储在 `src/language/locales/` 目录下,为 JSON 格式。 +- **添加新语言**:在 `src/language/locales/` 中创建 `{lang}.json` 文件,并在 `src/language/index.ts` 中导入即可。 + +--- + +## 安装 + +1. 下载 [`manifest.prod.xml`](https://pi-for-excel.vercel.app/manifest.prod.xml) +2. 将其添加至 Excel——详见 [**安装指南**](docs/install.md)(含 macOS + Windows 逐步说明) +3. 单击功能区中的 **打开 Pi** +4. 连接提供商(API 密钥或 OAuth),或在 `/settings` 中配置自定义 OpenAI 兼容网关 +5. 开始对话——试试"我有哪些工作表?"或"总结我当前选中的内容" + +--- + +## 开发者快速入门 + +### 前置条件 + +- **Node.js ≥ 20** +- **mkcert** — 用于本地 HTTPS(Office.js 要求) + +### 设置 + +```bash +git clone https://github.com/tmustier/pi-for-excel.git +cd pi-for-excel +npm install + +# 生成本地 HTTPS 证书(Office.js 需要 HTTPS) +mkcert -install # 一次性 CA 设置 +mkcert localhost # 创建 localhost.pem + localhost-key.pem +mv localhost-key.pem key.pem +mv localhost.pem cert.pem +``` + +### 运行 + +```bash +npm run dev # Vite 开发服务器,https://localhost:3000 +``` + +然后将开发清单侧载到 Excel: + +**macOS**([Microsoft 文档](https://learn.microsoft.com/en-us/office/dev/add-ins/testing/sideload-an-office-add-in-on-mac)): +```bash +cp manifest.xml ~/Library/Containers/com.microsoft.Excel/Data/Documents/wef/ +``` +然后打开 Excel → **插入** → **我的加载项** → **Pi for Excel**。 + +**Windows**([Microsoft 文档](https://learn.microsoft.com/en-us/office/dev/add-ins/testing/sideload-office-add-ins-for-testing)): + +打开 Excel → **插入** → **我的加载项** → **上传我的加载项** → 选择 `manifest.xml`。 + +开发清单指向 `https://localhost:3000`。生产清单(`manifest.prod.xml`)指向托管的 Vercel 部署。 + +### 有用命令 + +| 命令 | 说明 | +|------|------| +| `npm run dev` | 启动 Vite 开发服务器(端口 3000,HTTPS) | +| `npm run build` | 生产构建 → `dist/` | +| `npm run check` | 代码检查 + 类型检查 + CSS 主题检查 | +| `npm run typecheck` | 仅 TypeScript 类型检查 | +| `npm run lint` | ESLint | +| `npm run test:models` | 单元测试——模型排序 | +| `npm run test:context` | 单元测试——工具、上下文、会话、扩展、集成 | +| `npm run test:security` | 安全策略测试——代理、CORS、沙盒、OAuth | +| `npm run proxy:https` | OAuth 流程的 CORS 代理(默认 `https://localhost:3003`) | +| `npm run validate` | 验证 Office 加载项清单 | + +### CORS 代理 + +某些 OAuth 令牌端点在 Office WebView 中被 CORS 阻止。如果 OAuth 登录失败: + +1. 用户设置命令:`npx pi-for-excel-proxy`(如果缺少 Node.js,可用 `curl -fsSL https://piforexcel.com/proxy | sh`) +2. 开发/源码设置命令:`npm run proxy:https`(默认 `https://localhost:3003`) +3. 在 Pi → `/settings` → **代理** → 启用并设置 URL +4. 重试登录 + +API 密钥认证通常无需代理即可使用。 + +### 本地桥接(Python / tmux) + +使用一键本地桥接助手: + +- Python / LibreOffice 桥接:`npx pi-for-excel-python-bridge`(默认 URL `https://localhost:3340`,真实模式) +- tmux 桥接:`npx pi-for-excel-tmux-bridge`(默认 URL `https://localhost:3341`,真实模式) + +在 Pi 中,这些 localhost 桥接 URL 默认使用。仅当需要非默认 URL 时才配置 `/experimental ...-bridge-url`。 + +真实模式前置条件: + +- `python3` 必须安装(用于 `python_run` / `python_transform_range`) +- LibreOffice(`soffice` 或 `libreoffice`)是 `libreoffice_convert` 所必需的 +- `tmux` 是 tmux 桥接真实模式所必需的 + +可选的辅助安装(macOS/Homebrew): + +- `npx pi-for-excel-python-bridge --install-missing` +- `npx pi-for-excel-tmux-bridge --install-missing` + +手动 macOS 安装: + +```bash +brew install tmux +brew install --cask libreoffice +``` + +要强制使用安全模拟模式: + +- `PYTHON_BRIDGE_MODE=stub npx pi-for-excel-python-bridge` +- `TMUX_BRIDGE_MODE=stub npx pi-for-excel-tmux-bridge` + +源码检出替代方案仍可通过 `npm run python:bridge:https` 和 `npm run tmux:bridge:https` 使用。 + +--- + +## 架构 + +Pi for Excel 是一个单页 Office 任务窗格加载项,使用以下技术构建: + +- **[Vite](https://vite.dev/)** — 开发服务器 + 生产打包器 +- **[Lit](https://lit.dev/)** — 侧边栏 UI 的 Web 组件 +- **[pi-agent-core](https://www.npmjs.com/package/@earendil-works/pi-agent-core)** — 代理运行时(工具循环、流式传输、状态管理) +- **[pi-ai](https://www.npmjs.com/package/@earendil-works/pi-ai)** — 多提供商 LLM 抽象(Anthropic、OpenAI、Google、GitHub Copilot) +- **[pi-web-ui](https://www.npmjs.com/package/@earendil-works/pi-web-ui)** — 共享 Web UI 组件(消息渲染、存储、设置对话框) +- **[Office.js](https://learn.microsoft.com/en-us/office/dev/add-ins/)** — Excel 工作簿 API + +### 源码布局 + +``` +src/ +├── taskpane/ # 应用初始化、会话管理、标签布局、上下文注入 +├── taskpane.html # 入口 HTML(加载 Office.js + taskpane.ts) +├── taskpane.ts # 入口脚本 +├── boot.ts # 挂载前设置(CSS、补丁) +├── tools/ # 16 个核心工具 + 功能标记工具 + 注册表 +├── prompt/ # 系统提示构建器 +├── context/ # 工作簿蓝图缓存、选择/变更追踪 +├── auth/ # OAuth 提供商、API 代理、凭据恢复 +├── models/ # 模型排序 + 版本评分 +├── ui/ # 侧边栏组件、工具渲染器、主题 CSS +│ └── theme/ # 设计令牌、组件样式(DM Sans + 青绿色调色板) +├── commands/ # 斜杠命令注册表 + 内置命令 +├── extensions/ # 扩展存储、沙盒运行时、权限 +├── integrations/ # 网络搜索 + MCP 网关集成目录 +├── skills/ # Agent 技能目录 + 运行时加载器 +├── experiments/ # 功能标记定义 + 切换逻辑 +├── workbook/ # 工作簿标识(哈希)、会话关联、协调器 +├── conventions/ # 格式默认值(货币、负数、小数位) +├── rules/ # 持久化用户/工作簿规则存储 +├── compaction/ # 自动压缩阈值 + 逻辑 +├── storage/ # IndexedDB 初始化 +├── files/ # 文件工作区(始终可读/列表;写入/删除受功能门控) +├── audit/ # 工作簿变更审计日志 +├── messages/ # 消息转换助手 +├── debug/ # 调试模式工具 +├── stubs/ # CSP/仅 Node 依赖的浏览器存根(Ajv、Bedrock、stream 等) +├── compat/ # 兼容性补丁(Lit、marked、模型选择器) +├── language/ # 翻译系统(t() 函数 + en.json + zh-CN.json) +└── utils/ # 共享助手(HTML 转义、类型守卫、错误处理) + +scripts/ # 开发助手——CORS 代理、tmux/Python 桥接、清单生成 +pkg/proxy/ # 可发布的 npm CLI 包:`pi-for-excel-proxy` +pkg/python-bridge/ # 可发布的 npm CLI 包:`pi-for-excel-python-bridge` +pkg/tmux-bridge/ # 可发布的 npm CLI 包:`pi-for-excel-tmux-bridge` +tests/ # 单元测试 + 安全测试(约 50 个测试文件) +docs/ # 当前文档(安装/部署/功能/策略)+ archive/ 历史规划 +skills/ # 捆绑的 Agent 技能定义(web-search、mcp-gateway、tmux-bridge、python-bridge) +public/assets/ # 加载项图标(16/32/80/128px) +``` + +### 关键设计模式 + +- **工具注册表为单一真实来源** — `src/tools/registry.ts` 定义所有核心工具名称和构造。UI 渲染器、输入人性化器和提示文档均由此派生。 +- **工作簿协调器** — 按工作簿序列化可变工具调用,防止多个会话标签页并发写入。 +- **自动上下文** — 在每次用户消息前注入工作簿蓝图、选择状态和最近变更,使 AI 始终知道它在查看什么。 +- **执行策略** — 每个工具被分类为 `read/none` 或 `mutate/content|structure`,以确定锁定和检查点行为。 +- **恢复检查点** — 修改操作在写入前自动快照受影响的单元格,实现一键回滚。 +- **扩展沙盒** — 不受信任的扩展(内联代码、远程 URL)默认在 iframe 沙盒中运行;内置/本地模块在主机上运行。 + +--- + +## 部署 + +生产构建是部署到 [Vercel](https://vercel.com) 的静态站点。维护者设置请参阅 [docs/deploy-vercel.md](docs/deploy-vercel.md)。 + +用户通过下载 `manifest.prod.xml` 并在 Excel 中上传来安装——清单指向托管的 Vercel URL。更新是自动的(关闭并重新打开任务窗格)。 + +--- + +## 文档 + +| 文档 | 说明 | +|------|------| +| [docs/install.md](docs/install.md) | 非技术用户安装指南 | +| [docs/deploy-vercel.md](docs/deploy-vercel.md) | 托管部署(Vercel) | +| [docs/extensions.md](docs/extensions.md) | 扩展开发指南 | +| [docs/integrations-external-tools.md](docs/integrations-external-tools.md) | 网络搜索 + MCP 集成设置 | +| [docs/security-threat-model.md](docs/security-threat-model.md) | 安全威胁模型 | +| [docs/compaction.md](docs/compaction.md) | 会话压缩(`/compact`) | +| [src/tools/DECISIONS.md](src/tools/DECISIONS.md) | 工具行为决策日志 | +| [src/ui/README.md](src/ui/README.md) | UI 架构 + Tailwind v4 说明 | + +--- + +## 致谢 + +- [Pi](https://github.com/badlogic/pi-mono) by [@badlogic](https://github.com/badlogic)(Mario Zechner)— 驱动此项目的代理框架。Pi for Excel 使用 pi-agent-core、pi-ai 和 pi-web-ui 实现代理循环、LLM 抽象和会话存储。 +- [whimsical.ts](https://github.com/mitsuhiko/agent-stuff/blob/main/pi-extensions/whimsical.ts) by [@mitsuhiko](https://github.com/mitsuhiko)(Armin Ronacher)— 轮播的"工作中…"消息改编自他的 Pi 扩展,为电子表格/财务场景重写。 + +--- + +## 许可证 + +[MIT](LICENSE) © Thomas Mustier diff --git a/package-lock.json b/package-lock.json index 242c59f2..1a27acb3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "typebox": "^1.1.32", "typescript": "^5.7.0", "typescript-eslint": "^8.57.0", - "vite": "^7.3.2" + "vite": "^7.3.3" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" @@ -15167,9 +15167,9 @@ } }, "node_modules/vite": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", - "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index daf1592b..a030b627 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "typebox": "^1.1.32", "typescript": "^5.7.0", "typescript-eslint": "^8.57.0", - "vite": "^7.3.2" + "vite": "^7.3.3" }, "license": "MIT", "repository": { diff --git a/public/architecture/index.html b/public/architecture/index.html index b0f047e4..fe1af9ad 100644 --- a/public/architecture/index.html +++ b/public/architecture/index.html @@ -1,5 +1,5 @@ - +
diff --git a/public/index.html b/public/index.html index 447afd48..4bdf0dba 100644 --- a/public/index.html +++ b/public/index.html @@ -1,5 +1,5 @@ - + diff --git a/public/oauth-callback.html b/public/oauth-callback.html index cd8848fc..483f0c54 100644 --- a/public/oauth-callback.html +++ b/public/oauth-callback.html @@ -1,5 +1,5 @@ - + diff --git a/src/commands/builtins/addons.ts b/src/commands/builtins/addons.ts index 34754837..f1877b8d 100644 --- a/src/commands/builtins/addons.ts +++ b/src/commands/builtins/addons.ts @@ -4,6 +4,7 @@ import type { ExtensionsHubTab } from "./extensions-hub-overlay.js"; import type { SlashCommand } from "../types.js"; +import { t } from "../../language/index.js"; export interface AddonsCommandActions { openExtensionsHub: (tab?: ExtensionsHubTab) => void | Promise
- Understands and acts in Excel. Remembers how you like things.
Builds its own tools.
+ ${t("sidebar.empty.tagline")}
" +
- "npx pi-for-excel-proxy, then try again. " +
- `Step-by-step guide →`;
+ `${t("provider.cors_error")} npx pi-for-excel-proxy${t("provider.cors_error.retry")} ${t("provider.proxy_gate.guide")}`;
} else {
- errorEl.textContent = msg || "Login failed";
+ errorEl.textContent = msg || t("provider.login_failed");
}
errorEl.hidden = false;
} finally {
- oauthBtn.textContent = `Login with ${label}`;
+ oauthBtn.textContent = t("provider.login_with", { label });
oauthBtn.style.opacity = "1";
}
})();
@@ -626,7 +617,7 @@ export function buildProviderRow(
disconnectBtn.addEventListener("click", (e) => {
e.stopPropagation();
void (async () => {
- disconnectBtn.textContent = "Disconnecting…";
+ disconnectBtn.textContent = t("provider.disconnecting");
disconnectBtn.disabled = true;
disconnectBtn.style.opacity = "0.7";
errorEl.hidden = true;
@@ -640,10 +631,10 @@ export function buildProviderRow(
onDisconnected?.(row, id, label);
} catch (err: unknown) {
const msg = getErrorMessage(err);
- errorEl.textContent = msg ? `Failed to disconnect: ${msg}` : "Failed to disconnect";
+ errorEl.textContent = msg ? t("provider.disconnect_failed_msg", { msg }) : t("provider.disconnect_failed");
errorEl.hidden = false;
} finally {
- disconnectBtn.textContent = `Disconnect ${label}`;
+ disconnectBtn.textContent = t("provider.disconnect", { label });
disconnectBtn.disabled = false;
disconnectBtn.style.opacity = "1";
}
@@ -664,7 +655,7 @@ export function buildProviderRow(
}
const key = normalized.key;
- saveBtn.textContent = "Testing…";
+ saveBtn.textContent = t("provider.testing");
saveBtn.style.opacity = "0.7";
errorEl.hidden = true;
try {
@@ -675,10 +666,10 @@ export function buildProviderRow(
expandedRef.current = null;
} catch (err: unknown) {
const msg = getErrorMessage(err);
- errorEl.textContent = msg ? `Failed to save key: ${msg}` : "Failed to save key";
+ errorEl.textContent = msg ? t("provider.save_failed_msg", { msg }) : t("provider.save_failed");
errorEl.hidden = false;
} finally {
- saveBtn.textContent = "Save";
+ saveBtn.textContent = t("provider.save");
saveBtn.style.opacity = "1";
}
})(); });
diff --git a/src/ui/proxy-banner.ts b/src/ui/proxy-banner.ts
index 367cb04a..ac91bbd0 100644
--- a/src/ui/proxy-banner.ts
+++ b/src/ui/proxy-banner.ts
@@ -5,6 +5,7 @@
* unavailable. Expands inline with quick setup guidance.
*/
+import { t } from "../language/index.js";
import { AlertTriangle, Check, Copy, lucide } from "./lucide-icons.js";
const PROXY_COMMAND = "npx pi-for-excel-proxy";
@@ -43,14 +44,14 @@ export function createProxyBanner(): ProxyBannerHandle {
warningIcon.setAttribute("aria-hidden", "true");
const textLabel = document.createElement("span");
- textLabel.textContent = "Proxy not running · some features won't work.";
+ textLabel.textContent = t("proxy-banner.warning");
text.append(warningIcon, textLabel);
const action = document.createElement("button");
action.type = "button";
action.className = "pi-proxy-banner__action";
- action.textContent = "How to fix →";
+ action.textContent = t("proxy-banner.action");
topRow.append(text, action);
@@ -60,7 +61,7 @@ export function createProxyBanner(): ProxyBannerHandle {
const detailsIntro = document.createElement("p");
detailsIntro.className = "pi-proxy-banner__details-text";
- detailsIntro.textContent = "Run this command in a terminal and keep that window open:";
+ detailsIntro.textContent = t("proxy-banner.intro");
const codeRow = document.createElement("div");
codeRow.className = "pi-proxy-banner__code";
@@ -76,14 +77,14 @@ export function createProxyBanner(): ProxyBannerHandle {
const renderCopyIcon = (): void => {
copyButton.replaceChildren(lucide(Copy));
- copyButton.title = "Copy command";
- copyButton.setAttribute("aria-label", "Copy command");
+ copyButton.title = t("proxy-banner.copyCommand");
+ copyButton.setAttribute("aria-label", t("proxy-banner.copyCommand"));
};
const renderCopiedIcon = (): void => {
copyButton.replaceChildren(lucide(Check));
- copyButton.title = "Copied";
- copyButton.setAttribute("aria-label", "Copied");
+ copyButton.title = t("proxy-banner.copied");
+ copyButton.setAttribute("aria-label", t("proxy-banner.copied"));
};
renderCopyIcon();
@@ -115,14 +116,14 @@ export function createProxyBanner(): ProxyBannerHandle {
const hint = document.createElement("p");
hint.className = "pi-proxy-banner__hint";
- hint.textContent = "Open Terminal · paste · press Enter · type y and Enter if prompted · leave open";
+ hint.textContent = t("proxy-banner.hint");
const guideLink = document.createElement("a");
guideLink.className = "pi-proxy-banner__link";
guideLink.href = INSTALL_GUIDE_URL;
guideLink.target = "_blank";
guideLink.rel = "noopener noreferrer";
- guideLink.textContent = "No Node.js? See install guide →";
+ guideLink.textContent = t("proxy-banner.guideLink");
details.append(detailsIntro, codeRow, hint, guideLink);
@@ -130,7 +131,7 @@ export function createProxyBanner(): ProxyBannerHandle {
const shouldOpen = details.hidden;
details.hidden = !shouldOpen;
root.classList.toggle("is-open", shouldOpen);
- action.textContent = shouldOpen ? "Hide steps" : "How to fix →";
+ action.textContent = shouldOpen ? t("proxy-banner.hideSteps") : t("proxy-banner.action");
});
root.append(topRow, details);
@@ -142,7 +143,7 @@ export function createProxyBanner(): ProxyBannerHandle {
if (!shouldShow) {
details.hidden = true;
root.classList.remove("is-open");
- action.textContent = "How to fix →";
+ action.textContent = t("proxy-banner.action");
}
};
diff --git a/src/ui/render-csv-table.ts b/src/ui/render-csv-table.ts
index 55fe60bb..5a47e8d9 100644
--- a/src/ui/render-csv-table.ts
+++ b/src/ui/render-csv-table.ts
@@ -26,7 +26,7 @@ async function copyToClipboard(csv: string, btn: HTMLButtonElement): Promise