图片翻译编辑器协议
1、产品简介
该编辑器当前支持图片翻译标准版和图片翻译Pro版,融合多模态大模型技术,支持100+语向互译,通过编辑器对图片翻译结果进行局部优化与二次编辑,满足用户对高质量多语言营销物料的制作需求。
编辑器本身不收费,只有当使用了图片翻译标准版,图片翻译Pro版,文本翻译时会触发收费,收费方式详见本文9的计费方式。
主要功能 | 功能概述 | Demo |
端面简介 |
|
|
2、方案介绍
本期图像编辑器产品对外提供两种接入形态,二者共用同一套「iframe + postMessage」的通信框架,差异仅在于所嵌入的能力路由与输入参数。您可以根据下表选择匹配自身业务的方案。
如何选择:
需要一站式、多能力串联、且希望翻译数据不流出自身业务系统 → 选方案A。
已经在使用图片翻译Pro API、仅需要对既有翻译结果做人工二次编辑 → 选方案B。
维度 | 方案A:一站式图像工作台 | 方案B:二次编辑 |
定位 | 翻译、抠图、消除、场景图等多能力串联的一站式工作台 | 在图片翻译 API 结果之上做二次编辑的专用编辑器 |
典型使用场景 | 客户希望翻译流程与编辑闭环在一个界面内完成,翻译过程与数据全部在客户侧沉淀 | 客户已对接图片翻译API,选择目标图片后进入编辑器做局部优化,属于「API + 编辑器」两段式 |
数据管理 | 上传、翻译、编辑、导出全部在接入方的工作台内完成 | 翻译由 API 完成,编辑器仅承接二次编辑;翻译结果与协议由客户自行管理 |
可组合能力 | 图片翻译标准版,图片翻译Pro版,以及二次编辑时的文本翻译 | |
计费方式 | 编辑器本身不收费,使用到翻译API能力时收费,包含图片翻译和文本翻译 | |
3、接入方式
3.1、整体流程
客户前端向客户服务端发起请求,例如 POST /api/aidge-editor/access-url。
客户服务端根据当前用户身份,向阿里云登录服务申请一次性 Key,并拼接完整的 iframe URL。
客户服务端将完整的 iframe URL 返回给客户前端。
客户前端创建或刷新 iframe,并将 src 设置为该 URL。
阿里云登录服务对 Key 进行校验。校验通过后,进入编辑器页面。
编辑器页面加载完成后,通过 postMessage 向父页面发送加载完成事件。
客户前端收到加载完成事件后,通过 postMessage 向 iframe 发送初始化事件,并携带相应的初始化参数。
建议由客户服务端直接返回完整的 iframe URL,以减少前端拼接错误并降低 Key 泄露风险。客户服务端也可仅返回 Key,由客户前端获取 Key 后立即拼接 iframe URL。
如需对下游用户进行数据隔离,可将用户唯一 ID 传入 RoleSessionName 字段。详见下方 3.2.6 中的 Java 示例代码。
3.2、客户服务端接入
具体实现可参考 免登访问 方案。
3.2.1、创建 RAM 子用户并授权
如子账号已存在,可跳过此步骤。
3.2.2、为 RAM 用户授权 AliyunSTSAssumeRoleAccess

3.2.3、创建 RAM 角色
使用 RAM 主账号登录控制台并创建 RAM 角色,也可通过 RAM API CreateRole 创建。



3.2.4、为角色授权
搜索“Aidge”,并选择对应的权限策略。

3.2.5、获取 roleArn、accessKeyId、accessKeySecret 参数



