使用 ChatGPT 翻译 FreeSWITCH 官方文档

Published on

RTS 最近使用 ChatGPT 将 FreeSWITCH 官方文档翻译成了中文,而在翻译过程中也遇到不少问题和坑,这里就用本文记录下整个翻译过程。另外当前中文版 FreeSWITCH 文档已经上线了,欢迎大家阅读指正机翻的错误。

翻译工具的选型

在开始翻译工作之前,找了几种常用的翻译工具做了个简单对比,结果如下:

DeepLChatGPTAzure 文本翻译 API
正文翻译准确度很好一般一般
markdown 格式保留一般很好

通过以上简单对比,最终确定用 ChatGPT 来做文档翻译,因为这样能节省大量解析文档的时间。而 DeepL 和 Azure 文本翻译 API 只适合纯文本的文档翻译,如果用他们来做需要不少的时间浪费在 markdown 文档解析上。

哪些基于 ChatGPT 翻译工具的轮子

在确定了翻译工具后,就基于整个工具在 Github 搜索,看能否找到现成的轮子直接拿来用。结果还真找到两个比较合适的轮子,如下:

开始先使用的是ChatGPT-for-Translation,因为看介绍它会在翻译后单独存一份中英双语版本,不过当笔者使用次工具运行时发现OpenAI接口是老版本的,新版本的调用方法已经改了,下文是最新的调用方法:

client = openai.OpenAI(api_key=key)
completion = client.chat.completions.create(
    model=options.model,
    messages=messages,
)

修复此问题后,程序可以正常跑了,期间抽检了几个介绍类的文档,也基本正常。但是历经十几个小时运行完毕后,抽查翻译后的文件发现有大量代码也被一起翻译了。比如下面这段原文:

<students>
    <student>
        <name>John</name>
        <age>20</age>
        <gender>Male</gender>
    </student>
    <student>
        <name>Emily</name>
        <age>18</age>
        <gender>Female</gender>
    </student>
    <student>
        <name>Michael</name>
        <age>21</age>
        <gender>Male</gender>
    </student>
</students>

被翻译为:

<学生们>
    <学生>
        <名字>John</名字>
        <年龄>20</年龄>
        <性别></性别>
    </学生>
    <学生>
        <名字>Emily</名字>
        <年龄>18</年龄>
        <性别></性别>
    </学生>
    <学生>
        <名字>Michael</名字>
        <年龄>21</年龄>
        <性别></性别>
    </学生>
</学生们>

同时还有部分Markdown的段落标记##被删除了,这时仔细看了该工具的源码才发现它是按行切片,然后交由 ChatGPT 进行翻译,因此即便使用下面的prompt描述,也依然出现大量Markdown格式被破坏,同时代码或配置部分被翻译的问题。

messages = [{
        'role': 'system',
        'content': 'You are a translator assistant.'
    }, {
        "role":
        "user",
        "content":
        f"Translate the following text into {target_language}. You must maintain the original markdown format. Return only the translation and nothing else:\n{text}",
    }]

后面又对比了Auto-i18n的分段方式,发现它的分段处理逻辑要好点,是根据连续两个换行符进行切片,然后交给 ChatGPT 进行翻译。这样基本保留了原文的代码块,也不会造成代码部分被翻译的问题。

文档中的坑

&lt;< 的坑

解决文章分段切片问题后,重新将所有文档翻译一遍,然后在将文档渲染成 html 格式时,报错抛出大量语法错误,如下:

SyntaxError: /opt/jenkins/rts-docs/docs/FreeSWITCH-Explained/Auxiliary-Knowledge-and-Utilities/Multi-home-tutorial/index.mdx: Expected corresponding JSX closing tag for <gateway>. (453:192)
  451 | <p>{`假设您已经设置了分机1013(物理电话的语音邮件会话),并留下了一个关于每个人都很忙,请留下您的回拨号码的消息,那么您就可以开始使用了(记得重新加载XML文件或者关闭/启动)。我的电话(诚然是一部旧的Grandstream GXP-2000)在我拨打并错误留言后就会出现红色闪烁的消息指示灯(我是1013) 。根据《仔细观察您的拨号计划》中的适当号码,您现在可以获取您的语音邮件并处理它。`}</p>
  452 | <h3 {...{"id":"添加另一个did"}}>{`添加另一个DID`}</h3>
