zoukankan      html  css  js  c++  java
  • 文件对比与差异提取的实现

    File Comparison Tool

     

    前言

      一款需要多国语言的应用(真正受欢迎的至少需要中文和英文,不管是PC端还是移动端),那么应用程序开发过程中定义的字串文件就需要进行翻译,而这项工作在大公司往往是多人(甚至是多部门)协同完成。比如移动端开发常见的为strings.xml,默认是在res/values目录下,严格来说该目录对应的是英文环境,而中文简体环境规范的目录为res/values-zh-rCH。

     

    需求

      那么问题来了,开发人员定义好应用中需要用到的字串(最初可能是英文,也可能是中文)并且送翻(提交给专门的翻译人员进行字串翻译)后,怎么判断返回的翻译结果是完整的呢?或者说,中途接手一个项目,怎么快速掌握原来的字串翻译情况呢?

      之前有篇文章讲过Lint工具可以分析出项目中资源冗余及翻译不全等问题,但给出的信息不够简洁,用其来完成专门的字串翻译文件对比并不是很合适。通过下图可以看出,对于某一条字串的翻译缺失,Lint给出的信息是什么样的。

      协同工作的好处是,项目文件的commit、pull、push及review均可以在项目管理工具或者工具对应的命令行环境中进行。而review还可以在项目对应的远程网页中进行,便于管理者查看新提交的文件到底进行了哪些改动。但是其和文件比较工具(如beyond compare)类似,只能一个一个文件地查看,若想提取出所有的修改结果(或者不同点),只能认为地复制与黏贴到自己建立的文件中。

      所以,若想以最初的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实现代码如下:

      1 package com.filediff;
      2 
      3 import java.awt.image.WritableRaster;
      4 import java.io.BufferedReader;
      5 import java.io.File;
      6 import java.io.FileNotFoundException;
      7 import java.io.FileReader;
      8 import java.io.FileWriter;
      9 import java.io.IOException;
     10 import java.io.Writer;
     11 import java.time.chrono.JapaneseChronology;
     12 import java.util.ArrayList;
     13 import java.util.Date;
     14 
     15 import javax.swing.JFrame;
     16 
     17 @SuppressWarnings("serial")
     18 public class FileDiff extends JFrame {
     19 
     20     //resources root path
     21     private static String rootDir = "res";
     22     //directory name values
     23     private static String dirFilter = "values";
     24     //file name
     25     private static String fileName = "strings.xml";
     26     //all directory in rootDir
     27     private static String[] subDirs = null;
     28     //all directory(name contain values) in rootDir
     29     private static ArrayList<String> valuesDirs = new ArrayList<String>();
     30     //all file full names as strings.xml
     31     private static ArrayList<String> fileFullNames = new ArrayList<String>();
     32     //result file name
     33     private static String result = "Result.xml";
     34     private static int fileCounts = 0;
     35     private static int fileIndex = 0;
     36     private static String indicate = "name=";
     37     
     38     private static String preDate = new Date().toString()+"
    
    ";
     39 
     40     public static void main(String[] args) throws FileNotFoundException {
     41         getSubDirs();
     42         getValuesDirs();
     43         getfileFullNames();
     44         
     45         fileCounts = fileFullNames.size();
     46         
     47         //打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
     48         FileWriter writer;
     49         try {
     50             writer = new FileWriter(result);
     51             writer.write(preDate);
     52             writer.write("与"+fileFullNames.get(0)+"相比, ");
     53             writer.close();
     54         } catch (IOException e) {
     55             // TODO Auto-generated catch block
     56             e.printStackTrace();
     57         }
     58         //fileCounts
     59         for(fileIndex=1;fileIndex<fileCounts;++fileIndex){
     60             compareFiles();
     61         }
     62     }
     63     
     64     private static void compareFiles(){
     65         FileWriter writer;
     66         try {
     67             writer = new FileWriter(result, true);
     68             String preContent2 = "
    
    "+fileFullNames.get(fileIndex)+"未翻译的字串为: 
    ";
     69             writer.write(preContent2);
     70             
     71             File fileSrc = new File(fileFullNames.get(0));
     72             File fileDes = new File(fileFullNames.get(fileIndex));
     73             BufferedReader readerSrc = null;
     74             BufferedReader readerDes = null;
     75             try {
     76                 readerSrc = new BufferedReader(new FileReader(fileSrc));
     77                 String stringSrc = null;
     78                 String stringDes = null;
     79                 boolean contantFlag = false;
     80                 int line = 1;
     81                 // 一次读入一行,直到读入null为文件结束
     82                 while ((stringSrc = readerSrc.readLine()) != null) {
     83                     if(stringSrc.contains(indicate) && !stringSrc.contains("translatable="false"")){
     84                         String str1 = stringSrc.substring(stringSrc.indexOf("=")+2);
     85                         String str2 = str1.substring(0,str1.indexOf("""));
     86                         readerDes = new BufferedReader(new FileReader(fileDes));
     87                         contantFlag = false;
     88                         while ((stringDes = readerDes.readLine()) != null) {
     89                             if(stringDes.contains(str2)){
     90                                 contantFlag = true;
     91                                 break;
     92                             }
     93                         }
     94                         if (!contantFlag) {
     95                             writer.write("Line number and content is: "+line+", "+stringSrc+"
    ");
     96                         }
     97                     }
     98                     line++;
     99                 }
    100                 readerSrc.close();
    101             } catch (IOException e) {
    102                 e.printStackTrace();
    103             } finally {
    104                 if (readerSrc != null) {
    105                     try {
    106                         readerSrc.close();
    107                     } catch (IOException e1) {
    108                     }
    109                 }
    110             }
    111                         
    112             writer.close();
    113         } catch (IOException e) {
    114             // TODO Auto-generated catch block
    115             e.printStackTrace();
    116         }
    117     }
    118     
    119     //get all directory in rootDir
    120     private static void getSubDirs(){
    121         File fileDirRoot = new File(rootDir);
    122         subDirs = fileDirRoot.list();
    123     }
    124     
    125     //get all directory(name contain values) in rootDir
    126     private static void getValuesDirs(){
    127         for(int i=0;i<subDirs.length;++i){
    128             if(subDirs[i].contains(dirFilter)){
    129                 valuesDirs.add(subDirs[i]);
    130             }
    131         }
    132     }
    133     
    134     //get all fileFullNames(contain strings.xml) in rootDir
    135     private static void getfileFullNames(){
    136         for(int i=0;i<valuesDirs.size();++i){
    137             File file = new File(rootDir+"\"+valuesDirs.get(i)+"\"+fileName);
    138             if(file.exists()){
    139                 fileFullNames.add(rootDir+"\"+valuesDirs.get(i)+"\"+fileName);
    140             }
    141         }
    142     }
    143 }

      从代码可以看出,以上描述的四个步骤对应的方法分别为getSubDirs()、getValuesDirs()、getfileFullNames()、compareFiles()。

      在结果文件Result.xml的开头,写入了当前时间,其对应字串的获取方式为:new Date().toString()。

      注意,如代码138-140行所示,在将某个语言环境对应的strings.xml文件完整名称添加入ArrayList对象fileFullNames中之前,对其存在性进行了判断,也可以在后面的文件比较环节进行个文件的存在性判断,但不能不判断就进行文件的读取(多余,也容易引起异常)。

      res目录下的文件夹获取情况:

      res目录下的名称中包含values的文件夹获取情况:

      res目录下的名称中包含values的文件夹中strings.xml的完整名称获取情况:

      所有翻译字串最终的对比结果文件Result.xml的部分内容为:

       有了这样的描述,就很容易查看当前多国语言的翻译情况了。这里的字串是随意敲的,不然命名的level也太低了。

     

    总结

      虽然实现的功能具有局限性,但已经可以满足上面的需求。正是因为一般通用的工具不能完成项目开发过程中遇到的所有情况,所以才要自己动手针对特定的需求进行实现。这篇文章只是起到抛砖引玉的作用,对于其他各种各样的场景,实现的思路还是相同的。

      欢迎感兴趣的朋友一起讨论、学习与进步!

  • 相关阅读:
    共享纸巾更换主板代码分析 共享纸巾主板更换后的对接代码
    Python Django Ajax 传递列表数据
    Python Django migrate 报错解决办法
    Python 创建字典的多种方式
    Python 两个list合并成一个字典
    Python 正则 re.sub替换
    python Django Ajax基础
    Python Django 获取表单数据的三种方式
    python Django html 模板循环条件
    Python Django ORM 字段类型、参数、外键操作
  • 原文地址:https://www.cnblogs.com/tgyf/p/4754547.html
Copyright © 2011-2022 走看看