相信有不少人很讨厌,谷歌翻译把代码或其他一些不该翻译的东西翻译了,观看感受奇差,于是我就有了做一款部分选择翻译的工具的想法
首先我想到的是使用css选择器,选择那部分不翻译,不过实现太麻烦,改为那部分选择并翻译。
整个项目使用Java开发,不太方便在浏览器使用,可以考虑写个js版,作为浏览器插件使用,每个页面运行时执行一下,当然也可以在js中执行其他逻辑如移除广告等。
Javs使用的Jsoup对文档进行解析
第一版
先使用CSS选择器,在使用正则表达式把内部文本翻译,可以复用StringBuffer,只需要一遍就完成了,效率比较高。
public class HtmlTranslator {
private String source;
private String[] select;
private StringBuffer buffer;
private Pattern pattern;
private BaiduTranslator baiduTranslator;
public HtmlTranslator(String source) {
this.source=source;
initData();
}
public HtmlTranslator() {
this(null);
}
private void initData() {
buffer=new StringBuffer();
pattern = Pattern.compile(">(.*?)<",Pattern.DOTALL);
baiduTranslator=new BaiduTranslator("en","zh");
}
public void setSelect(String ...select) {
this.select=select;
}
@SneakyThrows
public void translation(String path){
source=path;
Files.write(Paths.get(path),translation().getBytes());
}
@SneakyThrows
public String translation() {
if(source==null)throw new RuntimeException("必须指定目标文件");
Document document = Jsoup.parse(new File(source),"UTF-8");
handleDocument(document);
return document.html();
}
private void handleDocument(Document document) {
if(select==null)return;
for (int i = 0; i < select.length; i++) {
handleSelect(document,select[i]);
}
}
// 处理一个选择器
private void handleSelect(Document document, String select) {
Elements elements = document.select(select);
elements.forEach(element -> {//toString 获取标签的全部内容
buffer.setLength(0);
// System.out.println(element.toString());
Matcher matcher = pattern.matcher(element.toString());
while (matcher.find()){
String translation=Translation(matcher.group(1));
matcher.appendReplacement(buffer,">"+translation+"<");
}
matcher.appendTail(buffer);
trimBuffer();
element.html(buffer.toString());//直接插入 文本当html不转义
});
}
// 翻译
@SneakyThrows
private String Translation(String str) {
// System.out.println(str);
if(str.trim().isEmpty())return "";
// return baiduTranslator.translationHtml(str);
return "翻译后的文本";
}
// 移除 两边的标签
private void trimBuffer() {
buffer.setLength(buffer.lastIndexOf("<"));
buffer.delete(0,buffer.indexOf(">")+1);
}
}
不过缺点也比较明显,没有对选中标签内的嵌套标签进行再次筛选,选择元素必须足够小
第二版
使用递归,对选择的标签当作文档再次选择,精确选择
public class HtmlTranslator {
private String[] select;
private BaiduTranslator baiduTranslator;
public HtmlTranslator() {
baiduTranslator=new BaiduTranslator("en","zh");
}
public void setSelect(String ...select) {
this.select=select;
}
@SneakyThrows
public void translationHtml(String path){
Document document = Jsoup.parse(new File(path),"UTF-8");
handleElement(document);
// System.out.println(document);//TODO
Files.write(Paths.get(path),document.html().getBytes());
}
private void handleElement(Element element) {
if(select==null)return;
for (int i = 0; i < select.length; i++) {
handleSelect(element,select[i]);
}
}
// 处理一个选择器
private void handleSelect(Element parentElement, String select) {
Elements elements = parentElement.select(select);
elements.forEach(sonElement -> {
StringBuilder buffer = new StringBuilder();
List<Node> nodes = sonElement.childNodes();
for (Node node:nodes){
if(node instanceof TextNode){
TextNode textNode= (TextNode) node;
String text = textNode.text();
String translation = translation(text);
buffer.append(translation);
}else if(node instanceof Element){
Element element= (Element) node;
handleElement(element);
buffer.append(element);
}
}
sonElement.html(buffer.toString());//直接插入 文本当html不转义
});
}
// 翻译
@SneakyThrows
private String translation(String str) {
// System.out.println(str);
if(str.trim().isEmpty())return "";
// return baiduTranslator.translationHtml(str);
return "翻译后的文本";
}
}
虽然因为递归StringBuilder不能复用,而且递归降低了一点效率(实测并没有降低多少),但是选择变得非常精确。
使用翻译文档的方式进行测试
这里文档选择GameMaker Studio 2英文帮助文档,因为百度翻译有访问频率限制,暂时,将翻译后的文本使用 翻译后的文本 表示
共翻译2427个页面用时11s
原页面
翻译后
可见就算要保护的文本与要翻译的文本层层嵌套,还是可以正确识别并翻译css选择的文本
再看看最重要的代码
代码被完美的保留了下来,连参数说明中的代码引用也被保留的下来
最后
希望谷歌翻译早日开发出鼠标选中元素保护不翻译的功能,谷歌浏览器本来就支持右键选中元素css,它要做应该不会太难