npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

translation-hub-sdk

v0.3.33

Published

Runtime i18n SDK: zero-invasion DOM translation, glossary, pivot, micro-batch, dev editor

Readme

translation-hub-sdk

运行时国际化 SDK。零侵入接入:业务代码无需修改,SDK 自动扫描页面中文、批量翻译、缓存、术语命中。

i18n-jsautotranslate 实战经验中提取,替代其 DOM 集成层,同时支持直连 MTranServer 或走 Translation Hub server。

特性

  • 零侵入:业务代码不动一行,SDK 用 MutationObserver 自动扫描页面中文文本
  • 切换语言不丢原文:DOM 翻译后保留中文原文,可任意次切换语言
  • 微批处理 + 并发限制:100ms 窗口内合并请求,同时最多 4 个在途
  • Pivot 翻译:MTranServer 不支持非英语互译时,自动 zh → en → target(可关闭)
  • 术语表(Glossary):品牌名、专业术语统一翻译,命中后绕过翻译引擎
  • 混合文本切分:中英文混排时只翻译中文段,英文段保留原样;拼接时智能加空格
  • HTML 属性翻译:placeholder / title / aria-label / alt 自动翻译
  • SVG 文本翻译:自动扫描 SVG <text> 节点,ECharts 用 SVGRenderer 即可零侵入翻译图表文案
  • LocalStorage 缓存:按语言分桶 + 翻译失败不写入 + 静态 JSON 覆盖更新
  • 服务端译文同步:SDK 在 init/switchLanguage 时拉取 /runtime/overrides,让服务端修订立即生效
  • 语言检测:navigator.languages → 翻译引擎语言 ID,覆盖 70+ 浏览器语言变体
  • 多 Provider:MTranServer / Edge 浏览器公共 API / Translation Hub server,可单独用或组合 fallback 链
  • 静态数据懒加载:构建时打包 approved 翻译,运行时按语言按需 fetch 写入缓存
  • Vite 构建插件vitePluginTranslateExport 一行接入构建时拉取翻译数据
  • URL ?lang= 解析parseUrlLang / readLangFromUrl 兼容新旧格式(cn / zh-CN / chinese_simplified 等),由 createTranslationHubinit() 时自动优先处理首次进入的 ?lang=
  • 跳过翻译标记:用 class="no-translate"data-no-translate 属性标记元素不翻译
  • vue-i18n 兼容onBeforeSwitchLanguage 钩子处理时序冲突,5 种新语言保持中文

支持的语言

SDK 当前支持 7 种语言,源语言固定为简体中文,目标语言 6 种:

| TranslateLangId | 中文名 | BCP-47 | Edge API | 是否源语言 | |---|---|---|---|---| | chinese_simplified | 简体中文 | zh-Hans | zh-CHS | ✅ 源 | | english | 英语 | en | en | 目标 | | japanese | 日语 | ja | ja | 目标 | | korean | 韩语 | ko | ko | 目标 | | french | 法语 | fr | fr | 目标 | | german | 德语 | de | de | 目标 | | spanish | 西班牙语 | es | es | 目标 |

为什么没有繁体中文/俄语/阿拉伯语等:与上游 MTranServer 默认支持的语言对齐。扩展步骤见 QUESTION.md

安装

# npm registry(推荐)
npm install translation-hub-sdk

# 本地 link(开发期)
cd packages/sdk && pnpm build && pnpm link --global
# 在业务项目里:pnpm link --global translation-hub-sdk

# workspace 协议(monorepo 内)
# 业务项目 package.json: "translation-hub-sdk": "workspace:*"

# 离线 tarball
cd packages/sdk && pnpm build && npm pack
# 在业务项目里:npm install /path/to/translation-hub-sdk-x.x.x.tgz

三种翻译服务(service 选项)

| service | Provider | 适用场景 | 服务端 glossary | 服务端缓存 | |---|---|---|---|---| | translate.service(默认) | MTranServer(+ Edge fallback) | 自部署 MTranServer,客户端直连 | ❌ | ❌ | | hub.server | Translation Hub server | 对接托管翻译服务:服务端处理术语表、缓存与翻译引擎选路 | ✅ | ✅ | | client.edge | Edge 浏览器公共 API | 无需部署,国内不稳定 | ❌ | ❌ |

快速开始

1. 最小接入(工厂,约 5 行)

import { createTranslationHub } from 'translation-hub-sdk';

