文件对比与差异提取的实现
2015-08-24 15:41
363 查看
File Comparison Tool
前言
一款需要多国语言的应用(真正受欢迎的至少需要中文和英文,不管是PC端还是移动端),那么应用程序开发过程中定义的字串文件就需要进行翻译,而这项工作在大公司往往是多人(甚至是多部门)协同完成。比如移动端开发常见的为strings.xml,默认是在res/values目录下,严格来说该目录对应的是英文环境,而中文简体环境规范的目录为res/values-zh-rCH。
需求
那么问题来了,开发人员定义好应用中需要用到的字串(最初可能是英文,也可能是中文)并且送翻(提交给专门的翻译人员进行字串翻译)后,怎么判断返回的翻译结果是完整的呢?或者说,中途接手一个项目,怎么快速掌握原来的字串翻译情况呢?
之前有篇文章讲过Lint工具可以分析出项目中资源冗余及翻译不全等问题,但给出的信息不够简洁,用其来完成专门的字串翻译文件对比并不是很合适。通过下图可以看出,对于某一条字串的翻译缺失,Lint给出的信息是什么样的。
![](http://images0.cnblogs.com/blog2015/450484/201508/241527352027352.png)
协同工作的好处是,项目文件的commit、pull、push及review均可以在项目管理工具或者工具对应的命令行环境中进行。而review还可以在项目对应的远程网页中进行,便于管理者查看新提交的文件到底进行了哪些改动。但是其和文件比较工具(如beyond compare)类似,只能一个一个文件地查看,若想提取出所有的修改结果(或者不同点),只能认为地复制与黏贴到自己建立的文件中。
![](http://images0.cnblogs.com/blog2015/450484/201508/241528226243273.png)
所以,若想以最初的strings.xml为基础,查看其它语言环境的翻译情况,并将未翻译的字串统一写入到一个特定的文件中,就需要自己进行实现了。
实现
用来实现该功能的语言平台为Eclipse中的Java,过程大致可以分为四步:
1、 以项目res目录为起始路径,获取其中包含的所有子文件夹;
2、 在1的基础上,获取名称中包含values的文件夹,因为其他文件夹和strings.xml无关(当然,这是一般而言的);
3、 在2的基础上,获取存在的strings.xml的完整路径;
4、 文件内容对比,针对最初的strings.xml文件中的某一条字串(需要翻译的,即不包括文件定义头<?xml ... ?>、<resources>…</resources>及显示说明不需翻译的字串等),若其他语言环境对应的strings.xml文件中不存在该字串的翻译,那么就将该字串的行号和内容写入结果文件。为了方便阅读和修改,结果是以文件为单位,而不是像Lint工具那样以字串为单位,即原来是某条字串未进行翻译的国家有A、B、C、D、……,而现在是某个语言环境未翻译的字串有A、B、C、D、……;
完整的Java实现代码如下:
从代码可以看出,以上描述的四个步骤对应的方法分别为getSubDirs()、getValuesDirs()、getfileFullNames()、compareFiles()。
在结果文件Result.xml的开头,写入了当前时间,其对应字串的获取方式为:new Date().toString()。
注意,如代码138-140行所示,在将某个语言环境对应的strings.xml文件完整名称添加入ArrayList对象fileFullNames中之前,对其存在性进行了判断,也可以在后面的文件比较环节进行个文件的存在性判断,但不能不判断就进行文件的读取(多余,也容易引起异常)。
res目录下的文件夹获取情况:
![](http://images0.cnblogs.com/blog2015/450484/201508/241530144833514.png)
res目录下的名称中包含values的文件夹获取情况:
![](http://images0.cnblogs.com/blog2015/450484/201508/241530284833924.png)
res目录下的名称中包含values的文件夹中strings.xml的完整名称获取情况:
![](http://images0.cnblogs.com/blog2015/450484/201508/241530413899134.png)
所有翻译字串最终的对比结果文件Result.xml的部分内容为:
![](http://images0.cnblogs.com/blog2015/450484/201508/241544441082490.png)
有了这样的描述,就很容易查看当前多国语言的翻译情况了。这里的字串是随意敲的,不然命名的level也太低了。
总结
虽然实现的功能具有局限性,但已经可以满足上面的需求。正是因为一般通用的工具不能完成项目开发过程中遇到的所有情况,所以才要自己动手针对特定的需求进行实现。这篇文章只是起到抛砖引玉的作用,对于其他各种各样的场景,实现的思路还是相同的。
欢迎感兴趣的朋友一起讨论、学习与进步!
前言
一款需要多国语言的应用(真正受欢迎的至少需要中文和英文,不管是PC端还是移动端),那么应用程序开发过程中定义的字串文件就需要进行翻译,而这项工作在大公司往往是多人(甚至是多部门)协同完成。比如移动端开发常见的为strings.xml,默认是在res/values目录下,严格来说该目录对应的是英文环境,而中文简体环境规范的目录为res/values-zh-rCH。
需求
那么问题来了,开发人员定义好应用中需要用到的字串(最初可能是英文,也可能是中文)并且送翻(提交给专门的翻译人员进行字串翻译)后,怎么判断返回的翻译结果是完整的呢?或者说,中途接手一个项目,怎么快速掌握原来的字串翻译情况呢?
之前有篇文章讲过Lint工具可以分析出项目中资源冗余及翻译不全等问题,但给出的信息不够简洁,用其来完成专门的字串翻译文件对比并不是很合适。通过下图可以看出,对于某一条字串的翻译缺失,Lint给出的信息是什么样的。
![](http://images0.cnblogs.com/blog2015/450484/201508/241527352027352.png)
协同工作的好处是,项目文件的commit、pull、push及review均可以在项目管理工具或者工具对应的命令行环境中进行。而review还可以在项目对应的远程网页中进行,便于管理者查看新提交的文件到底进行了哪些改动。但是其和文件比较工具(如beyond compare)类似,只能一个一个文件地查看,若想提取出所有的修改结果(或者不同点),只能认为地复制与黏贴到自己建立的文件中。
![](http://images0.cnblogs.com/blog2015/450484/201508/241528226243273.png)
所以,若想以最初的strings.xml为基础,查看其它语言环境的翻译情况,并将未翻译的字串统一写入到一个特定的文件中,就需要自己进行实现了。
实现
用来实现该功能的语言平台为Eclipse中的Java,过程大致可以分为四步:
1、 以项目res目录为起始路径,获取其中包含的所有子文件夹;
2、 在1的基础上,获取名称中包含values的文件夹,因为其他文件夹和strings.xml无关(当然,这是一般而言的);
3、 在2的基础上,获取存在的strings.xml的完整路径;
4、 文件内容对比,针对最初的strings.xml文件中的某一条字串(需要翻译的,即不包括文件定义头<?xml ... ?>、<resources>…</resources>及显示说明不需翻译的字串等),若其他语言环境对应的strings.xml文件中不存在该字串的翻译,那么就将该字串的行号和内容写入结果文件。为了方便阅读和修改,结果是以文件为单位,而不是像Lint工具那样以字串为单位,即原来是某条字串未进行翻译的国家有A、B、C、D、……,而现在是某个语言环境未翻译的字串有A、B、C、D、……;
完整的Java实现代码如下:
package com.filediff; import java.awt.image.WritableRaster; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.time.chrono.JapaneseChronology; import java.util.ArrayList; import java.util.Date; import javax.swing.JFrame; @SuppressWarnings("serial") public class FileDiff extends JFrame { //resources root path private static String rootDir = "res"; //directory name values private static String dirFilter = "values"; //file name private static String fileName = "strings.xml"; //all directory in rootDir private static String[] subDirs = null; //all directory(name contain values) in rootDir private static ArrayList<String> valuesDirs = new ArrayList<String>(); //all file full names as strings.xml private static ArrayList<String> fileFullNames = new ArrayList<String>(); //result file name private static String result = "Result.xml"; private static int fileCounts = 0; private static int fileIndex = 0; private static String indicate = "name="; private static String preDate = new Date().toString()+"\n\n"; public static void main(String[] args) throws FileNotFoundException { getSubDirs(); getValuesDirs(); getfileFullNames(); fileCounts = fileFullNames.size(); //打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件 FileWriter writer; try { writer = new FileWriter(result); writer.write(preDate); writer.write("与"+fileFullNames.get(0)+"相比, "); writer.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //fileCounts for(fileIndex=1;fileIndex<fileCounts;++fileIndex){ compareFiles(); } } private static void compareFiles(){ FileWriter writer; try { writer = new FileWriter(result, true); String preContent2 = "\n\n"+fileFullNames.get(fileIndex)+"未翻译的字串为: \n"; writer.write(preContent2); File fileSrc = new File(fileFullNames.get(0)); File fileDes = new File(fileFullNames.get(fileIndex)); BufferedReader readerSrc = null; BufferedReader readerDes = null; try { readerSrc = new BufferedReader(new FileReader(fileSrc)); String stringSrc = null; String stringDes = null; boolean contantFlag = false; int line = 1; // 一次读入一行,直到读入null为文件结束 while ((stringSrc = readerSrc.readLine()) != null) { if(stringSrc.contains(indicate) && !stringSrc.contains("translatable=\"false\"")){ String str1 = stringSrc.substring(stringSrc.indexOf("=")+2); String str2 = str1.substring(0,str1.indexOf("\"")); readerDes = new BufferedReader(new FileReader(fileDes)); contantFlag = false; while ((stringDes = readerDes.readLine()) != null) { if(stringDes.contains(str2)){ contantFlag = true; break; } } if (!contantFlag) { writer.write("Line number and content is: "+line+", "+stringSrc+"\n"); } } line++; } readerSrc.close(); } catch (IOException e) { e.printStackTrace(); } finally { if (readerSrc != null) { try { readerSrc.close(); } catch (IOException e1) { } } } writer.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //get all directory in rootDir private static void getSubDirs(){ File fileDirRoot = new File(rootDir); subDirs = fileDirRoot.list(); } //get all directory(name contain values) in rootDir private static void getValuesDirs(){ for(int i=0;i<subDirs.length;++i){ if(subDirs[i].contains(dirFilter)){ valuesDirs.add(subDirs[i]); } } } //get all fileFullNames(contain strings.xml) in rootDir private static void getfileFullNames(){ for(int i=0;i<valuesDirs.size();++i){ File file = new File(rootDir+"\\"+valuesDirs.get(i)+"\\"+fileName); if(file.exists()){ fileFullNames.add(rootDir+"\\"+valuesDirs.get(i)+"\\"+fileName); } } } }
从代码可以看出,以上描述的四个步骤对应的方法分别为getSubDirs()、getValuesDirs()、getfileFullNames()、compareFiles()。
在结果文件Result.xml的开头,写入了当前时间,其对应字串的获取方式为:new Date().toString()。
注意,如代码138-140行所示,在将某个语言环境对应的strings.xml文件完整名称添加入ArrayList对象fileFullNames中之前,对其存在性进行了判断,也可以在后面的文件比较环节进行个文件的存在性判断,但不能不判断就进行文件的读取(多余,也容易引起异常)。
res目录下的文件夹获取情况:
![](http://images0.cnblogs.com/blog2015/450484/201508/241530144833514.png)
res目录下的名称中包含values的文件夹获取情况:
![](http://images0.cnblogs.com/blog2015/450484/201508/241530284833924.png)
res目录下的名称中包含values的文件夹中strings.xml的完整名称获取情况:
![](http://images0.cnblogs.com/blog2015/450484/201508/241530413899134.png)
所有翻译字串最终的对比结果文件Result.xml的部分内容为:
![](http://images0.cnblogs.com/blog2015/450484/201508/241544441082490.png)
有了这样的描述,就很容易查看当前多国语言的翻译情况了。这里的字串是随意敲的,不然命名的level也太低了。
总结
虽然实现的功能具有局限性,但已经可以满足上面的需求。正是因为一般通用的工具不能完成项目开发过程中遇到的所有情况,所以才要自己动手针对特定的需求进行实现。这篇文章只是起到抛砖引玉的作用,对于其他各种各样的场景,实现的思路还是相同的。
欢迎感兴趣的朋友一起讨论、学习与进步!
相关文章推荐
- Caliburn Micro Part 6: Introduction to Screens and Conductors
- Android-非常棒的HTTP通讯总结
- Area(Pick定理POJ1256)
- strace命令用法详解
- Autolayout断点调试
- linux下在grub.cfg中配置win7启动引导
- android开发之重写Application类
- 【system】linux 常用命令
- html中出现的script失效
- 运算符优先级和结合性
- 华为机试再回忆--第一题
- Area(Pick定理POJ1256)
- 在table中tr的display:block在firefox,chrome下显示布局错乱问题
- 旋转三维物体(加三维物体上)
- parseSdkContent failed Could not initialize class android.graphics.Typeface
- Strace在Linux下简单使用方法
- webService的基础知识二
- JQuery(三)——操作HTML和CSS内容
- C# 二进制字符串互转
- Algorithms—242.Valid Anagram