目前市面上的不少翻译,一般场景比较简单,都是纯文本翻译(可能会包含换行\n之类的),但是最近遇到一个需求是要实现富文本里面的翻译,这里的翻译很大的概率会有格式,比如这种
file
我们需要带格式翻译,翻译成如下的效果
file
这种翻译跟大多数翻译一样,也需要一个预览的功能,便于用户预览翻译结果和微调翻译结果,然后点击替换就直接将翻译结果替换到富文本的内容里面,样式需要保留,比如上面的加粗。
做个类似选择功能,选区的都应该知道下列api
Selection Range
常用的方法就是window.getSelection()以及window.getSelection().getRangeAt(),具体的用法大家可以参看MDN,这里主要说下里面的坑。

怎么保留样式

首先大家想到的是如果里面有复杂样式,比如选区的文字有加粗、颜色什么的,选区里面还有图片啊,更深一层讲的话可能就是有里面会还有classstyleimg之类的,怎么过滤呢?其实我们这样看还只是看到的是视觉层面的东西,我们应该看里面都是一个个的element node:text,我们所谓的保留格式替换,其实也只是更换里面的文字内容信息即可,其它只有不是一股脑全换掉,就实现了我们的目标了。

怎么找到需要替换的文字

其实使用window.getSelection().toString()能获取到选择的文字,但是这种方式类似innerText获取的是当前节点及其字节的文字,所以会丢失层级关系。

根据我们的需求我们要使用的是遍历对应的dom节点的方法。常用的获取子节点的属性有两个:

  1. children返回的是HTMLCollection,不包含textNode
  2. childNodes返回的是NodeList,包含textNode

以下面的代码为例

<h2 class="lg_loginbox_title" id="logintitle">携程账号登录<a href="https://passport.ctrip.com/user/member/fastOrder" class="login_phone_number getOrder">手机号查单<i>></i></a></h2>

我们分别根据上述方式看看返回
file
很显然,这里我们需要使用childNodes
既然遍历,就需要知道入口,我们从哪里开始遍历呢?
这就要用到前面说的Range了。
我们以这样的选区为例,我们模板就是获取到“账号登录”和“手机号”这几个文字并实现替换。
file
我们使用widow.getSelection().getRangeAt(0).cloneContents(),目前我们都是只有一个选区,所以用getRangeAt(0)获取第一个选区的Range
file
返回的fragment是就是我们选区里面的dom结构,并且classstyle以及层级结构都保留了,这个API为我们做了很大贡献。
我们直接遍历它就能获取到选区的所有文字了,我们就按照深度遍历来将选区内的文字放到一个数组里面。我们就可以将数组['账号登录', '手机号']进行翻译得到['account login', 'phone number']。

怎么将翻译好的文字替换到原来位置

这就需要我们找到选区在当前dom的位置了,这时我们还是看看Range
file
window.getSelection.getRangeAt(0).commonAncestorContainer返回的是选区的节点的最近一层的共同父节点。
file
可以看到我们现在的选区其实都在h2里面,的确如此。
现在唯一的难点就是怎么找到文字的起始位置了,如果像我们上面的选区那样,用户选取的只是文字的一部分怎么办,比如“携程账号登录”只选取“账号登录”,“手机号查单”只选取“手机号”。
我们有两个办法可以做到
file

  1. window.getSelection.getRangeAt(0).startContainerwindow.getSelection.getRangeAt(0).startOffset可以获取到“账号登录”,因为“账号登录”就是从“携程账号登录”的第2位开始的(0为第一位,下同),就是说startContainer里面的第2位开始都是选区内容了。
  2. window.getSelection.getRangeAt(0).endContainerwindow.getSelection.getRangeAt(0).endOffset可以获取到“手机号”,因为“手机号”就是从“手机号查单”的第3位开始结束的,类似slice的前闭后开原则,就是说endContainer里面的第3位开始都是选区外了。
    到此,是不是觉得这个需求有眉目了,能搞定了?剩下的我们下篇再讲。

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据