const handle = createTranslationHub({
  service: 'translate.service',     // 或 'hub.server' / 'client.edge'
  hubProjectKey: 'xxx',             // 仅 hub.server 必填
  enableDevEditor: import.meta.env.MODE !== 'production',  // hub.server 模式下控制是否显示右下角编辑胶囊(编辑文案 + 批准当前页面草稿),生产环境务必关掉
  glossary: { translateHub: { english: 'translateHub' } },  // 可选
  onLanguageChange: (lang) => { /* 同步框架层状态 */ },
});

await handle.init();  // 必须 app.mount 之后调

工厂内部自动处理:Provider 选择、静态数据懒加载、URL ?lang= 优先级、init/start 流程、语言切换钩子。所有配置项及默认值见 API.md - createTranslationHub

2. 三种场景的接入示例

import { TranslationHub, MTranServerProvider, EdgeTranslateProvider } from 'translation-hub-sdk';

const hub = new TranslationHub({
  provider: new MTranServerProvider({ url: 'http://localhost:8989' }),
  fallback: new EdgeTranslateProvider(),
  glossary: { translateHub: { english: 'translateHub', japanese: 'translateHub' } },
});

await hub.init();
await hub.start();
import { TranslationHub, HubServerProvider } from 'translation-hub-sdk';

const hub = new TranslationHub({
  provider: new HubServerProvider({
    endpoint: 'http://localhost:3000/api/v1',
    projectKey: 'your-project-key',  // 在托管服务注册项目后获得
  }),
  skipPivot: true,  // 服务端翻译服务已处理 pivot
  glossary: { translateHub: { english: 'translateHub' } },
});

await hub.init();
await hub.start();
import { TranslationClient, MTranServerProvider } from 'translation-hub-sdk';

const client = new TranslationClient({
  provider: new MTranServerProvider({ url: 'http://localhost:8989' }),
  glossary: { translateHub: { english: 'translateHub' } },
});

const { translation, source } = await client.translate('登录', 'english');
// translation = 'Login', source = 'translated'

const results = await client.translateBatch(['登录', '退出', 'translateHub'], 'english');
// [
//   { translation: 'Login',    source: 'translated' },
//   { translation: 'Logout',   source: 'translated' },
//   { translation: 'translateHub',   source: 'glossary'  }
// ]

DOM 翻译覆盖范围

SDK 自动扫描以下 DOM 位置的中文文案并翻译:

| 场景 | 是否翻译 | 说明 | |---|---|---| | Text 节点(<div>/<p>/<span> 等内文本) | ✅ | 中英文混排时只翻中文段,英文段保留 | | HTML 属性(placeholder / title / aria-label / alt) | ✅ | 切换语言时先还原属性原文再重译 | | SVG <text>(含 ECharts SVGRenderer) | ✅ | ECharts 必须用 SVGRenderer 而不是 CanvasRenderer | | Canvas 渲染的图表 | ❌ | 是位图不是 DOM,改用 SVGRenderer | | JS 变量里的中文(如 alert('成功')) | ❌ | 不在 DOM 里,用 await hub.translate(msg) 手动翻译 | | 纯英文/数字 | ❌ | 自动跳过 |

跳过某元素翻译:加 class="no-translate"data-no-translate 属性,父元素标记则子孙全部跳过:

<div class="no-translate">这段不翻译</div>
<div data-no-translate>这段也不翻译</div>
<div class="no-translate"><MyComponent />  <!-- 子组件内所有中文都跳过 --></div>

自定义标记名称(避免和业务 class 冲突):

new TranslationHub({
  skipTranslateClass: 'i18n-skip',
  skipTranslateAttr: 'data-i18n-skip',
});

手动翻译 / 重扫 DOM

const { translation } = await hub.translate('登录成功');
alert(translation);

hub.reTranslate();              // 路由切换后重扫新渲染的 DOM
hub.clearCache();                // 不传清所有语言
hub.clearCache('english');       // 只清英文

ECharts 接入要点见 QUESTION.md - ECharts 图表翻译

项目接入指南(Vite + Vue 实战)

业务代码零侵入——业务组件完全不动,只需在入口文件初始化、路由钩子里 reTranslate、UI 里调 switchLanguage

1. src/utils/translate.ts(项目入口,约 40 行)

import { shallowRef } from 'vue';
import {
  createTranslationHub,
  LANGUAGE_OPTIONS,
  localLangToTranslateLang,
  parseUrlLang,
  type TranslateLangId,
  type GlossaryEntries,
} from 'translation-hub-sdk';