3.2.6、Java 示例代码
<dependencies>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-sts</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URISyntaxException;
/**
* Aidge 虚商控制台免登接入示例。
* <p>
* 流程:AssumeRole 获取临时凭证 → GetSigninToken → 构造 federation 免登链接。
*/
public class Test {
private static final String SIGN_IN_DOMAIN = "https://signin.aliyun.com/federation";
/**
* 使用安全令牌获取登录令牌
* https://help.aliyun.com/document_detail/91913.html
*
* @param accesskeyId
* @param accessKeySecret
* @param securityToken
* @return
* @throws IOException
* @throws URISyntaxException
*/
private static String getSignInToken(String accesskeyId, String accessKeySecret, String securityToken)
throws IOException, URISyntaxException {
URIBuilder builder = new URIBuilder(SIGN_IN_DOMAIN);
builder.setParameter("Action", "GetSigninToken")
.setParameter("AccessKeyId", accesskeyId)
.setParameter("AccessKeySecret", accessKeySecret)
.setParameter("SecurityToken", securityToken)
.setParameter("TicketType", "mini");
HttpGet request = new HttpGet(builder.build());
CloseableHttpClient httpclient = HttpClients.createDefault();
try (CloseableHttpResponse response = httpclient.execute(request)) {
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String context = EntityUtils.toString(response.getEntity());
JSONObject jsonObject = JSON.parseObject(context);
return jsonObject.getString("SigninToken");
} else {
System.out.println(response.getStatusLine());
}
}
return null;
}
private static String getChatbotLoginUrl(String pageUrl, String signInToken) throws URISyntaxException {
URIBuilder builder = new URIBuilder(SIGN_IN_DOMAIN);
builder.setParameter("Action", "Login");
// 登录失效后的跳转地址,一般配置为自建 Web 服务中用于 302 跳转的 URL
builder.setParameter("LoginUrl", "https://signin.aliyun.com/login.htm");
// 实际访问的 Aidge 控制台页面,例如首页或编辑器页面
builder.setParameter("Destination", pageUrl);
builder.setParameter("SigninToken", signInToken);
HttpGet request = new HttpGet(builder.build());
return request.getURI().toString();
}
/**
* 通过 AssumeRole 接口获取用户临时身份
* 参考 https://help.aliyun.com/document_detail/28763.html
*
* @param roleArn RAM 角色 ARN,格式:acs:ram::<主账号UID>:role/<角色名>
* @param accessKeyId 调用方 AK
* @param accessKeySecret 调用方 SK
* @param roleSessionName 用户自定义会话名,可用于用户级别的访问审计。格式:^[a-zA-Z0-9\.@\-_]+$
* @return
* @throws ClientException
*/
private static AssumeRoleResponse.Credentials assumeRole(String roleArn, String accessKeyId,
String accessKeySecret, String roleSessionName)
throws ClientException {
String defaultRegion = "cn-hangzhou";
IClientProfile profile = DefaultProfile.getProfile(defaultRegion, accessKeyId, accessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
AssumeRoleRequest request = new AssumeRoleRequest();
// 直接使用拼接好的角色 ARN
request.setRoleArn(roleArn);
// 用户自定义参数。此参数用来区分不同的令牌,可用于用户级别的访问审计。格式:^[a-zA-Z0-9\.@\-_]+$
request.setRoleSessionName(roleSessionName);
// 指定过期时间,单位为秒。可设置为 900~3600,默认值为 3600
request.setDurationSeconds(3600L);
AssumeRoleResponse response = client.getAcsResponse(request);
return response.getCredentials();
}
public static void main(String[] args) throws IOException, URISyntaxException {
try {
/*
* Step 0:准备子账号并完成权限授权
*/
// 要扮演的 RAM 角色 ARN(由主账号 UID 和角色名拼接),请替换为真实值 acs:ram::${accountId}:role/${roleName}
String roleArn = "按需替换";
// 子账号的 AK 和 SK,需具备 AliyunSTSAssumeRoleAccess 权限,请替换为真实值
String accessKeyId = "按需替换";
String accessKeySecret = "按需替换";
// 业务侧用户 ID:用于同一 RAM 角色下区分真实业务用户,便于访问审计。
// 建议填写业务系统中的用户唯一标识(如 userId),格式需满足:^[a-zA-Z0-9.@-_]+$,长度为 2~64 个字符
String userId = "按需替换";
/*
* Step 1:通过 AssumeRole 接口获取临时 AK、SK 和 SecurityToken
*/
AssumeRoleResponse.Credentials credentials =
assumeRole(roleArn, accessKeyId, accessKeySecret, userId);
System.out.println("Expiration: " + credentials.getExpiration());
System.out.println("Access Key Id: " + credentials.getAccessKeyId());
System.out.println("Access Key Secret: " + credentials.getAccessKeySecret());
System.out.println("Security Token: " + credentials.getSecurityToken());
System.out.println("RoleSessionName: " + userId);
/*
* Step 2:获取 SigninToken
*/
String signInToken = getSignInToken(credentials.getAccessKeyId(),
credentials.getAccessKeySecret(),
credentials.getSecurityToken());
System.out.println("Your SigninToken is: " + signInToken);
// 集成 Aidge 编辑器页
String pageUrl = "https://ecoa4service.console.aliyun.com/editor";
String accessUrl = getChatbotLoginUrl(pageUrl, signInToken);
System.out.println("Your PageUrl is : " + accessUrl);
} catch (ClientException e) {
System.out.println("Failed:");
System.out.println("Error code: " + e.getErrCode());
System.out.println("Error message: " + e.getErrMsg());
System.out.println("RequestId: " + e.getRequestId());
}
}
}3.2.7、生成 Key 的服务端要求
Key 生成逻辑必须部署在服务端。服务端应妥善保管 AK、SK、RoleArn 和 Secret 等敏感信息。
每次打开编辑器页面时,都应生成新的 Key。
不得将 Key 作为长期凭证持久化存储。如需记录,应仅保留必要的审计信息,并避免将其明文输出至前端日志、Nginx 访问日志或监控系统。
Key 生成后应立即使用。一次性 Key 的有效期为 30 秒,且仅可使用一次。
刷新或重新打开页面,以及复制链接后再次访问时,都应重新执行服务端 Key 生成流程。
3.3、客户前端接入
在页面中通过 <iframe> 标签引入编辑器,并将 src 设置为服务端返回的地址。详见上方 3.2。
客户前端与编辑器通过 postMessage 进行通信。编辑器的 Origin 为 https://ecoa4service.console.aliyun.com。建议客户前端发送消息时指定该 Origin,接收消息时校验该 Origin。详见下方 3.3.7。
当客户前端收到编辑器 iframe 发送的 pageReady 消息后,可向编辑器发送 action 为 init 的消息,完成初始化。
window.addEventListener("message", (ev: MessageEvent) => {
const { action } = ev.data ?? {};
if (action === "pageReady") {
iframeEle.contentWindow?.postMessage(
{
action: "init",
data: {
tool: "translate",
},
},
editorOrigin,
);
}
});3.3.1、编辑器初始化配置项
序号 | 名称 | 配置字段 | 字段类型 | 是否必填 | 默认值 | 说明 |
1 | 编辑器能力 |
|
| 是 | - |
|
2 | 图片 URL |
|
| 否 | - | 待翻译图片的 URL。请勿直接使用限制图片访问的电商平台图片链接,建议转存后再传入。如不传入,可在编辑器中手动上传 |
3 | 原始语向 |
|
| 否 | - | 如不传入,可在编辑器中手动选择。支持的语向详见下方 7.1 |
4 | 目标语向 |
|
| 否 | - | 如不传入,可在编辑器中手动选择。支持的语向详见下方 7.1 |
5 | 原文保留(品牌名)默认值 |
|
| 否 |
|
|
6 | 二次编辑 |
|
| 否 |
|
|
7 | 二次编辑数据 |
|
| 否 |
| 翻译后的结果数据,包含图层信息,可从 API 返回结果中获取 |
8 | 生产拦截 |
|
| 否 |
|
|
9 | 是否执行调用 |
|
| 否 |
|
|
10 | 编辑器 UI 配置 |
|
| 否 |
| 对于每个子配置项,如果用户已传入自定义值,则以自定义值为准;未传入的子配置项将使用下表中的默认值 |
3.3.2、editorConfig 配置项
以下配置项均为选填项。
序号 | 名称 | 配置字段 | 字段类型 | 默认值 | 说明 |
1 | 返回按钮是否展示 |
|
|
|
|
2 | “图片编辑器”大标题文案 |
|
|
| - |
3 | “历史”按钮是否展示 |
|
|
|
|
4 | “历史”按钮文案 |
|
|
| - |
5 | “图片翻译”标题文案 |
|
|
| - |
6 | “历史任务”标题文案 |
|
|
| - |
7 | “点击上传图片”按钮文案 |
|
|
| - |
8 | “标准版”标签文案 |
|
|
|
|
9 | “Pro 版”标签文案 |
|
|
|
|
10 | “标准版”说明文案 |
|
|
| 设置为 |
11 | “Pro 版”说明文案 |
|
|
| 设置为 |
12 | “语言”小标题文案 |
|
|
| - |
13 | “原始语向”选项标签文案 |
|
|
| - |
14 | “原始语向”选项说明文案 |
|
|
| 设置为 |
15 | “目标语向”选项标签文案 |
|
|
| - |
16 | “目标语向”选项说明文案 |
|
|
| 设置为 |
17 | “原文保留(品牌名)”选项标签文案 |
|
|
| - |
18 | “原文保留(品牌名)”选项说明文案 |
|
|
| 设置为 |
19 | “立即翻译”按钮文案 |
|
|
| - |
20 | “文案”按钮是否展示 |
|
|
|
|
21 | “贴纸”按钮是否展示 |
|
|
|
|
22 | “素材”按钮是否展示 |
|
|
|
|
3.3.3、编辑器消息体
编辑器通过 postMessage 发送的消息体结构如下:
{
biz: "translate",
action: "generate",
data: {
imageNum: 1,
},
}字段说明:
字段 | 示例值 | 是否必返 | 含义 |
|
| 否 | 业务标识。图片翻译标准版为 |
|
| 是 | 消息类型 |
|
| 否 | 消息数据 |
3.3.4、编辑器报错消息体
编辑器通过 postMessage 发送的报错消息体结构如下:
{
biz: "translate",
action: "error",
errCode: "translate-taskError",
errMessage: errMsg,
}字段说明:
字段 | 示例值 | 是否必返 | 含义 |
|
| 是 | 发生错误的业务标识 |
|
| 是 | 消息类型。报错消息固定为 |
|
| 是 |
|
|
| 是 | 错误信息 |
接入方可根据 errCode 自定义面向用户的错误提示。
3.3.5、编辑器发送的消息
通信方向:编辑器 iframe → 客户前端
| 触发时机 | 消息体 | 关键数据 | 关联消息 |
| 编辑器页面加载完成 |
| 无其他字段 | 客户前端收到后发送 |
| 用户点击返回按钮,且初始化配置中 |
| 无其他字段 | - |
| 用户准备发起付费 API 调用,且初始化配置中 |
|
| 客户前端需返回 |
| 编辑器业务执行失败 |
|
详见上方 3.3.4 | - |
| 生成任务执行成功 |
|
详见下方 5.1 | - |
| 用户点击“保存”或“下载”,或编辑器响应 |
|
详见下方 5.3 | 可由 |
3.3.6、编辑器可接收的消息
通信方向:客户前端 → 编辑器 iframe
| 发送时机 | 消息体 | 关键数据 | 编辑器处理结果 |
| 客户前端收到 |
|
详见上方 3.3.1 | 根据配置完成编辑器初始化 |
| 客户前端收到 |
|
详见下方 4.1 | 根据 |
| 客户前端自行完成 API 调用后 |
|
详见下方 4.3 | 加载并展示结果 |
| 客户前端需主动获取当前完整结果时 |
| 无其他字段 详见下方 5.1 | 编辑器通过 |
3.3.7、示例前端代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Aidge 编辑器接入示例</title>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
}
#editorIframe {
display: block;
width: 100%;
height: 100%;
border: 0;
}
</style>
</head>
<body>
<iframe
id="editorIframe"
title="Aidge 在线编辑器"
allow="clipboard-read; clipboard-write"
></iframe>
<script>
const editorIframe = document.getElementById("editorIframe");
// 编辑器页面的 Origin,用于校验和发送 postMessage。
const editorOrigin = "https://ecoa4service.console.aliyun.com";
// 编辑器初始化参数。请根据实际业务替换示例值。
const editorInitData = {
// translate:图片翻译标准版
// translatePro:图片翻译 Pro 版
// translateBoth:标准版 + Pro 版,可在编辑器中切换
tool: "translateBoth",
// 以下三项为选填项。不传 imageUrl 时,可在编辑器中手动上传图片。
imageUrl: "https://example.com/source.jpg",
sourceLanguage: "zh",
targetLanguage: "en",
// 开启生产拦截。编辑器发送 generate 后,会等待父页面返回 respond。
charge: true,
// editorConfig 及其所有子配置项均为选填项。
editorConfig: {
editorTitle: "图片编辑器",
showHistory: true,
historyButtonText: "历史",
imageTransPanelTitle: "图片翻译",
historyPanelTitle: "历史任务",
imageUploaderButtonText: "点击上传图片",
imageTransLabel: "标准版",
imageTransProLabel: "Pro 版",
imageTransTip: "高性价比",
imageTransProTip: "翻译更精准",
imageTransOptionsTitle: "语言",
sourceLanguageLabel: "原始语向",
sourceLanguageTip: "被翻译内容的语向",
targetLanguageLabel: "目标语向",
targetLanguageTip: "想要翻译成的语向",
includingProductAreaLabel: "原文保留(品牌名)",
includingProductAreaTip: "开启后主流品牌将不会被翻译",
includingProductAreaDefaultValue: true,
imageTransButtonText: "开始翻译",
showAddText: true,
showAddSticker: true,
showAddImage: true
}
};
/**
* 判断是否允许本次付费 API 调用。
* 实际接入时,请在此处调用客户服务端,校验用户余额或权限。
*/
async function checkGenerationPermission({ biz, imageNum, data }) {
console.log("待确认的生产请求:", {
biz,
imageNum,
data
});
// TODO:根据实际业务返回校验结果。
return true;
}
/**
* 处理生产拦截请求。
*/
async function handleGenerate(message) {
const { biz, data } = message;
const { imageNum } = data ?? {};
let success = false;
try {
success = await checkGenerationPermission({
biz,
imageNum,
data
});
} catch (error) {
console.error("生产请求校验失败:", error);
}
// biz 必须与编辑器发送的 generate 消息保持一致。
editorIframe.contentWindow.postMessage(
{
biz,
action: "respond",
success
},
editorOrigin
);
}
/**
* 处理编辑器报错。
*/
function handleEditorError(message) {
const { biz, errCode, errMessage } = message;
console.error("编辑器报错:", {
biz,
errCode,
errMessage
});
// TODO:可根据 errCode 自定义面向用户的错误提示。
}
/**
* 处理算法生成结果。
* 该结果不包含用户在编辑器中的后续编辑操作。
*/
function handleTaskSuccess(message) {
const { biz, data } = message;
const results = Array.isArray(data) ? data : [];
console.log("生成任务完成:", {
biz,
results
});
// TODO:根据实际业务处理算法生成结果。
}
/**
* 处理用户点击“保存”或“下载”后返回的最终结果。
*/
function handleSubmitAll(message) {
const results = Array.isArray(message.data) ? message.data : [];
console.log("收到编辑器最终结果:", results);
// TODO:将 schema 原样保存,不得修改其内容。
// TODO:及时转存结果图片,返回的图片 URL 默认有效期为 7 天。
}
/**
* 监听编辑器 iframe 发送的消息。
*/
window.addEventListener("message", function (event) {
// 只接收当前编辑器 iframe 发送的消息。
if (event.source !== editorIframe.contentWindow) {
return;
}
// 校验消息来源,避免处理其他网站发送的消息。
if (event.origin !== editorOrigin) {
console.warn("忽略未知来源的消息:", event.origin);
return;
}
const message = event.data;
if (!message || typeof message !== "object") {
return;
}
switch (message.action) {
case "pageReady":
console.log("编辑器页面已准备就绪");
// 收到 pageReady 后发送初始化参数。
editorIframe.contentWindow.postMessage(
{
action: "init",
data: editorInitData
},
editorOrigin
);
break;
case "generate":
// 生产拦截为异步流程,处理结果将通过 respond 返回编辑器。
void handleGenerate(message);
break;
case "error":
handleEditorError(message);
break;
case "taskSuccess":
handleTaskSuccess(message);
break;
case "submitAll":
handleSubmitAll(message);
break;
default:
console.warn("收到未识别的编辑器消息:", message);
}
});
/**
* 从客户服务端获取编辑器地址,并设置 iframe src。
*/
async function loadEditor() {
try {
const response = await fetch("/api/aidge-editor/access-url", {
method: "POST",
cache: "no-store"
});
if (!response.ok) {
throw new Error(`获取编辑器地址失败:HTTP ${response.status}`);
}
const result = await response.json();
if (!result.src) {
throw new Error("服务端未返回编辑器 src");
}
const editorUrl = new URL(result.src);
if (editorUrl.protocol !== "https:") {
throw new Error("编辑器地址必须使用 HTTPS");
}
// 消息监听器注册完成后再加载 iframe,避免错过 pageReady。
// 一次性 Key 生成后应立即使用。
editorIframe.src = editorUrl.href;
} catch (error) {
console.error("编辑器加载失败:", error);
}
}
loadEditor();
</script>
</body>
</html>4、定制化功能
4.1、生产拦截
仅当编辑器初始化配置中的 charge 设置为 true 时,生产拦截功能才会生效。
生产拦截功能允许接入方控制用户的付费 API 调用请求。编辑器将根据接入方返回的结果,决定是否继续执行调用,从而实现调用前的二次确认。
使用场景:用户每次在编辑器中调用付费 API 时,接入方可验证用户的可用余额或权限是否满足要求
实现步骤:
监听编辑器页面发送的消息,并实现接入方的业务判断逻辑。
let receiveBiz = "";
window.addEventListener("message", (ev: MessageEvent) => {
const { data, action, biz } = ev.data ?? {};
if (action === "generate") {
// 保存业务类型,回复编辑器时需要原样传回
receiveBiz = biz;
// 本次用户准备生成的图片数量
const { imageNum } = data ?? {};
console.log("生产请求:", {
biz,
imageNum,
data,
});
// TODO:在这里判断用户余额、权限等
}
});返回消息,控制是否继续生产流程。当返回消息中的
success为true时,继续执行调用;为false时,中断调用并向用户显示提示。
// true:继续生产并产生费用
// false:中断生产流程
const success = true;
iframeEle.contentWindow?.postMessage(
{
biz: receiveBiz,
action: "respond",
success,
},
editorOrigin,
);
4.2、二次编辑
仅当编辑器初始化配置中的 reEdit 设置为 true 时,二次编辑功能才会生效。
二次编辑功能支持在编辑器中加载历史结果数据并继续编辑。启用该功能时,接入方需通过初始化配置中的 reEditData 传入历史结果数据。该数据可使用 API 返回结果中的 genFiles 字段(详见下方第 6 节),也可使用编辑器返回的结果数据(详见下方 5.2 和 5.3)。
reEditData 为 string 类型,反序列化后为符合 EditorData 结构的对象数组。目前仅支持单张图片的二次编辑,因此数组中只能包含一个元素。详见下方 5.3。
iframeEle.contentWindow?.postMessage(
{
action: "init",
data: {
tool: "translate",
reEdit: true,
reEditData: JSON.stringify(
[
{
editInfo: {
goodsRects: {
top: 0,
left: 0,
width: 802,
height: 802,
degree: 0,
},
languages: ["en"],
textAreas: [
{
verticalLayout: "top",
horizontalLayout: "center",
texts: [
{
valid: true,
verticalLayout: "top",
horizontalLayout: "center",
color: "#f6eec1",
imageRect: {
top: 27,
left: 68,
width: 692,
height: 100,
degree: 0,
},
fontsize: 50,
language: "en",
textRect: {
top: 27,
left: 68,
width: 692,
height: 100,
degree: 0,
},
value: "Hello Kitty Sleep Companion Night Light",
lineCount: 1,
stroke: "#000000",
strokeWidth: 1,
paintFirst: "stroke",
shadow: {
offsetX: 0,
offsetY: 0,
blur: 1,
color: "#000000",
},
},
],
color: "#f6eec1",
fontsize: 50,
lineCount: 1,
content: "凯蒂猫伴睡小夜灯",
},
{
verticalLayout: "top",
horizontalLayout: "center",
texts: [
{
valid: true,
verticalLayout: "top",
horizontalLayout: "center",
color: "#f6edc2",
imageRect: {
top: 154,
left: 262,
width: 290,
height: 26,
degree: 0,
},
fontsize: 26,
language: "en",
textRect: {
top: 154,
left: 262,
width: 290,
height: 26,
degree: 0,
},
value: "Tap, squeeze, and play",
lineCount: 1,
},
],
color: "#f6edc2",
fontsize: 26,
lineCount: 1,
content: "任拍任捏任打",
},
{
verticalLayout: "top",
horizontalLayout: "center",
texts: [
{
valid: true,
verticalLayout: "top",
horizontalLayout: "center",
color: "#f9eac5",
imageRect: {
top: 328,
left: 206,
width: 112,
height: 12,
degree: 12,
},
fontsize: 11,
language: "en",
textRect: {
top: 328,
left: 206,
width: 112,
height: 12,
degree: 12,
},
value: "Gently tap to light up",
lineCount: 1,
},
],
color: "#f9eac5",
fontsize: 11,
lineCount: 1,
content: "轻拍点亮",
},
{
verticalLayout: "top",
horizontalLayout: "center",
texts: [
{
valid: true,
verticalLayout: "top",
horizontalLayout: "center",
color: "#f7e8c2",
imageRect: {
top: 355,
left: 206,
width: 150,
height: 12,
degree: 13,
},
fontsize: 11,
language: "en",
textRect: {
top: 355,
left: 206,
width: 150,
height: 12,
degree: 13,
},
value: "Soft light for eye protection!",
lineCount: 1,
stroke: "#000000",
strokeWidth: 41,
paintFirst: "stroke",
},
],
color: "#f7e8c2",
fontsize: 11,
lineCount: 1,
content: "柔光更护眼!",
},
],
resultImageIds: [
"xxx",
],
repairedUrl: "xxx",
font: ["AlibabaSans-Regular"],
},
srcImage: "xxx",
resultList: [
{
language: "en",
fileUrl: "xxx",
},
],
},
]
),
},
},
editorOrigin,
);4.3、接入方自行调用 API
仅当编辑器初始化配置中的 enableToolExecution 设置为 false 时,接入方自行调用 API 功能才会生效。
该功能允许接入方自行调用当前能力对应的 API,并将调用结果回传给编辑器。用户在编辑器中触发能力后,编辑器不会直接调用 API,而是通过 generate 消息将调用信息发送给客户前端。客户前端完成 API 调用后,需通过 applyToolResult 消息将结果回传给编辑器,由编辑器加载并展示结果。
实现步骤:
监听编辑器发送的
generate消息,并根据消息中的biz和data调用对应的 API。API 调用成功后,通过
applyToolResult消息将结果数据回传给编辑器。biz需与generate.biz保持一致,data的数据结构需与init消息中的reEditData一致。API 调用失败时,可将
data设置为空字符串""并回传给编辑器。接入方需同时通过 Toast 等方式自行向用户展示错误提示。
window.addEventListener("message", async (event) => {
const { action, biz, data } = event.data ?? {};
if (action === "generate") {
try {
// 由接入方实现对应能力的 API 调用
// invokeToolApi 为示例方法,需由接入方根据实际业务实现。
const apiResult = await invokeToolApi({ biz, data });
// 图片翻译 API 可直接使用 genFiles 字段
const resultData = apiResult.genFiles;
iframeEle.contentWindow.postMessage(
{
biz,
action: "applyToolResult",
data: resultData,
},
editorOrigin,
);
} catch (error) {
console.error("能力 API 调用失败:", error);
// TODO:由接入方通过 Toast 等方式向用户展示错误提示。
iframeEle.contentWindow.postMessage(
{
biz,
action: "applyToolResult",
data: "",
},
editorOrigin,
);
}
}
});
5、结果获取
5.1、生成任务结果获取
用户发起生成请求且调用成功后,接入方可通过监听 action 为 taskSuccess 的消息,获取当次任务的结果数据。
注意:此处的任务结果是算法生成的结果,不是用户修改后的最终结果,因此不包含用户在编辑器中的编辑操作。
interface ResultItem {
id: string;
url: string;
}
type Result = ResultItem[];
window.addEventListener("message", (ev: MessageEvent) => {
const { data, action, biz } = ev.data ?? {};
if (action === "taskSuccess") {
const result: Result = data;
console.log("业务:", biz, "生产任务完成,结果:", result);
}
});5.2、最终结果获取
方式1:事件监听
用户执行以下操作时,编辑器会向接入方返回结果数据:
点击“保存”按钮:返回当前画布的结果图片和协议。
点击“下载”按钮:将结果图片下载到本地,同时返回结果图片和协议。
window.addEventListener("message", (ev: MessageEvent) => {
const { data, action } = ev.data ?? {};
if (action === "submitAll") {
// TODO:在这里处理最终任务结果
console.log("收到任务结果:", data);
}
});方式2:主动获取
接入方可发送 action 为 requestResult 的消息,主动获取编辑器中的结果数据。
iframe.contentWindow?.postMessage(
{
action: "requestResult",
},
editorOrigin,
);编辑器收到上述消息后,会按以下方式返回完整的结果数据。
window.addEventListener("message", (ev: MessageEvent) => {
const { data, action } = ev.data ?? {};
if (action === "submitAll") {
// TODO:在这里处理最终任务结果
console.log("收到任务结果:", data);
}
});5.3、返回的数据格式
返回数据包含用户当前画布的结果图片 url 和结果数据 editData。
结果数据需由接入方自行保存。开启二次编辑功能时,将该数据传入编辑器,即可加载并继续编辑。
注意:结果数据在存储和传输过程中不得进行任何修改,否则可能导致编辑器无法正确读取或加载历史数据。结果图片 URL 的默认有效期为 7 天,请接入方及时转存,以免因链接过期而无法访问。
interface ResultType {
/**
* 本次是否由用户点击「下载图片」触发。
* 下载与保存的报文体完全相同,接入方靠它判断该不该重复计数 / 提示。
*/
isDownload: boolean;
/** 结果 ID,用于跟踪算法结果以及用户调整后的结果 */
id: string;
/** 原图地址(单张,可直接访问的签名 URL) */
originalUrl: string;
/** 结果图地址(单张,可直接访问的签名 URL) */
url: string;
/**
* 编辑器完整数据,类型为 JSON string
* 与初始化参数 reEditData 一致,反序列化后为符合 EditorData 结构的对象数组。
*/
editData: string;
}
interface EditorData {
/** 编辑器完整数据:文字区、用户新增图层、修复底图、字体等 */
editInfo: EditInfo;
/** 原图地址 */
srcImage: string;
/** 结果图列表。按语种分项(算法结构),当前单语种翻译故恒为单元素 */
resultList: ResultImageItem[];
}
interface EditInfo {
/** 商品区域 */
goodsRects?: Rect;
/** 目标语种列表 */
languages: string[];
/** 文字区列表(算法识别出的「原文 → 译文」区域) */
textAreas: TextArea[];
/** 结果图 ID 列表 */
resultImageIds: string[];
/** 去字后的修复底图地址 */
repairedUrl: string;
/** 字体列表 */
font: string[];
/** 图翻标准版特有:上传的原图地址(Pro 版放在 srcImage) */
pictUrl?: string;
/**
* 用户在编辑器内新增的贴纸 / 素材 / 文本等图层。
* textAreas 只建模算法识别出的文字区,表达不了这些图层,故单独存放。
* 算法原始返回不带该字段,用户未新增图层时也不带。
*/
editorLayers?: EditorLayer[];
}
interface Rect {
top: number;
left: number;
width: number;
height: number;
/** 旋转角度 */
degree: number;
}
interface TextArea {
/** 垂直对齐:top / center / bottom */
verticalLayout: string;
/** 水平对齐:left / center / right */
horizontalLayout: string;
/** 译文列表(按语种) */
texts: TextItem[];
color: string;
fontsize: number;
lineCount: number;
/** 原文内容 */
content: string;
}
interface TextItem {
valid: boolean;
verticalLayout: string;
horizontalLayout: string;
color: string;
/** 在图片上的位置 */
imageRect: Rect;
fontsize: number;
/** 语种 */
language: string;
/** 文字框位置 */
textRect: Rect;
/** 译文内容 */
value: string;
lineCount: number;
/** 描边颜色 */
stroke?: string;
/** 描边粗细 */
strokeWidth?: number;
/** 描边绘制顺序:stroke / fill */
paintFirst?: string;
/** 阴影 */
shadow?: Shadow;
/** 字间距 */
charSpacing?: number;
/** 文字底色 */
backgroundColor?: string;
/** 行高 */
lineHeight?: number;
/** 文字装饰:underline / line-through */
textDecoration?: string;
}
interface Shadow {
color: string;
offsetX: number;
offsetY: number;
/** 模糊度 */
blur: number;
}
interface ResultImageItem {
/** 语种 */
language: string;
/** 结果图地址 */
fileUrl: string;
}6、API 返回的图层信息
图片翻译标准版与 Pro 版 API 返回的图层结构基本一致。两者的原图地址均存储在 srcImage 字段中;区别在于,标准版还会通过特有字段 editInfo.pictUrl 记录上传的原图地址。
Data.GenFiles 在原始 API 响应中为 String,反序列化后为 Array[Object]。下表从第 2 项开始的参数名称,均表示 Data.GenFiles 反序列化后的逻辑结构。
注意:[*]表示数组中的任意元素。例如,GenFiles[*]表示任意一项图层数据,TextAreas[*]表示任意一个文本框,Texts[*]表示任意一项翻译后文本,ResultList[*]表示任意一项翻译结果。
序号 | 参数名称 | 数据类型 | 参数描述及示例值 |
1 |
|
| 编辑器协议信息,仅当 |
2 |
|
| 原图 URL |
3 |
|
| 编辑器详细信息 |
4 |
|
| 上传的原图 URL,仅图片翻译标准版返回 |
5 |
|
| 翻译后图片的全局 ID 集合 |
6 |
|
| 所有文字涂抹后的图片 URL |
7 |
|
| 翻译语种列表 |
8 |
|
| 文本框列表 |
9 |
|
| 字体大小 |
10 |
|
| 翻译前的文本 |
11 |
|
| 文字颜色,例如 |
12 |
|
| 垂直对齐方式: |
13 |
|
| 水平对齐方式: |
14 |
|
| 文本框行数 |
15 |
|
| 翻译后文本列表 |
16 |
|
| 字体大小 |
17 |
|
| 语言 |
18 |
|
|
|
19 |
|
| 图片修复区域 |
20 |
|
| 文本框区域 |
21 |
|
| 文字颜色 |
22 |
|
| 翻译后的文本 |
23 |
|
| 垂直对齐方式 |
24 |
|
| 水平对齐方式 |
25 |
|
| 文本框行数 |
26 |
|
| 商品框区域 |
27 |
|
| 字体类型列表 |
28 |
|
| 翻译结果列表 |
29 |
|
| 翻译语种 |
30 |
|
| 翻译后的图片 URL |
7、翻译支持语向
7.1、语言代码
语言代码采用 ISO 639-1 标准的两位字母表示法。对于特定语言的区域变体,遵循 RFC 5646 格式,即在语言代码后加短横线,并接上 ISO 3166 的两位国家/地区代码。例如,繁体中文的语言代码是 zh-tw。
支持桥接翻译,例如zh-en,en- ar,则可实现zh-en-ar的桥接翻译
序号 | 源语言 | 目标语言 | ||
语言代码 | 中文名称 | 语言代码 | 中文名称 | |
1 | zh zh-tw | 中文(简体) 中文(繁体) | en | 英语 |
2 | ja | 日语 | ||
3 | ko | 韩语 | ||
4 | kk | 哈萨克语 | ||
5 | ms | 马来语 | ||
6 | th | 泰语 | ||
7 | ar | 阿拉伯语 | en | 英语 |
8 | tr | 土耳其语 | ||
9 | az | 阿塞拜疆语 | en | 英语 |
10 | bn | 孟加拉语 | en | 英语 |
11 | cs | 捷克语 | en | 英语 |
12 | de | 德语 | en | 英语 |
13 | el | 希腊语 | en | 英语 |
14 | en | 英语 | ar | 阿拉伯语 |
15 | az | 阿塞拜疆语 | ||
16 | bn | 孟加拉语 | ||
17 | bs | 波斯尼亚语 | ||
18 | cs | 捷克语 | ||
19 | da | 丹麦语 | ||
20 | de | 德语 | ||
21 | el | 希腊语 | ||
22 | es | 西班牙语 | ||
23 | et | 爱沙尼亚语 | ||
24 | fi | 芬兰语 | ||
25 | fr | 法语 | ||
26 | he | 希伯来语 | ||
27 | hi | 印地语 | ||
28 | hu | 匈牙利语 | ||
29 | id | 印度尼西亚语 | ||
30 | it | 意大利语 | ||
31 | ja | 日语 | ||
32 | ko | 韩语 | ||
33 | lt | 立陶宛语 | ||
34 | lv | 拉脱维亚语 | ||
35 | ms | 马来语 | ||
36 | my | 缅甸语 | ||
37 | ne | 尼泊尔语 | ||
38 | nl | 荷兰语 | ||
39 | no | 挪威语 | ||
40 | pl | 波兰语 | ||
41 | pt | 葡萄牙语(巴西) | ||
42 | pt-pt | 葡萄牙语(葡萄牙) | ||
43 | ro | 罗马尼亚语 | ||
44 | ro_ur | 罗马尼亚语(乌尔都) | ||
45 | ru | 俄语 | ||
46 | si | 僧伽罗语 | ||
47 | sk | 斯洛伐克语 | ||
48 | sl | 斯洛文尼亚语 | ||
49 | sr | 塞尔维亚语 | ||
50 | sv | 瑞典语 | ||
51 | th | 泰语 | ||
52 | tl | 菲律宾语 | ||
53 | tr | 土耳其语 | ||
54 | uk | 乌克兰语 | ||
55 | ur | 乌尔都语 | ||
56 | vi | 越南语 | ||
57 | zh | 中文(简体) | ||
58 | es | 西班牙语 | bg | 保加利亚语 |
59 | cs | 捷克语 | ||
60 | da | 丹麦语 | ||
61 | de | 德语 | ||
62 | el | 希腊语 | ||
63 | en | 英语 | ||
64 | et | 爱沙尼亚语 | ||
65 | fi | 芬兰语 | ||
66 | fr | 法语 | ||
67 | hr | 克罗地亚语 | ||
68 | hu | 匈牙利语 | ||
69 | it | 意大利语 | ||
70 | lt | 立陶宛语 | ||
71 | lv | 拉脱维亚语 | ||
72 | nl | 荷兰语 | ||
73 | no | 挪威语 | ||
74 | pl | 波兰语 | ||
75 | pt | 葡萄牙语(巴西) | ||
76 | ro | 罗马尼亚语 | ||
77 | ru | 俄语 | ||
78 | sk | 斯洛伐克语 | ||
79 | sv | 瑞典语 | ||
80 | pt-pt | 葡萄牙语(葡萄牙) | ||
81 | fi | 芬兰语 | en | 英语 |
82 | fr | 法语 | en | 英语 |
83 | he | 希伯来语 | en | 英语 |
84 | hi | 印地语 | en | 英语 |
85 | hu | 匈牙利语 | en | 英语 |
86 | id | 印度尼西亚语 | en | 英语 |
87 | it | 意大利语 | en | 英语 |
88 | ja | 日语 | en | 英语 |
89 | ko | 韩语 | en | 英语 |
90 | ms | 马来语 | en | 英语 |
91 | my | 缅甸语 | en | 英语 |
92 | ne | 尼泊尔语 | en | 英语 |
93 | nl | 荷兰语 | en | 英语 |
94 | pl | 波兰语 | en | 英语 |
95 | pt | 葡萄牙语(巴西) | en | 英语 |
96 | ro | 罗马尼亚语 | en | 英语 |
97 | tr | 土耳其语 | ||
98 | ru | 俄语 | en | 英语 |
99 | si | 僧伽罗语 | en | 英语 |
100 | sv | 瑞典语 | en | 英语 |
101 | th | 泰语 | en | 英语 |
102 | tl | 菲律宾语 | en | 英语 |
103 | tr | 土耳其语 | ar | 阿拉伯语 |
104 | cs | 捷克语 | ||
105 | de | 德语 | ||
106 | el | 希腊语 | ||
107 | en | 英语 | ||
108 | hu | 匈牙利语 | ||
109 | ro | 罗马尼亚语 | ||
110 | sk | 斯洛伐克语 | ||
111 | uk | 乌克兰语 | en | 英语 |
112 | ur | 乌尔都语 | en | 英语 |
113 | vi | 越南语 | en | 英语 |
114 | bg | 保加利亚语 | en | 英语 |
115 | bs | 波斯尼亚语 | en | 英语 |
116 | da | 丹麦语 | en | 英语 |
117 | et | 爱沙尼亚语 | en | 英语 |
118 | hr | 克罗地亚语 | en | 英语 |
119 | lt | 立陶宛语 | en | 英语 |
120 | lv | 拉脱维亚语 | en | 英语 |
121 | no | 挪威语 | en | 英语 |
122 | pt-pt | 葡萄牙语(葡萄牙) | en | 英语 |
123 | ro_ur | 罗马尼亚语(乌尔都) | en | 英语 |
124 | sk | 斯洛伐克语 | en | 英语 |
125 | sl | 斯洛文尼亚语 | en | 英语 |
126 | sr | 塞尔维亚语 | en | 英语 |
7.2、语种识别支持语向
注:仅在使用图片翻译pro版时,原始语向可以自动识别,可识别出的语向如下:
序号 | 语言名称(英文) | 语言代码 | 语言名称(中文) |
1 | Arabic | ar | 阿拉伯语 |
2 | Bengali | bn | 孟加拉语 |
3 | German | de | 德语 |
4 | English | en | 英语 |
5 | Spanish | es | 西班牙语 |
6 | French | fr | 法语 |
7 | Hebrew | he | 希伯来语 |
8 | Indonesian | id | 印度尼西亚语 |
9 | Italian | it | 意大利语 |
10 | Japanese | ja | 日语 |
11 | Korean | ko | 韩语 |
12 | Malay | ms | 马来语 |
13 | Dutch | nl | 荷兰语 |
14 | Polish | pl | 波兰语 |
15 | Portuguese (Brazil) | pt | 葡萄牙语(巴西) |
16 | Russian | ru | 俄语 |
17 | Thai | th | 泰语 |
18 | Turkish | tr | 土耳其语 |
19 | Ukrainian | uk | 乌克兰语 |
20 | Urdu | ur | 乌尔都语 |
21 | Vietnamese | vi | 越南语 |
22 | Chinese (Simplified) | zh | 中文(简体) |
23 | Chinese (Traditional) | zh-tw | 中文(繁体) |
24 | Hindi | hi | 印地语 |
8、计费方式
计费项 | 计费单位 | 刊例价(人民币) |
文本翻译 | 每百万字符 | ¥50 |
图片翻译(标准版) | 每张 | ¥0.015 |
图片翻译(Pro 版) | 每张 | ¥0.06 |





