国际化翻译平台文档解析 2.0 技术原理解析

发表于 1年以前  | 总阅读数:340 次

字节跳动国际化翻译平台为业务提供高效专业的【平台+服务】本地化一站式解决方案,简化本地化管理流程,提高多语言内容管理效率,助力产品出海。除文案管理外,国际化翻译平台 也提供文案、文档、视频等多模态翻译服务。目前不仅为公司内部大部分业务线提供服务,还通过火山引擎为外部客户提供服务,详见 https://www.volcengine.com/product/i18ntranslate。

一、背景

随着业务快速发展,国际化翻译平台文档翻译从最初仅支持 word 文档,到目前支持 8 种文档类型,其中核心 Node.js SDK(以下称为“文档解析 1.0”)变得越来越难以维护,究其根本,文档解析 1.0 对于每种文档类型的解析、还原、机器翻译、字数计算等能力,都单独维护一套逻辑,其优势是在文档解析开发的 0-1 阶段可以快速完成每种格式的能力建设,但随着后期优化需求以及新文档格式的增加,这种独立式架构的弊端会愈发凸显——改动某个逻辑需要同时修改 N 套代码、新接入一种文档格式需要重新编写几乎完全重复的业务逻辑。为了更好的支持文档翻译业务的高效维护与未来更多文档格式扩展的需求,有必要对现有架构进行一次彻底的重构升级。

二、重构收益

  1. SDK 核心逻辑代码量减少 70%+(6000+ 行 -> 2000 行)
  2. 受益于新架构的高可维护性与可扩展性:

a. lark 文档 2.0 还原效率提升 10 倍以上 b. 使用 SDK 的 Node BFF 项目文档解析相关逻辑代码量降低 80%+ c. 实现了基于 TS Decorator 与 FaaS 的 SDK 数据可视化

我们可以看到重构收益还是比较明显的,那么文档解析 2.0 的新架构具体是怎么样,以及是如何实现的呢?接下来的技术原理部分会进行详细解析。

三、技术原理

3.1 架构设计

让我们回到文档解析与还原的本质,解析的本质是将文档对应格式的文件转化为一套目标 DSL,而还原的本质则是将目标 DSL 转化为文件 DSL 然后生成文件。也就是说,所有类型文档的解析与还原流程都可以抽象为如下过程:

因此,可以将解析与还原抽象为文档的底层能力。在国际化翻译平台的业务场景下,文档经过解析之后需要转化为句段(Segments,如果有样式信息,则存储在每个 segment 的标签中),对于 segments 化之后的数据,有分句(根据标点符号进行句段划分)、机器翻译、统计字数、合并句段等需求。对于这些功能层面的需求,参考 TCP/IP 四层模型的话,可以类比为最上层的应用层

这样我们便有了底层解析与还原能力层,以及上层的应用层。但光有这两层架构还无法达到我们的最终目标,为什么呢?因为对于每种不同的文档类型,其解析后的数据结构都是各不相同的,而不同的数据结构就需要应用层的每个功能针对不同的数据结构做适配,随着不同文档数据结构的增多,在应用层做适配的复杂度也会呈指数级上升。如何解决这个问题呢?一个经典设计模式——Adapter(适配器)模式可以给我们答案。我们可以在底层与应用层中间,加入一个 Adapter 层,将每种文档的解析结果统一为一致的 DSL。

如此一来,国际化翻译平台文档解析 2.0 的三层架构便呼之欲出:

其中底层 parser 层负责所有文档的解析与还原,最上层 feature 应用层负责统一的能力暴露,中间层 adapter 则负责将 parser 层的解析与还原结果适配为统一 DSL

3.2 分层实现

解析完总体架构之后,我们可以继续往下深入,看看各层的具体实现。

其中 Parser 层设计如下:

class SomeTypeParser {
    constructor(config) {}

    @type2bridge
    parse() {}

    @bridge2type
    restore() {}
}

细心的读者可能发现了 parser 的实现借助了 ts decorator,这部分会在 3.3 SDK 数据可视化部分详解。这里可以先略过这部分。

Feature

这里需要前置说明一下 CAT 的含义,CAT 的意思是计算机辅助翻译(Computer aided translation,CAT),也是国际化翻译平台文档解析的目标,国际化翻译平台经过文档解析生成 segments,翻译人员在由 segments 所构成的 CAT 编辑器中进行翻译操作。

国际化翻译平台 CAT 编辑器目前如下图所示:

Feature 层设计如下:

class CAT {
    // 文档解析,返回结果就是segments
    adaptCAT(type, config) {}

