Skip to main content
官方应用-通义多模态翻译

网页翻译JSSDK

功能描述

基于翻译大模型的页面翻译插件,提供页面翻译功能。您可以通过JSSDK,一键完成网站的多语言改造。

技术流程

image

接入JSSDK

当前版本

项目

版本号

3.0.1

CDN 根路径

https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/

下文示例中的版本号均以 3.0.1 为准,升级时请同步替换 CDN 链接中的版本号。
SDK 提供两种翻译模式:
  • 页面翻译(pageTranslate):直接替换页面原文为译文,适合“单语展示”场景。
  • 段落对照翻译(paragraphTranslate):在原文后插入译文,适合“原文+译文对照阅读”场景。
同时支持:
  • 懒加载翻译(只翻译可视区域)
  • 动态内容翻译(DOM 变化后自动翻译)
  • 术语表与翻译记忆
  • 细粒度 hooks(翻译前/后插入自定义逻辑)

引入方式

通过 Script 标签从 CDN 引入,支持两种方式:

方式一:全量引入(推荐上手)

一次引入 index.js(已包含核心 + 全部插件),即可同时使用页面翻译与段落对照:

文件

说明

地址

index.js

全量包(核心 + 插件)

https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/index.js

<script src="https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/index.js"></script>

方式二:按需拆包引入

先加载 core.js,再按需加载对应插件脚本:

文件

说明

地址

core.js

核心 SDK

https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/core.js

page.js

页面翻译插件

https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/page.js

paragraph.js

段落对照插件

https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/paragraph.js

<!-- 页面翻译 -->
<script src="https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/core.js"></script>
<script src="https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/page.js"></script>

<!-- 段落对照:将 page.js 换成 paragraph.js -->
<!-- 两种模式都要用:同时引入 page.js 与 paragraph.js -->
说明:
  • 全量引入只需一个文件;拆包引入必须先加载 core.js,再加载插件。
  • 全局对象为 window.AliTranslate,同时提供别名 window.__AliTranslate(两者指向同一实例)。

快速开始

页面翻译(Pure Page)

<script src="https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/index.js"></script>
<script>
  AliTranslate.pageTranslate({
    getToken: async (params) => {
      const res = await fetch('https://your-sign-api.example.com/signature', {
        method: 'POST',
        body: JSON.stringify(params),
      });
      return (await res.json()).data;
    },
    srcLanguage: 'auto',
    tgtLanguage: 'en',
    targetSelectors: ['#app'],
    excludeSelectors: ['.no-translate', 'code', 'pre'],
    translateAttributes: ['placeholder'],
    lazyload: true,
    dynamic: true,
  });
</script>

段落对照翻译(Paragraph Compare)

<script src="https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/index.js"></script>
<script>
  AliTranslate.paragraphTranslate({
    getToken: async (params) => {
      const res = await fetch('https://your-sign-api.example.com/signature', {
        method: 'POST',
        body: JSON.stringify(params),
      });
      return (await res.json()).data;
    },
    srcLanguage: 'auto',
    tgtLanguage: 'en',
    targetSelectors: ['#article'],
    excludeSelectors: ['.no-translate', 'code', 'pre'],
    translateAttributes: ['placeholder'],
    lazyload: true,
    dynamic: true,
    extraBlockSelectors: ['.article-title', 'h1', 'h2', 'h3'],
    extraInlineSelectors: ['.breadcrumb', '.tag-inline'],
  });
</script>

初始化参数(SDKConfig)

AliTranslate.pageTranslate / AliTranslate.paragraphTranslate 中共用的 SDK 级参数如下:

参数

类型

必填

说明

getToken

(params) => Promise<SignedRequest>

鉴权签名函数,返回可直接请求翻译服务的签名请求

terminologies

Array<{ src, tgt }>

术语表,提升领域词一致性

examples

Array<{ src, tgt }>

翻译记忆

domainHint

string

业务领域(透传至翻译请求)

chunkSize

number

分块阈值(影响请求合并与吞吐)

maxRetries

number

翻译失败最大重试次数

afterTranslate

(result) => void

翻译完成回调


页面翻译参数(PageTranslateConfig)

参数

类型

必填

说明

tgtLanguage

LANGUAGE

目标语种

srcLanguage

LANGUAGE

源语种,不传默认自动检测

targetSelectors

string | string[]

翻译根节点,默认 body

excludeSelectors

string | string[]

排除节点(命中后整棵子树不翻译)

translateAttributes

