- 人工智能
- AI 应用
- 桌面应用
- 交互助手
【免费下载链接】ClawX
ClawX is a desktop app that provides a graphical interface for OpenClaw AI agents. It turns CLI-based AI orchestration into a desktop experience without using the terminal. China website is https://clawx.com.cn.
TokenDance 是一个多模型网关提供商,在 ClawX 中作为 OpenAI 兼容提供商接入。本文围绕任务规格 restrict-tokendance-to-chinese-ui.md 展开,深入讲解 ClawX 如何通过声明式界面语言可用性元数据,让 TokenDance 仅在中文界面下出现在"添加提供商"目录中,同时保证已配置的 TokenDance 账户在切换语言后仍可管理与使用。读完本文,你将掌握这套语言门控机制的完整数据流、底层实现位置、边界约束与测试验证方式,并可直接在 ClawX 代码库中追踪每个关键环节。
需求背景:为什么 TokenDance 只对中文界面开放发现入口
TokenDance 作为面向特定用户群体的网关服务,ClawX 希望其新配置入口(add-provider 目录)只出现在中文界面中,而英文、日文、俄文等界面不向用户暴露这一选项。这一需求不是简单的 UI 隐藏,而是有一套明确的行为契约,见任务规格的expectedUserBehavior:
- 当界面语言为英语、日语或俄语时,TokenDance不出现在添加提供商对话框中;
- 当界面语言为中文时,TokenDance出现在添加提供商对话框中,且从设置页切换语言后该变化即时生效;
- 已经配置好的 TokenDance 账户,在界面切换到非中文后仍然可见、可管理,不会被删除、禁用或隐藏。
这与配套规则 tokendance-oauth-provider.md 中的约束一致:
TokenDance discovery is available only when the ClawX interface language resolves to Chinese. English, Japanese, Russian, and unsupported fallback locales must not show TokenDance in the add-provider catalog. This presentation gate must remain declarative and reactive to language changes; it must not delete, disable, or hide an already configured TokenDance account.
也就是说,这套门控是纯表现层(presentation)约束,它只影响"发现"(discovery),绝不触及账户数据、密钥或运行时行为。
核心机制:availableInLanguages声明式元数据
整个语言门控的基石是前端提供商元数据中新增的availableInLanguages字段。在 src/lib/providers.ts 的ProviderTypeInfo接口中,该字段被明确定义并附有语义注释:
/** Limits discovery in the add-provider UI without affecting configured accounts. */ availableInLanguages?: readonly LanguageCode[];- 作用范围:仅限制添加提供商 UI 中的"发现",不影响已配置账户;
- 缺省语义:未声明该字段的提供商在所有语言下都可被发现(见下文
isProviderAvailableForLanguage的实现); - 值类型:
LanguageCode数组,来自 shared/language.ts 中定义的SUPPORTED_LANGUAGE_CODES = ['en', 'zh', 'ja', 'ru']。
TokenDance 的元数据声明位于同一文件的PROVIDER_TYPE_INFO列表中:
{ id: 'tokendance', name: 'TokenDance', icon: 'TD', placeholder: 'your-tokendance-api-key', model: 'Multi-Model', requiresApiKey: true, isOAuth: true, supportsApiKey: true, defaultBaseUrl: 'https://tokendance.space/gateway/v1', defaultModelId: 'qwen3.8-max', showModelId: true, modelIdPlaceholder: 'qwen3.8-max', apiKeyUrl: 'https://tokendance.space/keys', docsUrl: 'https://tokendance.space/docs/ai-integration', availableInLanguages: ['zh'], }availableInLanguages: ['zh']即宣告:TokenDance 仅在中文界面下可被发现。除语言门控外,这条元数据还携带了 TokenDance 的运行参数:网关地址https://tokendance.space/gateway/v1、默认模型qwen3.8-max、同时支持 OAuth 登录与 API Key 两种接入方式(isOAuth: true且supportsApiKey: true)。
语言解析:resolveSupportedLanguage如何归一化中文区域变体
由于用户界面语言可能携带区域后缀(如zh-CN),门控判断不能做简单的字符串相等比较。ClawX 在 shared/language.ts 中提供了resolveSupportedLanguage函数,将任意 locale 归一到四种受支持语言之一:
function normalizeLocale(locale: string | null | undefined): string { return locale?.trim().toLowerCase().replaceAll('_', '-') ?? ''; } export function resolveSupportedLanguage( locale: string | null | undefined, fallback: LanguageCode = 'en', ): LanguageCode { const normalizedLocale = normalizeLocale(locale); if (!normalizedLocale) { return fallback; } const [baseLanguage] = normalizedLocale.split('-'); return SUPPORTED_LANGUAGE_CODE_SET.has(baseLanguage) ? (baseLanguage as LanguageCode) : fallback; }关键点:
- 先做归一化:去除首尾空白、转小写、把下划线
_替换为连字符-(兼容zh_CN这类写法); - 再取连字符前的基础语言码:
zh-CN、zh-TW、zh_HK都会解析为zh,因此中文区域变体被统一识别; - 未支持的 locale 回退到
en:这意味着任何无法解析的界面语言都会落入英语,TokenDance 自然不会出现在目录中。
这一"基础语言码 + 回退"策略正是任务规格 acceptance 中所要求的"handles normalized Chinese locale variants"(处理归一化的中文区域变体)。
判断函数:isProviderAvailableForLanguage的过滤规则
在 src/lib/providers.ts 中,门控判断被封装为纯函数,便于复用与单元测试:
export function isProviderAvailableForLanguage( provider: Pick<ProviderTypeInfo, 'availableInLanguages'>, language: string | null | undefined, ): boolean { if (!provider.availableInLanguages?.length) { return true; } return provider.availableInLanguages.includes(resolveSupportedLanguage(language)); }逻辑非常简洁:
- 未声明
availableInLanguages(或为空数组)的提供商 → 恒为true,不受语言影响,保证绝大多数提供商(Anthropic、OpenAI、Google、DeepSeek 等)在所有语言下行为不变; - 声明了该字段的提供商,则将传入的语言字符串交给
resolveSupportedLanguage归一化后,判断是否落在允许列表内。
TokenDance 只允许zh,因此zh/zh-CN返回true,en/ja/ru以及任意不支持的语言都返回false。
目录过滤:添加提供商对话框中的响应式应用
元数据与判断函数最终在 UI 层落地。在 src/components/settings/ProvidersSettings.tsx 的availableTypes计算中,目录过滤同时应用了"暂时隐藏"与"语言不可用"两条规则:
const availableTypes = PROVIDER_TYPE_INFO.filter((type) => { // Skip providers that are temporarily hidden or unavailable in this UI language. if (type.hidden) return false; if (!isProviderAvailableForLanguage(type, i18n.resolvedLanguage || i18n.language)) return false; // ... 其余互斥性过滤(MiniMax、Z.AI 等) });这里有两个值得注意的工程细节:
- 响应式(reactive)语言来源:传入的是
i18n.resolvedLanguage || i18n.language,即当前生效的界面语言。由于该表达式位于组件渲染路径中,当用户在设置页切换语言时,availableTypes会随 i18n 状态变更自动重新计算——这正是任务规格expectedUserBehavior中"包括在设置中切换语言之后"TokenDance 仍能正确出现/消失的实现保障; - 门控与数据隔离:过滤只影响"添加到目录",
ProviderTypeInfo中 TokenDance 的类型注册、图标资源(见 src/assets/providers/index.ts 与tokendance.svg)以及已配置账户的卡片渲染逻辑都不在此路径上,从而保证已有账户不受影响。
从源码结构看,该过滤发生在"候选提供商枚举"阶段,与账户是否已配置无关,因此不存在"因为语言变化而移除账户"的可能。
边界约束:什么不做,比做什么更重要
任务规格用独立的Out Of Scope小节明确了语言门控的边界,这些约束在实现与后续维护中必须被尊重:
| 不做的事情 | 原因 |
|---|---|
| 从 Main 或共享提供商注册表移除 TokenDance | 已有账户的运行时同步仍依赖该注册表 |
| 用户切换语言时删除、禁用或迁移已有 TokenDance 账户 | 用户仍需要管理账户、接收本地化恢复指引 |
| 改变 TokenDance 的 OAuth、校验、恢复或运行时传输行为 | 语言门控是纯表现层改动 |
| 移除已有账户所需的 TokenDance 恢复翻译 | 切换到非中文界面的用户仍可能需要恢复指引 |
配套规则 tokendance-oauth-provider.md 进一步强调:该表现层门控"must not delete, disable, or hide an already configured TokenDance account, because users still need to manage that account and receive localized recovery guidance after switching languages"。
为了支撑"切换语言后账户仍可管理"这一承诺,TokenDance 的恢复指引必须覆盖所有支持的语言。以 shared/i18n/locales/zh/chat.json 为例,三类文档化恢复动作均有本地化文案:
"top_up_balance": "TokenDance 账户余额不足。请充值后重试此请求。", "reauthorize_api_key": "TokenDance 密钥已失效。请打开模型设置并重新授权 TokenDance。", "api_key_quota": "TokenDance 密钥已达到周期额度。请等待额度刷新,或重新授权 TokenDance。"这三类动作top_up_balance/reauthorize_api_key/api_key_quota对应ProviderRecoveryAction类型(见 src/lib/providers.ts),由 Main 侧的密钥校验识别并从响应头中提取,再交由渲染层 src/pages/Chat/AcpErrorBanner.tsx 替换为本地化指引。
实现背后的完整语境:TokenDance 的 OAuth 与运行时接入
语言门控只是 TokenDance 接入方案的呈现层部分。为了理解"发现入口"背后承接的是什么,简要梳理配套任务 add-tokendance-oauth-provider.md 与规则文档中的关键事实:
- 授权方式:Authorization Code + S256 PKCE,产出的是API Key而非可续期的 OAuth token,密钥经 API-key 密钥通道持久化;
- OAuth 细节:在 Electron Main 中持有 verifier,回调走随机
127.0.0.1loopback 端口,校验一次性回调的 flow 标识,代码交换限时十分钟; - 归属标识:
https://clawx.com.cn同时用于 OAuth 的app_url参数和每次模型请求的X-App-URL请求头,保证请求归属覆盖密钥归属; - 运行时配置:网关地址
https://tokendance.space/gateway/v1,协议openai-completions,默认模型qwen3.8-max。
这些常量可以在 electron/utils/tokendance-oauth.ts 中直接核对,例如:
export const TOKENDANCE_APP_URL = 'https://clawx.com.cn'; export const TOKENDANCE_GATEWAY_BASE_URL = 'https://tokendance.space/gateway/v1'; export const TOKENDANCE_DEFAULT_MODEL = 'qwen3.8-max'; export const TOKENDANCE_APP_HEADER = { 'X-App-URL': TOKENDANCE_APP_URL } as const;需要强调的是:这些行为明确属于Out Of Scope——语言门控任务不改变上述任何传输与授权行为,理解它们只是为了把握门控之外、账户配置完成后仍然持续运转的运行时链路。
测试验证:单元测试如何锁定语言门控行为
语言门控的正确性由单元测试直接锁定。在 tests/unit/providers.test.ts 中,limits TokenDance discovery to Chinese interface locales用例对判断函数做了完整覆盖:
it('limits TokenDance discovery to Chinese interface locales', () => { const tokenDance = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'tokendance'); const openAi = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'openai'); expect(tokenDance).toBeDefined(); expect(isProviderAvailableForLanguage(tokenDance!, 'zh')).toBe(true); expect(isProviderAvailableForLanguage(tokenDance!, 'zh-CN')).toBe(true); expect(isProviderAvailableForLanguage(tokenDance!, 'en')).toBe(false); expect(isProviderAvailableForLanguage(tokenDance!, 'ja')).toBe(false); expect(isProviderAvailableForLanguage(tokenDance!, 'ru')).toBe(false); expect(isProviderAvailableForLanguage(tokenDance!, 'unsupported')).toBe(false); expect(isProviderAvailableForLanguage(openAi!, 'en')).toBe(true); });该用例验证了任务规格 acceptance 中的关键条目:
- 中文可见:
zh与带区域后缀的zh-CN均返回true(归一化区域变体); - 非中文不可见:
en、ja、ru均返回false; - 不支持的语言回退:
unsupported返回false,说明未支持 locale 经回退到en后同样被门控; - 对照组不回归:未声明
availableInLanguages的 OpenAI 在en下仍返回true,证明该机制不影响其他提供商。
同一测试文件中includes TokenDance OAuth with ClawX request attribution用例还锁定了元数据本身:
expect(PROVIDER_TYPE_INFO).toEqual(expect.arrayContaining([ expect.objectContaining({ id: 'tokendance', isOAuth: true, supportsApiKey: true, defaultBaseUrl: 'https://tokendance.space/gateway/v1', defaultModelId: 'qwen3.8-max', availableInLanguages: ['zh'], }), ]));除此之外,任务规格的requiredTests还要求以下测试覆盖相关面,可作为继续深入阅读的索引:
- tests/unit/tokendance-oauth.test.ts:OAuth 协议细节(PKCE、回调 flow 校验、超时、取消);
- tests/unit/tokendance-openclaw-recovery.test.ts:恢复动作在运行时错误文本中的保留与分类;
- tests/unit/provider-validation.test.ts:Main 侧密钥校验行为;
- tests/unit/provider-runtime-sync.test.ts 与 tests/unit/provider-store-init.test.ts:运行时时同步与存储初始化;
- tests/e2e/provider-lifecycle.spec.ts:在 Electron E2E 层覆盖"英文隐藏、中文可见"的完整用户行为。
验收标准一览:如何判断实现是否达标
任务规格的acceptance小节给出了完整验收清单,浓缩了本节全部讨论:
- 提供商可用性元数据将 TokenDance 标记为仅中文界面可用,并处理归一化的中文区域变体;
- 添加提供商目录在过滤提供商类型时应用当前响应式界面语言;
- 非中文界面无法通过提供商对话框发起新的 TokenDance 配置;
- 已存在的 TokenDance 卡片与运行时行为,不因界面语言变化而被移除或禁用;
- 渲染层代码不新增直接 IPC 或 Gateway HTTP 调用(门控完全在渲染层元数据与 i18n 内完成);
- README 各语言翻译对"语言门控的 TokenDance 入口"描述一致;
- 聚焦测试、harness 校验、通信回放与对比、类型检查与 lint 全部通过。
其中第 5 条尤其值得注意:语言门控属于纯前端表现逻辑,由src/lib/providers.ts的元数据与纯函数、src/components/settings/ProvidersSettings.tsx的响应式过滤共同完成,不涉及任何进程边界调用。
小结
ClawX 通过"声明式元数据 + 纯函数判断 + 响应式目录过滤"三层结构,实现了 TokenDance 提供商的中文界面限定发现:availableInLanguages: ['zh']声明约束,resolveSupportedLanguage归一化语言变体,isProviderAvailableForLanguage封装判断逻辑,ProvidersSettings.tsx在渲染路径中随 i18n 语言即时应用过滤。与此同时,明确的 Out Of Scope 边界保证了已配置账户的管理与运行时行为、OAuth 授权链路和恢复翻译均不受语言切换影响,最终由单元测试与 E2E 测试将这套行为契约固化为可回归验证的工程事实。
- 人工智能
- AI 应用
- 桌面应用
- 交互助手
【免费下载链接】ClawX
ClawX is a desktop app that provides a graphical interface for OpenClaw AI agents. It turns CLI-based AI orchestration into a desktop experience without using the terminal. China website is https://clawx.com.cn.
相关推荐
Axure RP 界面语言定制方案:5分钟实现完整中文体验
Axure RP 界面语言定制方案:5分钟实现完整中文体验 还在为Axure RP界面中英文混杂而烦恼吗?Axure RP 简体中文界面定制工具提供了完整的解决
FanControl中文界面完整配置指南:轻松实现多语言散热控制
FanControl中文界面完整配置指南:轻松实现多语言散热控制 还在为英文风扇控制软件的操作界面感到困扰吗?想要快速调节PC散热系统却卡在语言障碍上?FanC
桌面应用智能硬件蓝鲸PaaS双环境部署模型:stag与prod环境的完整玩法
蓝鲸PaaS双环境部署模型:stag与prod环境的完整玩法 蓝鲸智云 PaaS 平台(BlueKing PaaS,蓝鲸PaaS)是一个开放式的 SaaS 应用
后端云原生微服务前端企业应用开发者门户
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考