> 453 | <p>{`假设您希望为家庭或企业拥有多个直拨号码,我们在注册SIP提供商时看到了如何注册一个DID号码。如果您要拥有多个号码,您需要设置另一个与之前相同的`}<gateway>{`。然而,如果您想使用同一提供商,我们需要更改/ usr / local / freeswitch / conf / sip_profiles / external目录中的XML文件的语法。以下是例子:`}</p>
      |                                                                                                                                                                                                 ^
  454 | <p>{`我的注册和使用一个DID的`}{`~`}{`/conf/sip`}{`_`}{`profiles/external/urban.xml文件:`}</p>
  455 | <pre><code parentName="pre" {...{"className":"language-xml"}}>{`<include>
  456 |   <gateway name="xxx.xxx.xxx.xxx">

仔细对比原文发现造成问题的原因是正文内出现了<,而原文将<转义为了&lt;。这个也不算是 ChatGPT 的锅,原因是原来的 markdown 文件不规范。后面我们手工改了。

代码块被破坏的坑

原文有部分代码块由于内容过长,会出现超出被 ChatGPT 分为两个代码块的问题。同时分段的代码块,有些还会被 ChatGPT 根据上线文的理解,加上自己的一部分代码,翻译异常示例如下:

```xml
<extension name="local_extension">
    <condition field="destination_number" expression="^\d+$">
      <action application="transfer" data="user/${destination_number}@exch.cluecon.com"/>
    </condition>
</extension>
```

要在Exchange服务器上启用统一消息服务器角色,请运行以下命令:

```powershell
Set-UMServer -Identity:'exch.cluecon.com' -DialPlans 4DigitDialPlan
```

要为特定用户(_Ext1000_)启用UM功能,请使用以下命令:

```powershell
Enable-UMMailbox -Identity:'CLUECON\EXT1000' -Pin '1234' -PinExpired $false -UMMailboxPolicy:'4DigitDialPlan Default Policy' -Extensions '1000'
```

可选:要在Exchange服务器上启用传真检测,请在具有统一消息服务器角色的Exchange 2007计算机的`\\Program Files\\Microsoft\\Exchange\\bin\\globcfg.xml`文件中将`EnableInbandFaxDetection`设置为True
```xml
  <extension name="本地分机">
    <condition field="destination_number" expression="^(10[01][0-9])$">
      <action application="set" data="dialed_ext=$1"/>
      <action application="export" data="dialed_ext=$1"/>
    </condition>
    <condition field="destination_number" expression="^${caller_id_number}$">
      <action application="answer"/>
      <action application="sleep" data="1000"/>
      <action application="export" data="sip_h_Diversion=${dialed_ext}"/>
      <action application="bridge" data="{absolute_codec_string=PCMA}sofia/gateway/exch.cluecon.com/${dialed_ext}"/>
      <anti-action application="bind_meta_app" data="1 b s execute_extension::dx XML features"/>
      <anti-action application="bind_meta_app" data="2 b s record_session::$${base_dir}/recordings/${caller_id_number}.${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/>
      <anti-action application="bind_meta_app" data="3 b s execute_extension::cf XML features"/>
      <anti-action application="set" data="transfer_ringback=${us-ring}"/>
      <anti-action application="set" data="call_timeout=30"/>
      <anti-action application="set" data="hangup_after_bridge=true"/>
      <anti-action application="set" data="continue_on_fail=true"/>
      <anti-action application="db" data="insert/call_return/${dialed_ext}/${caller_id_number}"/>
      <anti-action application="db" data="insert/last_dial_ext/${dialed_ext}/${uuid}"/>
      <anti-action application="bridge" data="user/${dialed_ext}@$${domain}"/>
      <anti-action application="answer"/>
      <anti-action application="sleep" data="1000"/>
      <anti-action application="voicemail" data="default $${domain} ${dialed_ext}"/>
      <anti-action application="export" data="sip_h_Diversion=${dialed_ext}"/>
      <anti-action application="bridge" data="{absolute_codec_string=PCMA}sofia/gateway/exch.cluecon.com/${dialed_ext}"/>
    </condition>
  </extension>
```

上文中 Exchange 部分正文和PowerShell命令部分都是 ChatGPT 自行添加的,这些在原文是没有的,下文是原文内容:

```xml
  <extension name="Local_Extension">
    <condition field="destination_number" expression="^(10[01][0-9])$">
      <action application="set" data="dialed_ext=$1"/>
      <action application="export" data="dialed_ext=$1"/>
    </condition>
    <condition field="destination_number" expression="^${caller_id_number}$">
      <action application="answer"/>
      <action application="sleep" data="1000"/>
      <action application="export" data="sip_h_Diversion=${dialed_ext}"/>
      <action application="bridge" data="{absolute_codec_string=PCMA}sofia/gateway/exch.cluecon.com/${dialed_ext}"/>
      <anti-action application="bind_meta_app" data="1 b s execute_extension::dx XML features"/>
      <anti-action application="bind_meta_app" data="2 b s record_session::$${base_dir}/recordings/${caller_id_number}.${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/>
      <anti-action application="bind_meta_app" data="3 b s execute_extension::cf XML features"/>
      <anti-action application="set" data="transfer_ringback=${us-ring}"/>
      <anti-action application="set" data="call_timeout=30"/>
      <anti-action application="set" data="hangup_after_bridge=true"/>
      <anti-action application="set" data="continue_on_fail=true"/>
      <anti-action application="db" data="insert/call_return/${dialed_ext}/${caller_id_number}"/>
      <anti-action application="db" data="insert/last_dial_ext/${dialed_ext}/${uuid}"/>
      <anti-action application="bridge" data="user/${dialed_ext}@$${domain}"/>
      <anti-action application="answer"/>
      <anti-action application="sleep" data="1000"/>
      <anti-action application="voicemail" data="default $${domain} ${dialed_ext}"/>
      <anti-action application="export" data="sip_h_Diversion=${dialed_ext}"/>
      <anti-action application="bridge" data="{absolute_codec_string=PCMA}sofia/gateway/exch.cluecon.com/${dialed_ext}"/>
    </condition>
  </extension>
```

而这个坑不太好处理,试着加过一些禁止自行解释的prompt也没啥效果。如果读者你有好的解决办法,欢迎到 RTS 社区分享下。

总结

FreeSWITCH 是一个历史悠久的项目,其官方文档都是社区力量贡献的,格式审查也不严格,风格也不统一。尤其是多年以来,使用过很多不同的 Wiki 系统,在格式转换过程中也不免会出现一些问题。在转换到现行的 Markdown 格式之前,使用的是 Confluence Wiki 系统。

虽然 ChatGPT 翻译后的文档还是有不少缺点,但是依然能帮助开发人员节省大量文档翻译的时间。尤其是对比传统的翻译工具,能自动理解文件格式,在不需要对代码和注释做特殊处理的情况下,依然能很好的只翻译注释部分,同时不修改代码或配置内容。

如果要更好地处理文档,ChatGPT 的大上下文应该能解决一切问题,只是大上下文会比较贵。如果在上下文 Token 受限的情况下,使用 Markdown 语法解析工具(有很多工具都可以解析 Markdown 语法,如一些 Node.js 模块或 Pandoc 等),将代码块完整地切片,应该可以达到更好的效果。不过,我们并没有尝试这些方法,现在的解决方案勉强够用。

接下来,就手工修吧,欢迎大家多看看,多提 PR。Github 地址: https://github.com/rts-cn/docs