OmniRoute i18n 国际化工程实战指南:42 语言、增量翻译流水线与全链路质量门禁
发布时间:2026/9/10 16:20:20来源:尧图网络
OmniRoute i18n 国际化工程实战指南42 语言、增量翻译流水线与全链路质量门禁【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150 free), 1200 models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline Copilot. Quota-aware auto-fallback, RTKCaveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550 contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute导读OmniRoute 是一个面向 AI 网关场景的开源项目其国际化体系覆盖了仪表盘 UI、命令行CLI、项目文档三大表面当前仓库中共配置了 42 个语言环境config/i18n.json中可见 42 个 locale 条目并针对阿拉伯语、波斯语、希伯来语、乌尔都语提供 RTL 支持。本篇指南以仓库自带的 i18n 工程文档docs/i18n/fi/docs/guides/I18N.md 及其英文主文档 docs/guides/I18N.md为核心骨架结合 scripts/i18n 目录下的真实脚本与 src/i18n 的运行时实现系统讲解其架构、增量翻译流水线、新增语言流程、验证与 QA 工具链。读完本文你将掌握如何为 OmniRoute 添加一种新语言、如何驱动哈希增量翻译、如何用校验脚本守住翻译质量以及如何将 i18n 检查接入 CI。说明FinnishSuomifi版本是英文原版 I18N 指南的翻译镜像之一下文以内容更全的英文主文档为准并结合fi版中保留的 Quick Reference、架构、验证命令等核心内容。一、i18n 架构总览三层表面与单一事实来源OmniRoute 的国际化并不是一套字典走天下而是三个各自独立、又共享同一份 locale 清单的子系统表面承载文件说明仪表盘 UINext.jssrc/i18n/messages/{locale}.json约 2800 个键的英文源en.json加 41 个翻译文件命令行 CLIbin/cli/locales/{locale}.json42 个开箱即用的 CLI 语言目录文档 Docsdocs/i18n/{locale}/docs/...每个 locale 一套翻译后的 Markdown 镜像单一事实来源是 config/i18n.json。该文件声明了defaulten、rtl[ar,fa,he,ur]、uiOnly[en]、docsExcluded[en]以及完整的locales数组。每个 locale 条目形如{ code: fi, label: FI, name: Suomi, native: Suomi, english: Finnish, flag: }注意 src/i18n/config.ts 文件头部明确写着SOURCE OF TRUTH: config/i18n.json… Keep this file as a thin typed adapter — do NOT add hand-maintained locale lists here.也就是说src/i18n/config.ts只是一个瘦适配层它通过 JSON import 读取config/i18n.json再导出LOCALES、LANGUAGES、RTL_LOCALES、LOCALE_ALIASES、LOCALE_COOKIE等常量供 UI 使用。从源码结构看新增语言唯一需要手改的声明位置就是config/i18n.json其余表面都由脚本驱动。1.1 仪表盘 UI 的运行时语言解析流程翻译文档中给出了仪表盘的 4 步运行时流程而 src/i18n/request.ts 给出了完整实现用户在界面选择语言 → 写入NEXT_LOCALEcookie由LOCALE_COOKIE NEXT_LOCALE定义request.ts通过resolveRequestedLocale()解析 locale优先级为 cookie →x-locale请求头 → 默认en动态 import 加载messages/{locale}.json组件通过useTranslations(namespace)t(key)取字符串。实现层面有两个值得注意的细节EN 兜底合并当激活 locale 不是en时request.ts会先做一次递归的deepMergeFallback()把英文源作为缺失键的兜底随后再做一次命名空间级浅合并保证新增命名空间如cliCode、cliAgents、acpAgents在尚未翻译的 39 个非 EN/非 pt-BR locale 中能以英文显示。__MISSING__:哨兵值sync-ui-keys.mjs回填未翻译键时写入__MISSING__:english value见 request.ts 中PLACEHOLDER_PREFIX的定义deepMergeFallback会把这类值当作缺失处理从而让干净的英文值胜出。原型污染防护deepMergeFallback与setNestedValue都会跳过__proto__、constructor、prototype键防止由构造的 locale 消息树引发原型污染。resolveRequestedLocale()还会消费LOCALE_ALIASES——例如旧值in曾被错误地用作印地语/印尼语代码如今通过 alias 映射到id因此用户保存的NEXT_LOCALEin或OMNIROUTE_LANGin仍能正确解析。1.2 CLI 层的 i18n独立于仪表盘CLI 有自己独立的 i18n 层源文件为bin/cli/i18n.mjs所有面向用户的字符串都经过t(module.key, vars)目录文件位于 bin/cli/locales42 个开箱即用任何缺失键回退到en因此部分翻译的 locale 也是合法的locale 来源清单同样共享config/i18n.json。CLI 的 locale 选择顺序首个命中优先优先级来源示例1--lang参数omniroute --lang de status2OMNIROUTE_LANG环境变量OMNIROUTE_LANGja omniroute providers3LC_ALL系统环境从终端 locale 自动探测4LC_MESSAGES系统环境从终端 locale 自动探测5LANG系统环境从终端 locale 自动探测6兜底en带下划线的代码pt_BR会被归一化为连字符形式pt-BRlocale 代码必须匹配/^[a-zA-Z0-9-]$/以此拒绝路径穿越攻击。语言偏好可通过以下命令持久化到~/.omniroute/.env原子写入在 CLI 引导阶段加载# 设置语言并持久化跨会话生效 omniroute config lang set pt-BR # 查看当前语言 omniroute config lang get # 列出全部 42 种可用语言 omniroute config lang list # JSON 输出 omniroute config lang list --output json # 仅本次调用生效不写入配置文件 omniroute --lang de providers list从仓库现状看42 个 CLI locale 文件中en、pt-BR为完整翻译bn、gu、he、mr、ms、phi、sw、ta、te、ur为纯脚手架所有键回退到en其余 30 个 locale 翻译了commonprogram两组键。二、翻译流水线从 Google 免费 API 到哈希增量 LLM 翻译2.1 推荐流水线v3.8.0 起哈希增量翻译器当前推荐的主流水线是 scripts/i18n/run-translation.mjs由 npm 脚本驱动。它基于 OpenAI 兼容的 LLM 端点典型场景是经 OmniRoute Cloud 走cx/gpt-5.4-mini一类模型其核心特性是增量与确定性# 运行翻译增量——只处理源文件有变化的文档 npm run i18n:run # 只处理一个 locale npm run i18n:run -- --localept-BR # 只处理指定文件逗号分隔、相对仓库根的路径 npm run i18n:run -- --filesCLAUDE.md,docs/architecture/ARCHITECTURE.md # 强制全量重译成本高 npm run i18n:run -- --force # 预演——不发 API 请求、不写任何文件 npm run i18n:run:dry # CI 门禁——状态漂移时以非零码退出 npm run i18n:check状态文件是提交进仓库的 .i18n-state.json为每个源文件和每个已产出的目标文件保存 SHA-256 哈希。重跑时只重译源哈希变化了或目标文件缺失的文件因此i18n:check对应 scripts/i18n/check-translation-drift.mjs做漂移检测时完全不需要调用 API确定性且廉价。状态重引导--adopt如果.i18n-state.json丢失或你只改了链接列表、数字这类无需重译的文本可以运行npm run i18n:run -- --adopt # 从磁盘上的镜像重建状态不发 API、不写任何 .md npm run i18n:run -- --adopt --targets-only--targets-only表示只对磁盘上的镜像重新哈希、保留全部source_hash这样机械性的镜像重写不会表现为target changed而真正的源漂移仍会被报告。从 run-translation.mjs 的注释看--adopt与--targets-only正是为这两类场景设计的。后端配置通过.env配置永不提交变量用途OMNIROUTE_TRANSLATION_API_URLOpenAI 兼容的 base URL如…/v1OMNIROUTE_TRANSLATION_API_KEYbearer token日志中脱敏OMNIROUTE_TRANSLATION_MODEL模型 id如cx/gpt-5.4-miniOMNIROUTE_TRANSLATION_TIMEOUT_MS可选默认60000OMNIROUTE_TRANSLATION_CONCURRENCY可选默认4run-translation.mjs自带一个极简的.env加载器不引入 dotenv 依赖已设置的环境变量优先因此 shell / CI 仍可覆盖。输出形状每份翻译文件顶部是# heading (native)行、一条 Languages: …语言栏、一个---分隔符然后才是翻译正文。这一版式与scripts/check/check-docs-sync.mjs对llm.txt、CHANGELOG.md镜像的既有约束保持一致翻译文件在记录哈希前还会经过 Prettiermarkdown parser格式化避免 lint-staged 的 Prettier 二次格式化与状态文件打架。2.2 旧流水线已弃用v3.10 移除文档中明确标注了两条旧流水线为deprecated将在 v3.10 移除Google 免费 API 生成器scripts/i18n/generate-multilang.mjs约 888 行LLM 文档翻译器scripts/i18n/i18n_autotranslate.py。旧生成器的四个模式与行为当前仍存在于仓库但不应再用于 UI 字符串node scripts/i18n/generate-multilang.mjs [messages|readme|docs|all]模式行为messages从en.json补齐src/i18n/messages/{locale}.json的缺失键readme把根目录README.md翻译为各语言的README.{code}.mddocs把DOC_SOURCE_FILES翻译到docs/i18n/{locale}/{docName}all依次执行以上三种旧生成器的工程细节仍值得了解体现了翻译流水线的通用设计文本保护翻译前先掩码代码块、行内代码、Markdown 链接/图片、HTML 标签、表格与 ICU 占位符{count}、{value}、{total}等翻译后再还原分块批处理用__OMNIROUTE_I18N_SEPARATOR__连接多条字符串单请求上限 1800 字符最小化 API 调用次数内存缓存会话内相同字符串不重复调用指数退避重试429/5xx 最多重试 5 次延迟为 300ms × 尝试次数超时单请求 20 秒跳过已存在文件目标文件已存在则不覆盖。支持 UI 字符串翻译的受支持路径是 LLM 后端# 补齐每个 locale 目录中缺失/占位的 UI 键 npm run i18n:sync-ui -- --translate-markers --batch-size40根目录README.{code}.md变体目前尚无替代流水线readme模式是唯一的例外。三、新增一种语言一条命令铺平所有表面老版本文档要求手动编辑generate-multilang.mjs的LOCALE_SPECS数组fi版芬兰语文档仍保留这一旧流程而英文主文档与当前仓库已演进为add-locale.mjs单命令流程——从 scripts/i18n/add-locale.mjs 的实现看它会一次性把新 locale 写入config/i18n.json、旗帜文件docs/assets/flags/cc.svg、UI 目录src/i18n/messages/code.json脚手架后经sync-ui-keys --translate-markers填充、文档镜像docs/i18n/code/**、CLI 目录bin/cli/locales/code.json、README 旗帜链接、docs/i18n/README.md行、docs/guides/I18N.md行以及可选的 site 目录。# 需要 .env 中配置 OMNIROUTE_TRANSLATION_API_URL / _API_KEY / _MODEL node scripts/i18n/add-locale.mjs --codeel --englishGreek --nativeΕλληνικά --flag # 印度语系共用 in.svg 旗帜文件 node scripts/i18n/add-locale.mjs --codekn --englishKannada --nativeಕನ್ನಡ --flag --flag-filein.svg # 只预览不执行 node scripts/i18n/add-locale.mjs --codeel --englishGreek --nativeΕλληνικά --flag --dry-runadd-locale.mjs还支持--aliases、--rtl、--docscore|all、--filescsv、--cli-full等可选参数。新增后执行验证node --import tsx/esm --test tests/unit/i18n-locale-surfaces-parity.test.ts npm run i18n:check-ui-coverage npm run i18n:check-ratio npm run check:docs-all npm run check:cli-i18n其中tests/unit/i18n-locale-surfaces-parity.test.ts双向守护配置的文档 locale 必须有一行记录且每行记录必须映射回一个已配置的文档 locale。核心原则再次重申config/i18n.json是声明新语言的唯一位置src/i18n/config.ts是类型化适配层禁止手改。四、验证与 QA 工具链四层检查保住翻译质量4.1 翻译校验器 validate_translation.pyscripts/i18n/validate_translation.py 将任意 locale JSON 与en.json对比并报告问题# 快速检查仅计数 python3 scripts/i18n/validate_translation.py quick -l cs # 输出示例 # Missing: 0 # Untranslated: 0 # Ignored (UNTRANSLATABLE_KEYS): 236 # 按分类输出差异 python3 scripts/i18n/validate_translation.py diff common -l cs python3 scripts/i18n/validate_translation.py diff settings -l cs # 导出 CSV / Markdown python3 scripts/i18n/validate_translation.py csv -l cs report.csv python3 scripts/i18n/validate_translation.py md -l cs report.md # 完整报告默认 python3 scripts/i18n/validate_translation.py -l cs检测四类问题Missing keys——en.json有、locale 文件没有的键Extra keys——locale 文件有、en.json没有的键Untranslated keys——locale 值与英文源相同的键排除 allowlistPlaceholder mismatches——ICU 占位符在源与译文间不匹配。退出码约定码含义0OK1一般错误2缺少字符串硬错误3未翻译警告软错误环境变量可用TRANSLATION_LANGcs或-l cs参数二选一。4.2 代码键检查器 check_translations.pyscripts/i18n/check_translations.py 扫描src/**/*.tsx与src/**/*.ts中的useTranslations()调用验证所有被引用的键都存在于en.json# 基础检查 python3 scripts/i18n/check_translations.py # 详细输出 python3 scripts/i18n/check_translations.py --verbose # 自动修复把缺失键补进 en.json python3 scripts/i18n/check_translations.py --fix4.3 静态 QA 报告 generate-qa-checklist.mjsscripts/i18n/generate-qa-checklist.mjs 扫描 Next.js 页面文件输出 i18n 风险指标 Markdown 报告node scripts/i18n/generate-qa-checklist.mjs检查项包括固定宽度 class 的使用溢出风险、方向性 left/right classRTL 风险、易裁剪clipping模式、与en.json相比的 locale 奇偶性缺失/多余键、以及es/fr/de/ja/ar等优先 locale 的 README 语言选择栏。输出为docs/reports/i18n-qa-checklist-{date}.md该目录当前未随仓库提交由脚本运行时生成。4.4 视觉 QA run-visual-qa.mjsPlaywrightscripts/i18n/run-visual-qa.mjs 通过 Playwright 对仪表盘所有路由在多种 locale 与多种视口下截图并评估页面健康度# 默认es, fr, de, ja, ar, zh-CN目标 localhost:20128 node scripts/i18n/run-visual-qa.mjs # 自定义 base URL 与 locale QA_BASE_URLhttp://staging.example.com QA_LOCALESde,fr node scripts/i18n/run-visual-qa.mjs # 自定义路由 QA_ROUTES/dashboard/settings,/dashboard/providers node scripts/i18n/run-visual-qa.mjs可检测文本溢出、元素裁剪、RTL 布局错位。输出docs/reports/i18n-visual-qa-{date}.md加 JSON 报告。4.5 术语一致性层glossary超出键奇偶与 ICU 合法性的第三道门禁仅靠键奇偶check-ui-keys-coverage.mjs和 ICU 合法性validate_translation.py无法发现语义漂移——例如同一个英文概念 provider 在中文里同时被译为提供商与提供者。为此仓库新增了术语表层文档注明由 #8038 引入从 zh-CN 起步scripts/i18n/glossary/zh-CN.json版本化的常见概念术语表provider、connection、routing、fallback、quota、context window、reasoning、tool call、cache、circuit breaker 等每个概念含canonical翻译与可选的synonyms列表——目录中出现任一同义词即标记为漂移scripts/i18n/glossary/protected-terms.json产品/提供商/模型/协议/CLI/环境变量/标识符名单OmniRoute、OAuth、MCP、A2A、DATA_DIR等必须逐字出现在任何翻译值中。它与untranslatable-keys.json粒度不同后者按整个键排除前者按概念检查任意值中出现的受保护词scripts/i18n/check-glossary-consistency.mjs导出纯函数checkGlossaryConsistency(localeMessages, glossary, protectedTerms)返回{ violations: [...] }——非规范词产生glossary-synonym违规受保护名被译错产生protected-term-altered违规。# 默认检查 zh-CN npm run i18n:check-glossary # 指定 locale、JSON 报告或非失败报告模式 node scripts/i18n/check-glossary-consistency.mjs --localezh-CN node scripts/i18n/check-glossary-consistency.mjs --localezh-CN --json node scripts/i18n/check-glossary-consistency.mjs --localezh-CN --report该检查在 CI 中作为i18n-glossary-zhcn作业运行与i18n-ui-coverage相同的门禁策略跳过 draft PRi18n 或代码变更时触发。仓库同时提供了 scripts/i18n/glossary/ko.json、scripts/i18n/glossary/zh-TW.json 与glossary-normalize.mjsrun-translation.mjs会调用它做翻译后术语归一化说明该层正在向更多语言扩展。五、不可翻译键白名单与手工编辑注意点scripts/i18n/untranslatable-keys.json 是应保持与英文源完全一致的键白名单用于避免validate_translation.py产生误报的 untranslated 警告。典型成员品牌/产品名landing.brandName、common.social-github技术术语/缩写health.cpu、mcpDashboard.pid、settings.aiICU/格式化字符串apiManager.modelsCount、health.millisecondsShort占位符值providers.openaiBaseUrlPlaceholder、cliTools.baseUrlPlaceholder协议名common.http、common.oauth、providers.oauth2Label导航分组sidebar.primarySection、sidebar.cliSection添加键只需编辑keys数组后重跑校验。文档还特别提醒docs/i18n/README.md已不再由脚本整体重生成而是由i18n:add-locale就地插入新 locale 行并手工维护其余编辑——任何手工编辑若想保留应写入docs/guides/I18N.md这类手写文档而不是自动生成的索引。六、CI 集成全 locale 并行校验.github/workflows/ci.yml 在每次 push 与 PR 上并行校验所有 localei18n-matrix作业——动态发现全部 locale 文件排除en.jsoni18n作业——对矩阵中的每个 locale 并行运行validate_translation.py quick -l langci-summary作业——汇总结果到仪表盘。# i18n-matrix: 发现语言 LANGS$(ls src/i18n/messages/*.json | xargs -n1 basename | sed s/.json$// | grep -v ^en$) # i18n: 校验每种语言 python3 scripts/i18n/validate_translation.py quick -l ${{ matrix.lang }}仪表盘输出形如## Translations | Metric | Value | |--------|------| | Languages checked | 30 | | Total untranslated | 0 | ✅ All translations complete此外CI 还挂载了i18n-check漂移门禁、i18n-ui-coverageUI 键覆盖与i18n-glossary-zhcn术语一致性等作业与 package.json 中i18n:*系列 npm 脚本一一对应npm 脚本底层脚本作用i18n:run/i18n:run:dryrun-translation.mjs增量文档翻译 / 预演i18n:checkcheck-translation-drift.mjs漂移检测无 API 调用i18n:sync-uisync-ui-keys.mjs补齐 UI 目录缺失键i18n:check-ui-coveragecheck-ui-keys-coverage.mjsUI 键覆盖检查i18n:check-ratiocheck-translation-ratio.mjs真实翻译比例棘轮与英文相同/占位符占比i18n:sync-barssync-language-bars.mjs重生成 语言栏i18n:add-localeadd-locale.mjs全表面新增语言i18n:check-glossarycheck-glossary-consistency.mjs术语一致性检查七、最佳实践与历史教训7.1 编辑翻译时的操作纪律永远先改en.json——它是事实来源运行npm run i18n:sync-ui -- --translate-markers旧流程为generate-multilang.mjs messages把新键传播到各 locale人工复核自动翻译——机器翻译只是起点提交前校验——python3 scripts/i18n/validate_translation.py quick -l lang若某键应保持英文更新untranslatable-keys.json。7.2 占位符安全ICU 占位符{count}、{value}、{total}、{seconds}必须逐字保留复数格式{count, plural, one {# model} other {# models}}必须保持结构完整校验器会自动检测占位符不匹配。7.3 代码中新增翻译键// 使用命名空间键 const t useTranslations(settings); t(cacheSettings); // 映射到 JSON 中的 settings.cacheSettings // 运行 check_translations.py 验证键存在 python3 scripts/i18n/check_translations.py --verbose7.4 RTL 注意事项ar、fa、he、ur是 RTL localeconfig/i18n.json的rtl数组当前版本较旧文档又新增了fa与ur两项避免硬编码left/rightCSS改用start/end逻辑属性run-visual-qa.mjs的视觉 QA 负责捕获 RTL 布局错位。7.5 历史教训已知问题in.json→hi.json修复生成器曾用已弃用的 Google 翻译代码in表示印地语正确应为 ISO 639-1 的hi产生了一个孤儿in.json。修复方式是改代码并删除孤儿文件2026-09-02 又移除了作为第二印尼语存活下来的in现在id声明aliases: [in]旧值自动解析到iddocs/i18n/README.md自动生成史该文件曾由generate-multilang.mjs docs整体重生成现改为add-locale就地插入行 手工维护并由i18n-locale-surfaces-parity.test.ts双向守护白名单外部化untranslatable-keys.json从validate_translation.py内联的 Python set 移出为独立 JSON 文件校验器运行时加载quick输出新增 Ignored 计数快速检查现在会显示来自untranslatable-keys.json的被忽略键数量Ignored (UNTRANSLATABLE_KEYS): per release。八、文件结构速查config/ └── i18n.json # 单一事实来源42 locale、RTL 集合、uiOnly/docsExcluded src/i18n/ ├── config.ts # 类型化适配层读取 config/i18n.json禁止手改 ├── request.ts # 运行时 locale 解析 EN 兜底合并 原型污染防护 ├── detectBrowserLocale.ts # 浏览器 locale 探测 ├── resolveRequestedLocale.ts # cookie/header 解析与 alias 映射 └── messages/ ├── en.json # 事实来源约 2800 键 ├── cs.json / de.json / … # 41 个翻译文件合计 42 bin/cli/ ├── i18n.mjs # CLI 的 t(module.key, vars) └── locales/ # 42 个 CLI locale 文件 scripts/i18n/ ├── run-translation.mjs # 推荐哈希增量文档翻译 ├── check-translation-drift.mjs # i18n:check 漂移门禁 ├── sync-ui-keys.mjs # UI 键补齐__MISSING__: 哨兵 ├── check-ui-keys-coverage.mjs # UI 键覆盖检查 ├── check-translation-ratio.mjs # 真实翻译比例棘轮 ├── add-locale.mjs # 全表面新增语言 ├── sync-language-bars.mjs # 语言栏同步 ├── generate-multilang.mjs # 旧 Google 翻译生成器deprecatedv3.10 移除 ├── i18n_autotranslate.py # 旧 LLM 翻译器deprecatedv3.10 移除 ├── validate_translation.py # 翻译校验器 ├── check_translations.py # 代码键检查器 ├── generate-qa-checklist.mjs # 静态 QA 报告 ├── run-visual-qa.mjs # Playwright 视觉 QA ├── check-glossary-consistency.mjs # 术语一致性 ├── untranslatable-keys.json # 不可翻译键白名单236 键量级 ├── glossary/ # 各语言术语表 protected-terms.json └── lib/ # docs-core-set / language-bar / translation-state 等 docs/ ├── guides/I18N.md # 手写 i18n 工程文档本主题主文档 ├── i18n/README.md # 自动维护的语言索引 ├── i18n/{locale}/docs/... # 42 个语言镜像fi 版即其中之一 └── diagrams/i18n-flow.mmd # 流水线架构图源文件 └── exported/i18n-flow.svg # 导出的架构图结语OmniRoute 的国际化工程是声明一次、处处生效的典型实践config/i18n.json是唯一事实来源add-locale.mjs一键把新语言铺到 UI、CLI、文档与站点表面哈希增量流水线让文档翻译变成确定性的增量任务而validate_translation.py、check_translations.py、术语表与 Playwright 视觉 QA 构成从键奇偶到语义一致性的完整质量门禁。无论你是要为本仓库新增语言、修复翻译漂移还是把类似的 i18n 工程思路迁移到自己的项目这套单一来源 脚本驱动 四层校验的组合都值得直接复用。【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150 free), 1200 models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline Copilot. Quota-aware auto-fallback, RTKCaveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550 contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网