# 翻译引擎(TranslationEngine)

本文按以下顺序说明翻译引擎能力:

  1. 普通翻译接口(TranslationEngine
  2. 普通翻译接口的分隔线批量翻译(allowBatchTranslationBySeparator
  3. 批量翻译接口(BatchTranslationEngine

# 一、普通翻译接口(TranslationEngine)

通过实现 TranslationEngine,可以为 MT 管理器的翻译模式和文本输入框增加单文本翻译能力。

# 接口概览

方法 说明
void init(PluginContext context) 初始化翻译引擎
PluginContext getContext() 获取插件上下文
String name() 获取翻译引擎名称
Configuration getConfiguration() 获取配置信息
List<String> loadSourceLanguages() 加载源语言列表
List<String> loadTargetLanguages(String sourceLanguage) 加载目标语言列表
String getLanguageDisplayName(String language) 将语言代码转换为显示名称
void beforeStart() 翻译开始前回调(UI线程)
void onStart() 翻译开始回调(子线程)
String translate(String text, String sourceLanguage, String targetLanguage) 执行翻译(子线程)
void onFinish() 翻译结束回调(子线程)
void afterFinish() 翻译结束后回调(UI线程)
boolean onError(Exception e) 错误处理回调(UI线程)

# BaseTranslationEngine 基类

BaseTranslationEngine 提供了 TranslationEngine 的默认实现:

  • 自动管理 PluginContext
  • 提供可重写的 init()(无参)
  • 提供可重写的 onBuildConfiguration(...)
  • 默认使用 MT 内置语言名称实现 getLanguageDisplayName(...)
  • 生命周期方法提供空实现
  • 默认将 autoRepairFormatSpecifiersError 设为 true(仅无参构造)

构造方式:

  • BaseTranslationEngine():先调用 onBuildConfiguration(builder) 再构建配置
  • BaseTranslationEngine(Configuration configuration):直接使用传入配置,不调用 onBuildConfiguration(...)

# 生命周期

TranslationEngine 生命周期如下:

  1. init(PluginContext)(每个引擎实例仅一次)
  2. beforeStart()(UI线程)
  3. onStart()(子线程)
  4. translate(...)(子线程,可多次)
  5. onFinish()(子线程)
  6. afterFinish()(UI线程)

如果 translate(...) 抛出异常,将调用 onError(...),且不会再调用 onFinish()afterFinish()

# 关键方法说明

# name

@NonNull
String name()

引擎显示名称。支持 {key} 本地化键,详见 本地化文本

# translate

@NonNull
String translate(String text, String sourceLanguage, String targetLanguage) throws IOException

执行单文本翻译。

  • text:待翻译文本。若 maxTranslationTextLength > 0,MT 调用该方法前会先按该长度口径拆分文本,传入的 text 不会超限
  • sourceLanguage:源语言代码
  • targetLanguage:目标语言代码
  • 返回值:翻译结果(不能为 null
  • 异常:网络/IO 错误可抛出 IOException

# 配置项(普通翻译接口)

继承 BaseTranslationEngine 时,通常通过重写 onBuildConfiguration(...) 来设置配置:

public class OnlineTranslationEngine extends BaseTranslationEngine {

    @Override
    protected void onBuildConfiguration(ConfigurationBuilder builder) {
        // 调用父类默认配置:会将 autoRepairFormatSpecifiersError 设为 true。
        super.onBuildConfiguration(builder);
        builder.setMaxTranslationTextLength(4000);
        builder.setTextLengthCalculator(text -> text.getBytes(StandardCharsets.UTF_8).length);
        builder.setAcceptTranslated(false);
    }
}
字段 默认值 说明
maxTranslationTextLength 0 单次翻译最大文本长度。<= 0 表示无限制
textLengthCalculator null 自定义长度计算器;为 null 时按 String.length()(字符数)计算
acceptTranslated false 是否允许把已翻译文本作为下一次输入
forceNotToSkipTranslated false 是否强制不跳过已翻译词条
targetLanguageMutable false 目标语言列表是否随源语言变化
autoRepairFormatSpecifiersError false 是否自动修复 %s 等格式化占位符误译
disableAutoHideLanguage false 是否禁用语言自动隐藏

注意:以上是 ConfigurationBuilder 的默认值。若继承 BaseTranslationEngine 并使用无参构造,autoRepairFormatSpecifiersError 默认会被设为 true

# setMaxTranslationTextLength

ConfigurationBuilder setMaxTranslationTextLength(int maxTranslationTextLength)

设置单次翻译最大长度,超长文本会自动拆分并拼接回填。

# setTextLengthCalculator

ConfigurationBuilder setTextLengthCalculator(TextLengthCalculator textLengthCalculator)

设置自定义文本长度计算器,用于定义 maxTranslationTextLength 的计量方式。

  • 默认值:null(按 String.length() 计算)
  • 典型场景:翻译 API 的限制基于 UTF-8 字节数、URL 编码长度等
  • 约束:实现应满足可加性,即 calculate(a) + calculate(b) == calculate(a + b),否则文本拆分时可能出现长度计算偏差
  • 不建议直接使用分词 Token 计数作为实现(通常不满足可加性)

# setAcceptTranslated

ConfigurationBuilder setAcceptTranslated(boolean acceptTranslated)

true 时,二次翻译可能使用“已翻译文本”作为输入;false 时始终用原文。

# setForceNotToSkipTranslated

ConfigurationBuilder setForceNotToSkipTranslated(boolean forceNotToSkipTranslated)

启用后,翻译模式中的“跳过已翻译”选项会被隐藏。

# setTargetLanguageMutable

ConfigurationBuilder setTargetLanguageMutable(boolean targetLanguageMutable)

启用后,源语言切换时会重新调用 loadTargetLanguages(sourceLanguage)

# setAutoRepairFormatSpecifiersError

ConfigurationBuilder setAutoRepairFormatSpecifiersError(boolean autoRepairFormatSpecifiersError)

自动修复常见格式化占位符误译(例如 %s 被翻译成 %S)。

# setDisableAutoHideLanguage

ConfigurationBuilder setDisableAutoHideLanguage(boolean disableAutoHideLanguage)

设置是否禁用语言自动隐藏功能。

# 示例(普通翻译接口)

public class OnlineTranslationEngine extends BaseTranslationEngine {

    private OkHttpClient httpClient;

    @Override
    protected void init() {
        httpClient = new OkHttpClient.Builder().build();
    }

    @Override
    protected void onBuildConfiguration(ConfigurationBuilder builder) {
        // 调用父类默认配置:会将 autoRepairFormatSpecifiersError 设为 true。
        super.onBuildConfiguration(builder);
        builder.setMaxTranslationTextLength(4000);
        builder.setTargetLanguageMutable(true);
    }

    @NonNull
    @Override
    public String name() {
        return "在线翻译";
    }

    @NonNull
    @Override
    public List<String> loadSourceLanguages() {
        return List.of("auto", "en", "zh-CN", "ja", "ko");
    }

    @NonNull
    @Override
    public List<String> loadTargetLanguages(String sourceLanguage) {
        return List.of("en", "zh-CN", "ja", "ko");
    }

    @NonNull
    @Override
    public String translate(String text, String sourceLanguage, String targetLanguage) throws IOException {
        Request request = new Request.Builder()
                .url("https://api.example.com/translate?text=" + text
                        + "&from=" + sourceLanguage
                        + "&to=" + targetLanguage)
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("翻译请求失败: " + response.code());
            }
            return response.body().string();
        }
    }

    @Override
    public boolean onError(Exception e) {
        if (e instanceof IOException) {
            getContext().showToastL("网络错误:" + e.getMessage());
            return true;
        }
        return false;
    }
}

# 二、普通翻译接口的分隔线批量翻译

本节仅适用于“只实现 TranslationEngine、未实现 BatchTranslationEngine”的引擎。

# 启用方式

ConfigurationBuilder setAllowBatchTranslationBySeparator(boolean allowBatchTranslationBySeparator)

将该选项设为 true 后,翻译模式会启用分隔线批量翻译。

# 处理流程

在翻译模式中,MT 会把多个词条拼接成一个字符串,通过一次 translate(...) 调用降低网络往返。

  1. 词条之间使用 "\n" + divider + "\n" 拼接。
  2. 分隔线会按当前批次动态生成,保证与本批原文不冲突。
  3. 如果翻译结果按分隔线拆分后数量不匹配,会自动切换另一套分隔线策略重试一次。
  4. 若重试后仍无法正确拆分,MT 会记录原始合并文本和翻译结果日志,并抛出 IOException
  5. 分批时会把“分隔线开销”一并计入长度估算,长度口径由 textLengthCalculator(默认 String.length())决定,而不是只看原文词条长度。
  6. 默认每批最多合并 100 条词条,达到上限会开启下一批请求。

# 与超长文本的关系

maxTranslationTextLength > 0 时,超长文本不会直接塞入分隔线合并请求,会先进入拆分翻译路径;其余文本仍按分隔线批量流程处理。

# 实现建议

  • translate(...) 尽量保持分隔线和换行不变(不要自动裁剪、重排、改写标点)。
  • 如果上游翻译服务无法稳定保留分隔线,建议关闭此优化(setAllowBatchTranslationBySeparator(false))。

# 三、批量翻译接口(BatchTranslationEngine)

当上游翻译 API 原生支持“多文本数组”请求时,建议实现 BatchTranslationEngine(通常继承 BaseBatchTranslationEngine)。

# 接口概览

BatchTranslationEngine 扩展自 TranslationEngine,除下表中的批量相关方法外,还继承 TranslationEngine 的全部方法(如生命周期、语言加载、错误处理等)。

方法 说明
String translate(String text, String sourceLanguage, String targetLanguage) 兼容签名(@Deprecated,MT 运行时不会调用;翻译逻辑请实现于 batchTranslate(...)
String[] batchTranslate(String[] texts, String sourceLanguage, String targetLanguage) 执行一批文本翻译(子线程,可多次)
BatchingStrategy createBatchingStrategy() 创建分批策略,控制 MT 如何拆批

# BatchingStrategy 概览

方法 说明
void reset() 每个新批次开始前调用,重置内部累计状态
boolean tryAdd(String text) 尝试把文本加入当前批次,返回 false 时 MT 会按批次流程回退/换批

# 核心契约

# translate(兼容签名)

@NonNull
@Deprecated
String translate(String text, String sourceLanguage, String targetLanguage) throws IOException

该方法仅为兼容 TranslationEngine 签名而保留,MT 运行时不会调用。

# batchTranslate

@NonNull
String[] batchTranslate(String[] texts, String sourceLanguage, String targetLanguage) throws IOException
  • texts:当前批次文本数组(不为空,可视为已通过当前 BatchingStrategy 容量校验)
  • 返回值:结果数组长度必须与 texts.length 一致,数组与数组元素都不能为 null
  • 异常:网络/IO 错误可抛出 IOException

# createBatchingStrategy

BatchingStrategy createBatchingStrategy()

返回分批策略实例,由 MT 调用 reset() / tryAdd(...) 来组织批次。

tryAdd(text) 返回 false 时,MT 的处理流程为:

  1. 当前批次不为空:先翻译当前批次,reset() 后再用同一文本重试一次 tryAdd(...)(同一文本可能触发两次 tryAdd)。
  2. 当前批次为空:拆分该文本使每一片段都符合当前策略,再逐段调用 batchTranslate(...) 翻译并合并结果。

# BaseBatchTranslationEngine 基类

BaseBatchTranslationEngineBaseTranslationEngine 基础上增加了 BatchTranslationEngine 的默认实现:

  • translate(...) 为兼容入口(final,调用即抛异常)
  • 默认 createBatchingStrategy() 返回 DefaultBatchingStrategy(100, 5000);其中该策略的长度口径使用 textLengthCalculator(未设置时按 String.length()

BaseBatchTranslationEngine 的构造方式:

  • BaseBatchTranslationEngine():先调用 onBuildConfiguration(builder) 再构建配置
  • BaseBatchTranslationEngine(Configuration configuration):直接使用传入配置,不调用 onBuildConfiguration(...)

# 配置语义(批量翻译接口)

  • allowBatchTranslationBySeparator:在实现 BatchTranslationEngine 时不起作用(不生效);批量流程由 batchTranslate(...)createBatchingStrategy() 接管。
  • maxTranslationTextLength:在实现 BatchTranslationEngine 时不起作用(不生效);批量路径下的单文本拆分由 BatchingStrategy 容量探测与分段流程决定。
  • acceptTranslatedforceNotToSkipTranslatedtargetLanguageMutableautoRepairFormatSpecifiersErrordisableAutoHideLanguage:仍按各自语义生效。

# 示例(批量翻译接口)

public class OnlineBatchTranslationEngine extends BaseBatchTranslationEngine {

    @Override
    protected void onBuildConfiguration(ConfigurationBuilder builder) {
        // 调用父类默认配置:会将 autoRepairFormatSpecifiersError 设为 true。
        super.onBuildConfiguration(builder);
        builder.setTargetLanguageMutable(true);
        // 这里使用 UTF-8 字节数作为长度口径,和下方 DefaultBatchingStrategy 的长度限制保持一致。
        builder.setTextLengthCalculator(text -> text.getBytes(StandardCharsets.UTF_8).length);
    }

    @NonNull
    @Override
    public String name() {
        return "在线翻译(批量)";
    }

    @NonNull
    @Override
    public List<String> loadSourceLanguages() {
        return List.of("auto", "en", "zh-CN", "ja", "ko");
    }

    @NonNull
    @Override
    public List<String> loadTargetLanguages(String sourceLanguage) {
        return List.of("en", "zh-CN", "ja", "ko");
    }

    @NonNull
    @Override
    public String[] batchTranslate(String[] texts, String sourceLanguage, String targetLanguage) throws IOException {
        String[] results = callBatchTranslationApi(texts, sourceLanguage, targetLanguage);
        if (results == null || results.length != texts.length) {
            throw new IOException("批量翻译返回结果长度不匹配");
        }
        return results;
    }

    @Override
    public BatchingStrategy createBatchingStrategy() {
        // maxTextLength=8000 按 UTF-8 字节数计算,而不是按字符数计算。
        return new DefaultBatchingStrategy(100, 8000);
    }
}

# 接口注册

所有翻译引擎实现都需要在模块的 build.gradle 中注册:

mtPlugin {
    pluginID = "com.example.myplugin"
    versionCode = 1
    versionName = "v1.0"
    name = "翻译插件"

    interfaces = [
        "com.example.myplugin.OnlineTranslationEngine",
        "com.example.myplugin.OnlineBatchTranslationEngine",
    ]
}

# 相关接口