    // 机器翻译
    adaptCATWithMT() {}

    // 字数统计
    countWords() {}

    // 生成文档
    genDoc() {}

    // 自定义解析器
    apply(type, parser) {}
}

Adapter

Adapter 层主要是以下两个方法,分别是文档数据转中间层数据,与中间层数据转文档数据。

export const type2bridge = () => {

}

export const bridge2type = () => {

}

统一 DSL

{
    blockId: string
    elements: {
        type: string // 文本:text, 其他:other,对应着单、双标签
        textRun?: {
            style: Object // 样式
            content: string // 文本
        }
        location: {
            start: number
            end: number
        }
    }[]
}

以上便是国际化翻译平台文档解析 2.0 架构的设计过程与分层实现。接下来对 SDK 中 decorator 的使用做更详细的解析,有了它的帮助,SDK 底层的解析、还原与数据可视化的具体实现变得更加简洁。

3.3 Decorator 的使用与 SDK 数据可视化

Decorator 可以用来增强类中的方法,使其具备额外的功能,提供增强后的接口。

以国际化翻译平台 TXT 文档 parser 层的具体实现为例,

export default class TxtParser {
  @txt2bridge
  async parse(token: string | Buffer) {
    const buffer = path2buffer(token)
    return [buffer.toString()]
  }

  @bridge2txt
  async restore(blocks: string[], _raw: string) {
    return { buffer: Buffer.from(blocks.join()) }
  }
}

我们可以看到两个装饰器方法txt2bridgebridge2txt,实现如下:

export const txt2bridge = proxyForReturnValue<string[], TxtParser>(function (blocks) {
  const bridgeBlocks = blocks.map((block, index) => {
    const bridgeBlock: Bridge.Block = {
      style: {},
      blockId: index + '',
      elements: [
        {
          type: 'text',
          textRun: {
            content: block,
            style: {}
          },
          location: {
            start: 0,
            end: block.length
          }
        }
      ]
    }
    return bridgeBlock
  })

  return { raw: JSON.stringify(blocks), blocks: bridgeBlocks }
})

export const bridge2txt = proxyForParam<string[], TxtParser>(function (blocks, _raw) {
  return blocks.map(block => {
    return block.elements.map(ele => ele.textRun.content).join()
  })
})

我们可以观察到这两个装饰器并不是直接实现的,是经过proxyForParamproxyForReturnValue而间接实现,这里又涉及到了 proxy 设计模式的使用,用来对返回值与函数参数做一次转化。这两个代理方法的具体实现如下:

Proxy 被用于做访问控制(access control),对本身想要访问的对象做转化,并且对外提供相同的接口。

export function proxyForReturnValue<T, THIS, C = { [key: string]: any }>(
  proxy: (this: THIS, data: T, config?: C) => Bridge.Data | Promise<Bridge.Data>
) {
  return function (
    _target: any,
    _propertyName: string,
    descriptor: TypedPropertyDescriptor<Function>
  ) {
    const method = descriptor.value
    descriptor.value = async function (token: string, config?: C) {
      const data = await method.call(this, token, config)
      return await proxy.call(this, data, config)
    }
  }
}

export function proxyForParam<T, THIS, C = { [key: string]: any }>(
  proxy: (this: THIS, blocks: Bridge.Block[], raw: string | Buffer, config?: C) => T | Promise<T>
) {
  return function (
    _target: any,
    _propertyName: string,
    descriptor: TypedPropertyDescriptor<Function>
  ) {
    const method = descriptor.value
    descriptor.value = async function (blocks: Bridge.Block[], raw: string | Buffer, config?: C) {
      return await method.call(this, await proxy.call(this, blocks, raw, config), raw, config)
    }
  }
}

通过上述实现,我们可以看到 Decorator 真正起作用的语句(descriptor.value = () => {})在这两个 proxy 方法内实现。通过 proxy 设计模式的使用,我们可以将 Decorator 的实现与业务逻辑进行解耦,更加便于后续维护。

通过 Decorator,我们可以对方法做增强处理,使之具备更多的能力,因此,解析与还原过程中的数据收集也可以通过 Decorator 来实现。在 TxtParser 中,我们只需要增加一个trace装饰器即可:


export default class TxtParser {
  @trace('txt', 'parse')
  @txt2bridge
  async parse(token: string | Buffer) {
    const buffer = path2buffer(token)
    return [buffer.toString()]
  }