string | string[]

翻译支持的元素属性

lazyload

boolean

是否启用懒加载翻译

lazyOffset

number

懒加载视口偏移(px)

lazySelectors

string | string[]

懒翻译选择器(当前版本为预留字段,建议优先使用 targetSelectors

dynamic

boolean

是否监听 DOM 变化并自动翻译

translateDelay

number

初始化等待/动态翻译节流延迟(ms)

rules

Array<{ selector; style?; className? }>

页面翻译规则(作用于原文容器)

onBeforeInsertTrans

(ctx) => void | HTMLElement | Promise<...>

插入译文前回调

onTranslatingStart

(ctx) => void | Promise<void>

单节点翻译开始回调

onTranslatingEnd

(ctx) => void | Promise<void>

单节点翻译结束回调


段落对照参数(ParagraphTranslateConfig)

参数

类型

必填

说明

tgtLanguage

LANGUAGE

目标语种

srcLanguage

LANGUAGE

源语种,不传默认自动检测

targetSelectors

string | string[]

翻译根节点,默认 body

excludeSelectors

string | string[]

排除节点(命中后不参与段落提取)

translateAttributes

string | string[]

翻译支持的元素属性

lazyload

boolean

是否启用懒加载翻译

lazyOffset

number

懒加载视口偏移(px)

lazySelectors

string | string[]

懒翻译选择器(当前版本为预留字段,建议优先使用 targetSelectors

dynamic

boolean

是否监听 DOM 变化并自动翻译

translateDelay

number

初始化等待/动态翻译节流延迟(ms)

extraBlockSelectors

string | string[]

强制按 block 方式插入译文(独占一行)

extraInlineSelectors

string | string[]

强制按 inline 方式插入译文(同行展示)

rules

TransStyleRule[]

对照翻译样式规则

onBeforeInsertTrans

(ctx) => void | HTMLElement | Promise<...>

插入 <trans> 之前回调

onTranslatingStart

(ctx) => void | Promise<void>

单段落翻译开始回调

onTranslatingEnd

(ctx) => void | Promise<void>

单段落翻译结束回调

TransStyleRule 说明

interface TransStyleRule {
  selector: string
  transStyle?: Record<string, string> // 作用于译文节点 <trans>
  style?: Record<string, string>      // 作用于命中 selector 的父容器
  className?: string                  // 作用于命中 selector 的父容器
}

Hooks 与可回滚 DOM 变更

SDK 提供了 mutator(可回滚变更操作器),支持在 hooks 中做安全 DOM 操作,并在 removeTranslations() 时自动回滚。
<script src="https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/index.js"></script>
<script>
  AliTranslate.paragraphTranslate({
    getToken: async (params) => {
      const res = await fetch('https://your-sign-api.example.com/signature', {
        method: 'POST',
        body: JSON.stringify(params),
      });
      return (await res.json()).data;
    },
    tgtLanguage: 'en',
    onTranslatingStart: (ctx) => {
      const loading = document.createElement('span');
      loading.className = 'loading';
      loading.textContent = '翻译中...';
      loading.setAttribute('data-source-hash', ctx.sourceHash);
      ctx.mutator.appendNode(ctx.paragraph.commonAncestor, loading);
    },
    onTranslatingEnd: (ctx) => {
      const loading = ctx.paragraph.commonAncestor.querySelector(
        `.loading[data-source-hash="${ctx.sourceHash}"]`,
      );
      if (loading) ctx.mutator.removeNode(loading);
    },
    onBeforeInsertTrans: (ctx) => {
      ctx.transNode.style.color = '#4f46e5';
      return ctx.transNode;
    },
  });
</script>
mutator 可用能力:
  • setStyle(el, prop, value, priority?)
  • addClass(el, className)
  • removeClass(el, className)
  • appendNode(parent, node)
  • removeNode(node)

语种与枚举值

常用语种值示例:
  • auto(自动检测)
  • zhenjakofrdeesruarthvi
完整语言枚举请参考 SDK 类型定义 LANGUAGE

常用 API

// 移除翻译结果(保留 SDK 实例)
AliTranslate.removeTranslations()

// 销毁 SDK(释放资源,插件与事件解绑)
AliTranslate.destroy()

完整示例(段落对照 + rules + hooks)

<script src="https://g.alicdn.com/code/npm/@alife/translate-js-sdk/3.0.1/index.js"></script>
<script>
  AliTranslate.paragraphTranslate({
    getToken: async (params) => {
      const res = await fetch('https://your-sign-api.example.com/signature', {
        method: 'POST',
        body: JSON.stringify(params),
      });
      return (await res.json()).data;
    },
    srcLanguage: 'auto',
    tgtLanguage: 'en',
    targetSelectors: ['#article'],
    excludeSelectors: ['.no-translate', 'code', 'pre'],
    lazyload: true,
    lazyOffset: 120,
    dynamic: true,
    translateDelay: 200,
    extraBlockSelectors: ['.title', 'h1', 'h2'],
    extraInlineSelectors: ['.breadcrumb'],
    rules: [
      {
        selector: '.highlight',
        transStyle: { color: '#4f46e5' },
        style: { background: '#eef2ff' },
        className: 'translated-highlight',
      },
    ],
    onBeforeInsertTrans: (ctx) => {
      // 自定义译文节点样式,给所有翻译前(排除h1, h2, h3)添加一个border
      if (ctx.paragraph.commonAncestor.matches('h1, h2, h3')) {
        return ctx.transNode;
      }
      ctx.transNode.style.borderLeft = '2px solid #e94560';
      ctx.transNode.style.paddingLeft = '0.75rem';
      ctx.transNode.style.marginTop = '0.5rem';
    },
    onTranslatingStart: (ctx) => {
      // 为翻译中的节点添加 loading
      const loading = document.createElement('span');
      loading.className = 'loading';
      loading.setAttribute('data-source-hash', ctx.sourceHash);
      loading.textContent = '翻译中...';
      ctx.mutator.appendNode(ctx.paragraph.commonAncestor, loading);
    },
    onTranslatingEnd: (ctx) => {
      // 移除自定义添加的 loading
      const root = ctx.paragraph.commonAncestor;
      const loading = root.querySelector(`.loading[data-source-hash="${ctx.sourceHash}"]`);
      if (loading) ctx.mutator.removeNode(loading);
    },
  });
</script>

如需同时支持“页面翻译”和“段落对照翻译”,推荐直接使用全量包 index.js,并在业务 UI 中明确区分两种模式的切换与清理流程。

获取Token服务

使用阿里云SDK的V3版本请求体&签名机制进行请求签名
// 通过calculateSignature方法生成签名
import * as crypto from 'crypto';
export interface SignatureRequest {
  httpMethod: string;
  canonicalUri: string;
  host: string;
  xAcsAction: string;
  xAcsVersion: string;
  headers: Record<string, string>;
  body: any;
  queryParam: Record<string, any>;
}
export interface BatchTranslateRequest {
  scene: string;
  sourceLanguage: string;
  streaming: boolean;
  targetLanguage: string;
  text: { [key: string]: string };
}
export class Request implements SignatureRequest {
  httpMethod: string;
  canonicalUri: string;
  host: string;
  xAcsAction: string;
  xAcsVersion: string;
  headers: Record<string, string>;
  body: any;
  queryParam: Record<string, any>;
  constructor(httpMethod: string, canonicalUri: string, host: string, xAcsAction: string, xAcsVersion: string) {
    this.httpMethod = httpMethod;
    this.canonicalUri = canonicalUri || '/';
    this.host = host;
    this.xAcsAction = xAcsAction;
    this.xAcsVersion = xAcsVersion;
    this.headers = {};
    this.body = null;
    this.queryParam = {};
    this.initHeader();
  }
  private initHeader() {
    const date = new Date();
    this.headers = {
      'host': this.host,
      'x-acs-action': this.xAcsAction,
      'x-acs-version': this.xAcsVersion,
      'x-acs-date': date.toISOString().replace(/\..+/, 'Z'),
      'x-acs-signature-nonce': crypto.randomBytes(16).toString('hex')
    }
  }
}
const ALGORITHM = 'ACS3-HMAC-SHA256';
const accessKeyId = process.env.ACCESS_KEY_ID; // 这里填入阿里云的AK
const accessKeySecret = process.env.ACCESS_KEY_SECRET; // 这里填入阿里云的SK
const workspaceId = process.env.WORKSPACE_ID; // 这里填入百炼的WorkspaceId
const encoder = new TextEncoder();
const httpMethod = 'POST';
const canonicalUri = '/anytrans/translate/batchForHtml';
const xAcsAction = 'BatchTranslateForHtml';
const xAcsVersion = '2025-07-07';
const host = 'anytrans.cn-beijing.aliyuncs.com';
export interface SignedRequest {
  url: string;
  method: string;
  host: string;
  headers: Record<string, string>;
  body?: any; // 可能是 Uint8Array 或 Base64 字符串
}
export function getAuthorization(signRequest: SignatureRequest): SignedRequest {
  try {
    const newQueryParam: Record<string, any> = {};
    processObject(newQueryParam, "", signRequest.queryParam);
    signRequest.queryParam = newQueryParam;
    // 步骤 1:拼接规范请求串
    const canonicalQueryString = Object.entries(signRequest.queryParam)
      .sort(([a], [b]) => a.localeCompare(b))
      .map(([key, value]) => `${percentCode(key)}=${percentCode(value)}`)
      .join('&');
    // 请求体,当请求正文为空时,比如GET请求,RequestPayload固定为空字符串
    const requestPayload = signRequest.body || encoder.encode('');
    const hashedRequestPayload = sha256Hex(requestPayload);
    signRequest.headers['x-acs-content-sha256'] = hashedRequestPayload;
    // 将所有key都转换为小写
    signRequest.headers = Object.fromEntries(
      Object.entries(signRequest.headers).map(([key, value]) => [key.toLowerCase(), value])
    );
    const sortedKeys = Object.keys(signRequest.headers)
      .filter(key => key.startsWith('x-acs-') || key === 'host' || key === 'content-type')
      .sort();
    // 已签名消息头列表,多个请求头名称(小写)按首字母升序排列并以英文分号(;)分隔
    const signedHeaders = sortedKeys.join(";")
    // 构造请求头,多个规范化消息头,按照消息头名称(小写)的字符代码顺序以升序排列后拼接在一起
    const canonicalHeaders = sortedKeys.map(key => `${key}:${signRequest.headers[key]}`).join('\n') + '\n';
    const canonicalRequest = [
      signRequest.httpMethod,
      signRequest.canonicalUri,
      canonicalQueryString,
      canonicalHeaders,
      signedHeaders,
      hashedRequestPayload
    ].join('\n');
    console.log('canonicalRequest=========>\n', canonicalRequest);
    // 步骤 2:拼接待签名字符串
    const hashedCanonicalRequest = sha256Hex(encoder.encode(canonicalRequest));
    const stringToSign = `${ALGORITHM}\n${hashedCanonicalRequest}`;
    console.log('stringToSign=========>', stringToSign);
    // 步骤 3:计算签名
    const signature = hmac256(accessKeySecret!, stringToSign);
    console.log('signature=YOUR_SIGNATURE>', signature);
    // 步骤 4:拼接 Authorization
    const authorization = `${ALGORITHM} Credential=${accessKeyId},SignedHeaders=${signedHeaders},Signature=YOUR_SIGNATURE
    console.log('authorization=========>', authorization);
    signRequest.headers['Authorization'] = authorization;
    // 构建完整的URL
    let url = `https://${signRequest.host}${signRequest.canonicalUri}`;
    if (signRequest.queryParam && Object.keys(signRequest.queryParam).length > 0) {
      const query = new URLSearchParams(signRequest.queryParam);
      url += '?' + query.toString();
    }
    return {
      url,
      host: signRequest.host,
      method: signRequest.httpMethod.toUpperCase(),
      headers: signRequest.headers,
      body: signRequest.body
    };
  } catch (error) {
    console.error('Failed to get authorization');
    console.error(error);
    throw error;
  }
}
function percentCode(str: string): string {
  return encodeURIComponent(str)
    .replace(/\+/g, '%20')
    .replace(/\*/g, '%2A')
    .replace(/~/g, '%7E');
}
function hmac256(key: string, data: string): string {
  const hmac = crypto.createHmac('sha256', key);
  hmac.update(data, 'utf8');
  return hmac.digest('hex').toLowerCase();
}
function sha256Hex(bytes: any): string {
  const hash = crypto.createHash('sha256');
  const digest = hash.update(bytes).digest('hex');
  return digest.toLowerCase();
}
function processObject(map: Record<string, any>, key: string, value: any): void {
  // 如果值为空,则无需进一步处理
  if (value === null) {
    return;
  }
  if (key === null) {
    key = "";
  }
  // 当值为Array类型时,遍历Array中的每个元素,并递归处理
  if (Array.isArray(value)) {
    value.forEach((item, index) => {
      processObject(map, `${key}.${index + 1}`, item);
    });
  } else if (typeof value === 'object' && value !== null) {
    // 当值为Object类型时,遍历Object中的每个键值对,并递归处理
    Object.entries(value).forEach(([subKey, subValue]) => {
      processObject(map, `${key}.${subKey}`, subValue);
    });
  } else {
    // 对于以"."开头的键,移除开头的"."以保持键的连续性
    if (key.startsWith('.')) {
      key = key.slice(1);
    }
    map[key] = String(value);
  }
}
export function formDataToString(formData: Record<string, any>): string {
  const tmp: Record<string, any> = {};
  processObject(tmp, "", formData);
  let queryString = '';
  for (let [key, value] of Object.entries(tmp)) {
    if (queryString !== '') {
      queryString += '&';
    }
    queryString += encodeURIComponent(key) + '=' + encodeURIComponent(value);
  }
  return queryString;
}
// 固定变量签名计算方法
export interface FixedSignatureResult {
  httpMethod: string;
  canonicalUri: string;
  host: string;
  xAcsAction: string;
  xAcsVersion: string;
  signature: string;
  date: string;
  nonce: string;
  workspaceId: string;
  authorization: string;
}
export function calculateSignature(req: BatchTranslateRequest): SignedRequest {
  // 创建请求实例
  const signRequest = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);
  // 设置查询参数
  signRequest.queryParam = {
    RegionId: 'cn-beijing',
  };
  // 创建 body 的 Uint8Array
  const bodyUint8Array = encoder.encode(JSON.stringify({
    ...req,
    workspaceId,
  }));
  // 用于签名计算的 body
  signRequest.body = bodyUint8Array;
  signRequest.headers['content-type'] = 'application/json';
  // 获取签名
  const result = getAuthorization(signRequest);
  // 将 body 转换为 Base64 字符串用于传输
  const bodyBase64 = Buffer.from(bodyUint8Array).toString('base64');
  return {
    ...result,
    body: bodyBase64 // 返回 Base64 字符串而不是 Uint8Array
  };
}

注意事项(生产接入建议)

  • 必须配置getToken:由业务侧完成鉴权签名后再发起翻译请求。
  • 引入方式二选一:全量引入 index.js 即可;若按需拆包,页面翻译需引入 page.js,段落对照需引入 paragraph.js
  • 重复翻译建议先清理:多次切换参数/语言前建议先执行 removeTranslations(),避免旧状态干扰新配置验证。
  • 目标与排除选择器要收敛targetSelectors 建议限定业务容器,excludeSelectors 排除导航、代码块、编辑区等。
  • 避免翻译敏感区域:如输入框、编辑器、业务脚本节点、模板容器。
  • Hook 里避免重逻辑onTranslatingStart/End 触发频率高,避免耗时操作阻塞主线程。
  • 动态内容场景建议开启dynamic:适配懒渲染和流式加载页面。

常见问题

调用无效果

  • 检查是否已正确引入脚本(全量 index.js,或 core.js + 对应插件)。
  • 检查 targetSelectors 是否命中。
  • 检查 excludeSelectors 是否误伤目标区域。
  • 检查签名接口是否返回合法请求结构。

rules / hooks 未生效

  • 确认传参写在 pageTranslate / paragraphTranslate 的同一层级。
  • 重复执行前先 removeTranslations() 再重新翻译。
  • 检查 selector 是否可命中段落 commonAncestor 或其祖先。

loading 看不到

  • 翻译速度很快时会瞬时消失,建议在业务侧设置最小可见时长(如 300-500ms)。
  • data-source-hash 做唯一标识,避免并发段落相互覆盖。

AK/SK在哪里获取?

  • 阿里云平台的AccessKey模块中获取

获取Token服务中的workspaceId从哪里获取?

  • 登录AK/SK对应的阿里云账号后,在百炼左下角的业务空间详情中获取
  • 弹窗中的业务空间id即对应所需的workspaceId,单击字段右侧的复制图标可直接复制。

网络接口调用报没有权限

  • 未开通 通义多模态翻译 产品:需要使用AK所属的阿里云主账号,在百炼通义多模态翻译上进行开通

变更记录

2026年3月

发布版本

发布时间

发布内容

3.0.1

2026年8月6日

支持页面元素的属性内容翻译(配置参数)

3.0.0

2026年7月16日

大版本升级,增加按需引入、术语/语料/domain配置、样式干预、更多的配置参数

2.0.6

2026年3月5日

双语对照翻译增加lazyload和lazyOffset两个参数

Managed Agents
数据连接
Skill
应用评测
应用广场
权限管理