export const currentLang = shallowRef<TranslateLangId>('chinese_simplified');
export { LANGUAGE_OPTIONS, localLangToTranslateLang as localeTypeToLocalLang, parseUrlLang };

// 品牌名术语表(绝不送翻译引擎)
const GLOSSARY: GlossaryEntries = {
  BrandX: { english: 'BrandX', japanese: 'BrandX', korean: 'BrandX' },
  BrandY: { english: 'BrandY', japanese: 'BrandY', korean: 'BrandY' },
};

const env = import.meta.env as any;

const handle = createTranslationHub({
  service: env.VITE_TRANSLATE_SERVICE || 'translate.service',
  mtranServerUrl: env.VITE_TRANSLATE_HOST || 'http://localhost:8989',
  hubServerEndpoint: window.location.origin + (env.VITE_TRANSLATE_HUB_ENDPOINT || '/translate'),
  hubProjectKey: env.VITE_TRANSLATE_HUB_PROJECT_KEY || '',
  enableDevEditor: import.meta.env.MODE !== 'production',  // 生产环境关掉右下角编辑胶囊(编辑文案 + 批准当前页面草稿)
  langStorageKey: env.VITE_TRANSLATE_LANG_KEY || 'locale-lang',
  langStorageFormat: 'local',  // 写入 zh-CN / en(读取仍兼容旧 chinese_simplified / english)
  defaultLang: env.VITE_DEFAULT_LANG || 'chinese_simplified',
  staticDataBaseUrl: (env.BASE_URL || '/').replace(/\/$/, '') + '/translations',
  glossary: GLOSSARY,
  onLanguageChange: (lang) => { currentLang.value = lang; },
});

export const translationHub = handle.hub;
export const initTranslate = handle.init;
export const switchLanguage = handle.switchLanguage;
export const reTranslate = handle.reTranslate;
export const getCurrentLanguage = handle.getCurrentLanguage;

// vue-i18n 只有 zh-CN / en 两种 locale,5 种新语言让 vue-i18n 保持中文
export function localLangToLocaleType(lang: string): 'zh-CN' | 'en' {
  return lang === 'english' ? 'en' : 'zh-CN';
}

⚠️ hubServerEndpoint 必须是绝对 URL(含协议+域名+端口)

SDK 内部用 new URL(endpoint + '/runtime/glossary') 拼接请求地址。如果传相对路径如 /translatenew URL() 会抛异常,导致 glossary / overrides 接口静默失败(被 catch 吞掉,不会报错也不会发请求)。

通过 Vite proxy 转发:hubServerEndpoint: window.location.origin + '/translate'

直连服务端:hubServerEndpoint: 'http://10.1.4.227:9002/api/v1'

2. main.ts(入口初始化)

import { initTranslate } from './utils/translate';

app.mount('#app');
await initTranslate();  // 必须在 mount 之后,等 DOM 渲染完成

3. router/index.ts(路由切换后重扫 DOM)

import { reTranslate } from './utils/translate';

router.afterEach(() => {
  // 100ms 延迟让 Vue 完成新页面 patch,否则 reTranslate 扫不到新 DOM
  setTimeout(() => reTranslate(), 100);
});

4. UI 切换语言(Locale 下拉)

import { switchLanguage, currentLang, LANGUAGE_OPTIONS } from './utils/translate';

// 模板里 v-for LANGUAGE_OPTIONS 渲染下拉
// 用户选中后调 changeLanguage(item.value)

vue-i18n 项目切换语言必须按 3 步顺序(详见 QUESTION.md - vue-i18n 时序冲突):

const changeLanguage = async (lang: TranslateLangId) => {
  if (lang === currentLang.value) return;
  // 切英文用 skipRestore 保留本地文案;切中文需还原原文(源语言)
  // onBeforeSwitchLanguage 在 createTranslationHub 里全局配置,SDK 自动调用
  const skipRestore = lang === 'english';
  await switchLanguage(lang, skipRestore ? { skipRestore: true } : undefined);
  router.replace({ query: { ...route.query, lang } });
};

5. 构建时静态数据打包(vite 插件)

// vite.config.ts
import { defineConfig, loadEnv } from 'vite';
import { vitePluginTranslateExport } from 'translation-hub-sdk/vite';

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '');
  return {
    plugins: [
      vitePluginTranslateExport({
        endpoint: env.VITE_TRANSLATE_HUB_ENDPOINT || 'http://localhost:3000/api/v1',
        projectKey: env.VITE_TRANSLATE_HUB_PROJECT_KEY || '',
        // 可选:
        // langs: ['english', 'japanese'],  // 默认 6 种非中文语言
        // outDir: 'dist/translations',
        // silent: true,  // 配置缺失时静默跳过(默认 false 输出 console.log 警告)
      }),
    ],
  };
});