  @trace('txt', 'restore')
  @bridge2txt
  async restore(blocks: string[], _raw: string) {
    return { buffer: Buffer.from(blocks.join()) }
  }
}

trace装饰器实现如下:

export function trace ( docType: DocType, operateType: 'parse' | 'restore') {
  return function (
    _target: any,
    _propertyName: string,
    descriptor: TypedPropertyDescriptor<Function>
  ) {
    // prepare
    const method = descriptor.value
    descriptor.value = async function (...args) {
      try {
        // ...
        const res = await method.apply ( this, args)
        // 信息收集,例如操作时间、CPU、内存等消耗情况
        // 通过FaaS上报文档操作过程中的统计信息
        return res
      } catch (error) {
        // 错误处理,通过FaaS上报错误信息
      }
    }
  }
}

我们在装饰器中进行信息收集,并把收集到的数据上报到 FaaS 平台之后,我们就可以开发一个运营后台来将这些数据进行可视化展现,这也是国际化翻译平台的文档分析后台的由来。

3.4 Larkdocx 批量更新关键实现

最后可以再分享一下国际化翻译平台文档解析 2.0 中 Larkdocx 批量更新实现的一个关键。起初,国际化翻译平台对飞书文档 2.0 的还原速度非常慢,对于大型文档来说,平台几乎无法正常导出,且飞书开放平台有同篇文档 QPS <= 3 的限制,如何提升还原速度,并满足 QPS 限制成为了又一个核心问题。

之前飞书文档 2.0 还原慢的主要原因在于每个 Block 的还原都需要调用一次 updateBlock[1],对于大文档来说,block 总数有上千个,更新请求可达上千次,自然很慢。经过调研,飞书开放平台在不久之前开放了批量更新块接口[2],正好可以解决请求过多的问题。另外,由于飞书开放平台有同篇文档同一接口 QPS <= 3 的限制,为了同时实现批量更新分块与限流,需要开发一个专用的限流调度器,在传统限流调度器的基础上需要增加错误重试的功能,具体实现如下:

async function concurrentFetch(poolLimit, iterable, iteratorFn) {
  const result = [];
  const retry = [];
  const executing = new Set();
  for (const item of iterable) {
    const p = Promise.resolve().then(() => iteratorFn(item));
    result.push(p);
    executing.add(p);
    const clean = () => executing.delete(p);
    p.then((r) =>
               r ? retry.push(Promise.resolve().then(() => iteratorFn(r))) : clean())
        .catch(clean);
    if (executing.size >= poolLimit) {
      await Promise.race(executing);
    }
  }

  return Promise.all(result).then(() => Promise.all(retry));
}

// Usage
async batchUpdateBlock(blocks: Lark.Block[], documentId: string) {
  const blockUpdates = blocks.map(block => this.getUpdateForBlock(block)).filter(Boolean)
  const chunkedBlockUpdates = chunk(blockUpdates, MAX_BATCH_SIZE)

  const batchUpdateWithErrorHandle = async chunk => {
    return await this.lark
        .batchUpdateBlockForDocx(documentId, {
          requests : chunk
        })
        .then(resp => {
          // 如果遇到无权限报错,需要进行降级处理
          if (resp?.data.code === LARK_BLOCK_UPDATE_ERROR_CODE.ForBidden) {
            return this.downgradeUpdatesForBlocks(chunk)
          }
          // ...其余兜底处理操作
          return null
        })
  }

  // 3的QPS限制,用batchUpdateWithErrorHandle中的then条件判断决定是否降级处理与重试
  await concurrentFetch(3, chunkedBlockUpdates, batchUpdateWithErrorHandle)
}

四、总结

通过对国际化翻译平台文档解析当前所面临的问题分析,将 SDK 中解析与还原、各种翻译能力还原到本质,最终抽象出一套适配当前需求与未来发展的三层架构——文档解析 2.0。其中的具体实现使用了 Decorator 装饰器、Adapter 与 Proxy 设计模式、限流调度器等等,从中我们也可以看出平时似乎不太常用的语法、设计模式与热门面试题等等其实都有它们各自发挥作用的场合,这也是为什么我们要更加注重平时基础知识积累的原因——所有强大的上层实现,都离不开底层更强大的基础知识。

本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/KHEBKiJYqPEzI21frY6R5w

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:8月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:8月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:7月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:8月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:8月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:8月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:8月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:8月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:8月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:8月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:8月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:8月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:8月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:8月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:8月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:8月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:8月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:8月以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  236886次阅读
vscode超好用的代码书签插件Bookmarks 1年以前  |  6982次阅读
 目录