# 批量翻译引擎(BatchTranslationEngine)
通过实现 BatchTranslationEngine 接口,可以为翻译引擎增加批量翻译支持,显著提升在线翻译的效率。
批量翻译引擎扩展了 TranslationEngine 接口,通过一次翻译多个文本来减少网络往返次数,在翻译数百上千个词条时可以大幅提高整体翻译速度。
建议:推荐继承
BaseBatchTranslationEngine而非直接实现 BatchTranslationEngine 接口,基类提供了translate()方法的自动委托实现。
# 快速开始
public class MyBatchTranslationEngine extends BaseBatchTranslationEngine {
@NonNull
@Override
public String name() {
return "批量翻译引擎";
}
@NonNull
@Override
public List<String> loadSourceLanguages() {
return List.of("auto", "zh", "en", "ru");
}
@NonNull
@Override
public List<String> loadTargetLanguages(String sourceLanguage) {
return List.of("zh", "en", "ru");
}
@Override
public BatchingStrategy createBatchingStrategy() {
// 单批次最多 100 条,总字符数最多 4500
return new DefaultBatchingStrategy(100, 4500);
}
@NonNull
@Override
public String[] batchTranslate(String[] texts, String sourceLanguage, String targetLanguage) throws IOException {
// 调用支持批量翻译的 API
return callBatchTranslationAPI(texts, sourceLanguage, targetLanguage);
}
private String[] callBatchTranslationAPI(String[] texts, String sourceLanguage, String targetLanguage) throws IOException {
// 这里调用支持批量翻译的翻译 API
// 返回翻译后的文本数组,长度必须与输入数组相同
return new String[texts.length];
}
}
# 接口概览
# BatchTranslationEngine 接口
BatchTranslationEngine 继承自 TranslationEngine,新增以下方法:
| 方法 | 说明 |
|---|---|
String[] batchTranslate(String[] texts, String sourceLanguage, String targetLanguage) | 批量执行文本翻译(子线程) |
BatchingStrategy createBatchingStrategy() | 创建分批策略 |
继承的 TranslationEngine 方法请参考 翻译引擎文档。
# BaseBatchTranslationEngine 基类
BaseBatchTranslationEngine 提供了 BatchTranslationEngine 接口的基础实现:
- 继承自 BaseTranslationEngine,拥有其所有默认实现
- 提供默认的分批策略(单批次最多 50 条,总字符数最多 5000)
- 自动将
translate()委托给batchTranslate()实现 - 子类只需实现
batchTranslate()和createBatchingStrategy()方法
# BatchingStrategy 接口
分批策略接口,用于控制翻译任务的拆分:
| 方法 | 说明 |
|---|---|
void reset() | 重置状态,准备开始新批次 |
boolean tryAdd(String text) | 尝试将文本加入当前批次 |
# DefaultBatchingStrategy 类
默认分批策略实现,基于词条数和数据大小进行限制:
| 方法 | 说明 |
|---|---|
DefaultBatchingStrategy(int maxCount, int maxDataSize) | 构造函数 |
int getTextDataSize(String text) | 计算文本数据大小(可重写) |
# 详细说明
# batchTranslate
@NonNull
String[] batchTranslate(String[] texts, String sourceLanguage, String targetLanguage) throws IOException
批量执行文本翻译。此方法运行在 子线程,可能被 多次调用 以翻译不同批次的文本。
参数:
texts- 待翻译的文本数组,不为 null 且不为空sourceLanguage- 源语言代码targetLanguage- 目标语言代码
返回值: 翻译后的文本数组,长度必须与输入数组相同,不能为 null
异常: IOException - 翻译过程中发生的 IO 异常(如网络错误)
线程: 子线程
注意:返回的数组长度必须与输入数组完全相同,且顺序一一对应。
# createBatchingStrategy
BatchingStrategy createBatchingStrategy()
创建分批策略,用于控制翻译任务的拆分。
返回的 BatchingStrategy 实例可以维护内部状态(如累计字节数、Token 数等),实现增量计算以避免重复处理开销。实例会被复用,每次开始新批次时会调用 reset()。
返回值: 分批策略实例
线程: 调用方线程
默认实现: BaseBatchTranslationEngine 提供的默认实现为 new DefaultBatchingStrategy(50, 5000),即单批次最多 50 条,总字符数最多 5000。
# 分批策略
# BatchingStrategy 接口
分批策略用于控制如何将大量翻译任务拆分为多个批次。实现类可以在内部维护任意状态(如累计字符数、字节数、Token 数等),在 tryAdd() 时进行增量计算。
# reset
void reset()
重置状态,准备开始新批次。此方法会在每个批次开始前被调用,实现类应清空所有累计状态。
# tryAdd
boolean tryAdd(String text)
尝试将文本加入当前批次。
实现类应根据自身的限制条件(如最大词条数、最大字符数、最大字节数等)判断是否可以容纳新文本。如果可以容纳,应更新内部状态并返回 true。
参数:
text- 待加入的文本,不为 null
返回值:
true- 表示成功加入当前批次false- 表示当前批次已满
MT 侧处理流程:
- 返回
true:将该文本加入当前批次 - 返回
false且当前批次不为空:翻译当前批次,调用reset()开始新批次,再次使用该文本调用tryAdd尝试加入(即该文本会被调用两次) - 返回
false且当前批次为空:强制将该文本加入当前批次并翻译,然后调用reset()开始新批次(该文本仅调用一次)
# DefaultBatchingStrategy 类
默认分批策略实现,基于词条数和数据大小进行限制。
# 构造函数
public DefaultBatchingStrategy(int maxCount, int maxDataSize)
参数:
maxCount- 单批次最大词条数,0 或负数表示无限制maxDataSize- 单批次最大数据大小,0 或负数表示无限制
# getTextDataSize
protected int getTextDataSize(String text)
计算文本的数据大小。
参数:
text- 待计算的文本
返回值: 文本的数据大小
默认实现: 返回 text.length()
自定义计算逻辑:
子类可重写此方法以实现自定义的计算逻辑,例如按 UTF-8 编码字节数:
@Override
protected int getTextDataSize(String text) {
return text.getBytes(StandardCharsets.UTF_8).length;
}
# 完整示例
# 使用分割线合并文本
通用方案:对于只支持单文本翻译的 API,可以通过分割线将多个文本合并为一个请求,翻译后再拆分结果,从而实现批量翻译功能。
以下示例展示如何使用这种通用方案实现批量翻译引擎:
public class GoogleTranslationEngine extends BaseBatchTranslationEngine {
private static final OkHttpClient HTTP_CLIENT = new OkHttpClient.Builder()
.callTimeout(8, TimeUnit.SECONDS)
.build();
@NonNull
@Override
public String name() {
return "{google_translator}";
}
@NonNull
@Override
public List<String> loadSourceLanguages() {
return List.of("auto", "zh", "en", "ru");
}
@NonNull
@Override
public List<String> loadTargetLanguages(String sourceLanguage) {
return List.of("zh", "en", "ru");
}
@Override
public BatchingStrategy createBatchingStrategy() {
// 实际限制5000,留点余量
return new DefaultBatchingStrategy(100, 4500) {
@Override
protected int getTextDataSize(String text) {
return text.length() + 10; // 预留分割线大小
}
};
}
/**
* 由于我们使用的谷歌翻译接口只能翻译单个文本,因此这里使用分割线组合文本,翻译完再拆分
*/
@NonNull
@Override
public String[] batchTranslate(String[] texts, String sourceLanguage, String targetLanguage) throws IOException {
// 生成一个分割线,确保原文里面没有
String divider = "--------";
while (containsDivider(texts, divider)) {
divider += "--";
}
boolean tryAgain = true;
while (true) {
// 拼接所有文本
String mergedText = Arrays.stream(texts).collect(Collectors.joining("\n" + divider + "\n"));
// 调用单文本翻译接口
String translatedText = translate(mergedText, sourceLanguage, targetLanguage);
// 分割翻译结果
String[] array = translatedText.split("\n" + divider + "\n");
// 确保前后数量一致
if (array.length == texts.length) {
return array;
}
// 如果不一致,增加分割线长度再试一次
if (tryAgain) {
tryAgain = false;
//noinspection StringConcatenationInLoop
divider += "--";
continue;
}
// 输出错误数据
getContext().log("Original(" + texts.length + "):\n" + mergedText);
getContext().log("Translated(" + array.length + "):\n" + translatedText);
if (translatedText.length() > 100) {
translatedText = translatedText.substring(0, 99) + "...";
}
throw new IOException("Translation failed: " + translatedText);
}
}
private static boolean containsDivider(String[] texts, String divider) {
for (String s : texts) {
if (s.contains(divider)) {
return true;
}
}
return false;
}
@NonNull
@Override
public String translate(String text, String sourceLanguage, String targetLanguage) throws IOException {
// 中国大陆不支持直接访问 translate.googleapis.com,这里使用 IP 直连
// 142.250.0.160 142.250.0.161 142.250.0.183
// 142.250.0.184 172.217.4.108 142.250.4.160
String url = "http://142.250.0.160/translate_a/single";
FormBody formBody = new FormBody.Builder()
.add("client", "gtx")
.add("dt", "t")
.add("sl", sourceLanguage)
.add("tl", targetLanguage)
.add("q", text)
.build();
Request request = new Request.Builder()
.post(formBody)
.url(url)
.header("Host", "translate.googleapis.com")
.header("User-Agent", "Mozilla/5.0 (Linux; Android 6.0;)")
.build();
try (Response response = HTTP_CLIENT.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("HTTP response code: " + response.code());
}
ResponseBody body = response.body();
if (body == null) {
throw new IOException("Body is null");
}
return getResult(body.string());
}
}
private static String getResult(String string) throws IOException {
if (!string.startsWith("[[[")) {
throw new IOException("Parse result failed: " + string);
}
JSONArray array = new JSONArray(string).getJSONArray(0);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < array.size(); i++) {
sb.append(array.getJSONArray(i).getString(0));
}
return sb.toString();
}
}
# 自定义分批策略
以下示例展示如何实现基于 Token 数的自定义分批策略:
public class TokenBasedBatchingStrategy implements BatchingStrategy {
private final int maxTokens;
private int currentTokens;
public TokenBasedBatchingStrategy(int maxTokens) {
this.maxTokens = maxTokens;
}
@Override
public void reset() {
currentTokens = 0;
}
@Override
public boolean tryAdd(String text) {
int tokens = estimateTokenCount(text);
if (currentTokens > 0 && currentTokens + tokens > maxTokens) {
return false;
}
currentTokens += tokens;
return true;
}
private int estimateTokenCount(String text) {
// 简单估算:英文按空格分词,中文按字符数
if (text.matches(".*[\\u4e00-\\u9fa5]+.*")) {
return text.length(); // 中文字符数
} else {
return text.split("\\s+").length; // 英文单词数
}
}
}
使用自定义策略:
@Override
public BatchingStrategy createBatchingStrategy() {
return new TokenBasedBatchingStrategy(2000);
}
# 接口配置
所有实现的批量翻译引擎接口都必须在模块的 build.gradle 中注册:
mtPlugin {
pluginID = "com.example.myplugin"
versionCode = 1
versionName = "v1.0"
name = "翻译插件"
interfaces = [
"com.example.myplugin.GoogleTranslationEngine",
]
}
# 相关接口
- TranslationEngine - 基础翻译引擎接口
- PluginContext - 插件上下文
- LocalString - 本地化文本