打包内容优先级glossary > approved(不包含 draft,未审核的 AI 翻译不打包)。

6. 登录/登出:保留语言 key

清空 localStorage 时保留语言持久化 key:

const PRESERVE_KEYS = [
  import.meta.env.VITE_TRANSLATE_LANG_KEY || 'locale-lang',
];

export function clear() {
  const preserved = PRESERVE_KEYS.map(k => [k, localStorage.getItem(k)]);
  localStorage.clear();
  sessionStorage.clear();
  clearAllCookies();
  preserved.forEach(([k, v]) => v !== null && localStorage.setItem(k, v));
}

PRESERVE_KEYS 必须与 SDK langStorageKey 一致。

langStorageFormat 说明:默认 'translate'TranslateLangIdchinese_simplified / english / ...)。如需与后端业务代码对齐(zh-CN / en),设 langStorageFormat: 'local',SDK 写入时自动转换。读取永远兼容两种格式,旧用户的语言选择不会丢失。

框架与构建工具支持

SDK 是纯 TypeScript 库,零运行时依赖,不绑定任何框架。核心模块只用浏览器原生 API(MutationObserver / TreeWalker / localStorage / fetch),所有现代浏览器都支持。

| 框架 | 接入要点 | |---|---| | Vue 2 / 3 | onLanguageChange 同步 shallowRef | | React / Next.js | onLanguageChange 同步 useStateinit()useEffect 里调 | | Svelte / SvelteKit | onLanguageChange 同步 store | | Angular | onLanguageChange 同步 service 里的 signal / BehaviorSubject | | SolidJS | onLanguageChange 同步 createSignal | | Vanilla JS | 不传 onLanguageChange 即可 |

| 构建工具 | 静态数据打包 | |---|---| | Vite | 用专用插件 vitePluginTranslateExporttranslation-hub-sdk/vite 子路径导入) | | Webpack / Rspack / esbuild | 自己写 5 行脚本调 /runtime/export 接口拉翻译数据,输出到 dist/translations/{lang}.json | | Next.js / Nuxt | 用 getStaticProps / loader/runtime/export,或构建脚本同上 |

SSR 注意TranslationHubtypeof document === 'undefined' 时自动跳过 DOM 扫描;init() 必须在客户端调(useEffect / onMounted),不能在服务端调。

环境变量约定

SDK 不强制读环境变量,但建议业务项目用以下命名:

| 变量 | 示例 | 说明 | |------|------|------| | VITE_TRANSLATE_HOST | http://localhost:8989 | MTranServer 地址(场景 1) | | VITE_TRANSLATE_HUB_ENDPOINT | http://localhost:3000/api/v1 | Translation Hub server 地址(场景 2) | | VITE_TRANSLATE_HUB_PROJECT_KEY | xxx-xxx-xxx | 托管服务注册后获得的 projectKey | | VITE_TRANSLATE_LANG_KEY | locale-lang | 持久化语言的 localStorage key | | VITE_TRANSLATE_SERVICE | translate.service | client.edge | hub.server | 翻译引擎选择 | | VITE_DEFAULT_LANG | chinese_simplified | 默认语言 |

测试

pnpm --filter translation-hub-sdk test

233 个单测覆盖语言检测、术语表(含 id-based 跨 batch 占位符)、Pivot、缓存、Provider 适配、TranslationClient、混合文本切分、TranslationHub 集成(含 langStorageFormat 双向兼容)、静态数据懒加载、Vite 构建插件。

文档

  • API.md:所有类型、函数、类的完整签名和说明
  • QUESTION.md:实战坑、原理细节、不常用场景(ECharts / vue-i18n 时序 / 扩展新语言 / 缓存策略 / 服务端译文同步机制等)

新增 API 时同时更新 README 接入示例 + API.md 详细签名;遇到实战坑或原理性问题追加到 QUESTION.md。

Translation Hub server

service: 'hub.server' 模式依赖 Translation Hub server——一个翻译管理后端,提供项目注册、术语表管理、译文审核、缓存、多翻译引擎选路等能力,配套 admin 后台做人工修订。本 SDK 是它的客户端,服务端代码还在开发中,稍后开源,敬请期待。

License

MIT