随着web项目面向全球的发展,国际化已经凸显出其重要的一面,所以一个国际化较好的项目可以使程序开发更加简洁,而避免不必要的重复劳动。但是在项目开发的前期,往往很多内容和元素并未决定,这就导致在项目的尾声程序员要去解决以前遗留下来的诸多繁琐问题,例如,国际化标签内容的替换,翻译不同的资源文件等等。如果内容较少的情况下并不会造成太大的负担,但是如果是一个大型web项目可想而知辛苦的程序员要做多少没有技术含量的事情,而且谁也无法保证这些过程中不会出错,一旦出错很可能是很难发现的。
为了解决这样的重复劳动工作,我写了一个小程序,去批量的替换国际化文字,把它们放入同一的资源文件中,这样既高效又不易出错。
下面就是代码:
(为了方便阅读我已经加入了大量注释,所以各位在引用代码时请尊重他人的劳动成果,请保留所有注释)
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Scanner; import java.util.Set; import java.util.Map.Entry; /** * * 类描述:国际化小工具,为了方便大家阅读代码,我尽量多写注释,尽量一行代码分多行解释 * 注:如果想使用此工具或引用此代码,请保留这些注释 * @author liming * @time 2011-6-23 下午03:46:07 */ public class Translater { /** 已经存在的国际化值 */ private static Map<String, String> existPropertiesMap; /** 要翻译的内容 */ private static Set<String> toTranslateSet; /** 前缀 */ private static final String START_PREFIX = "$"; /** 后缀 */ private static int LAST_PREFIX = 1000; /** 国际化标签前缀 */ private static final String START_LABEL = "<s:text"; /** name */ private static final String NAME = "name"; /** 资源文件 */ private static Properties p; /** 路径 */ private static String PATH; /** * 方法描述:国际化方法main执行方法 * * @author liming * @time 2011-6-23 上午09:14:29 * * @param args * @throws IOException */ public static void main(String[] args) throws IOException { existPropertiesMap = new HashMap<String, String>(); System.out.println("请输入路径:"); // 要求用户输入文件夹所在路径 Scanner cin = new Scanner(System.in); // 目录地址 PATH = cin.nextLine(); System.out.println("请输入资源文件名称(注:资源文件应放在 " + PATH + " 下):"); Scanner cin2 = new Scanner(System.in); // 资源文件名称 String propertiesFileName = cin2.nextLine(); // 资源文件路径 String propertiesPath = PATH + "\\" + propertiesFileName + ".properties"; // 资源文件 File propertiesFile = new File(propertiesPath); // 也可以使用下面两行代码读取用户输入的信息 /** * BufferedReader bf=new BufferedReader(new * InputStreamReader(System.in)); String path=bf.readLine(); */ File file = new File(PATH); // 判断目录是否存在 if (!file.exists()) { System.out.println("输入的目录不存在"); System.exit(0); } // 判断是否为文件夹 if (!file.isDirectory()) { System.out.println("请输入目录路径"); System.exit(0); } // 判断资源文件是否存在 if (!propertiesFile.exists()) { System.out.println("输入资源文件不存在"); System.exit(0); } // 判断资源文件是否为文件 if (!propertiesFile.isFile()) { System.out.println("请输入正确的资源文件名称"); System.exit(0); } // 读取资源文件 InputStream in1 = new FileInputStream(propertiesFile); p = new Properties(); p.load(in1); propertiesMap(p); List<File> fileList = getFileList(PATH); // 首先取出要翻译的内容 getLabels(fileList); // 将要翻译的内容放入map中 putToMap(toTranslateSet); // 翻译国际化文字 translateLabel(fileList); // 写入资源文件 writeToPropertieFile(existPropertiesMap, propertiesPath); System.out.println("执行结束"); } /** * * 方法描述:获取目录文件夹下的所有文件,支持3层文件夹嵌套<br/> * eg:<br/> * pages/index/index_cn<br/> * pages/index/index_en<br/> * * @author liming * @time 2011-6-23 上午09:20:32 * * @param path * @return */ public static List<File> getFileList(String path) { // 文件列表 List<File> fileList = new ArrayList<File>(); File filePath = new File(path); // 判断path是否为文件夹路径 if (filePath.exists() || filePath.isDirectory()) { File[] files = filePath.listFiles(); for (File file : files) { if (file.isFile()) fileList.add(file); if (file.isDirectory()) { File[] filesTemp = file.listFiles(); for (File fileTemp : filesTemp) { if (fileTemp.isFile()) fileList.add(fileTemp); if (fileTemp.isDirectory()) { File[] filesTemp2 = fileTemp.listFiles(); for (File fileTemp2 : filesTemp2) if (fileTemp2.isFile()) fileList.add(fileTemp2); } } } } return fileList; } return null; } /** * * 方法描述:写入资源文件 * * @author liming * @time 2011-6-23 下午03:16:10 * */ public static void writeToPropertieFile(Map map, String fullPath) throws IOException { File file = new File(fullPath); OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file)); // 循环map中的值 for (Iterator<Entry<String, String>> i = map.entrySet().iterator(); i.hasNext();) { Entry e = i.next(); String key = e.getKey().toString(); String value = e.getValue().toString(); out.write(key + "=" + value+"\n"); } out.close(); } /** * * 方法描述:保存jsp页面 * * @author liming * @time 2011-6-29 上午11:04:51 * * @param lineList * @param file * @throws IOException */ public static void writeToJsp(List<String> lineList, File file) throws IOException { OutputStream out = new FileOutputStream(file); OutputStreamWriter writer = new OutputStreamWriter(out); for (String line : lineList) { writer.write(line + "\n"); } writer.close(); out.close(); } /** * * 方法描述:翻译国际化文字 * * @author liming * @time 2011-6-23 下午03:26:27 * * @param line * @return */ public static void translateLabel(List<File> fileList) throws IOException { for (File f : fileList) { List<String> lineList = new ArrayList(); // 读取文件内容 InputStream in = new FileInputStream(f); // 转型为reader类型 InputStreamReader inReader = new InputStreamReader(in); // 转型为BufferedReader,用来调取readLine方法 BufferedReader bufferReader = new BufferedReader(inReader); String line; // 读取内容,当readLine方法返回为null时说明这个文件已经读取完毕 while ((line = bufferReader.readLine()) != null) { int startCount = 0; int labelStart = 0; while (labelStart != -1) { labelStart = line.indexOf(START_LABEL, startCount); if (labelStart != -1) { // 取文字 int contextStart = line.indexOf(NAME, labelStart + START_LABEL.length()) + 6; int contextEnd = line.indexOf("\"", contextStart); if (contextEnd > contextStart) { String context = line.substring(contextStart, contextEnd); if (!context.isEmpty()) { if (!context.substring(0, 1).equals(START_PREFIX)) { String key = getFromMapByValue(context); if (key != null) { line = line.substring(0, contextStart) + key + line.substring(contextEnd, line.length()); } } } startCount = labelStart + 6; } continue; } } lineList.add(line); } writeToJsp(lineList, f); } } /** * * 方法描述:取出所有要翻译的文字 * * @author liming * @time 2011-6-28 上午09:20:32 * * @param fileList * @throws IOException */ public static void getLabels(List<File> fileList) throws IOException { toTranslateSet = new HashSet<String>(); for (File f : fileList) { // 读取文件内容 InputStream in = new FileInputStream(f); // 转型为reader类型 InputStreamReader inReader = new InputStreamReader(in); // 转型为BufferedReader,用来调取readLine方法 BufferedReader bufferReader = new BufferedReader(inReader); String line; while ((line = bufferReader.readLine()) != null) { int startCount = 0; int labelStart = 0; while (labelStart != -1) { labelStart = line.indexOf(START_LABEL, startCount); if (labelStart != -1) { // 取文字 int contextStart = line.indexOf(NAME, labelStart + START_LABEL.length()) + 6; int contextEnd = line.indexOf("\"", contextStart); if (contextEnd > contextStart) { String context = line.substring(contextStart, contextEnd); if (!context.isEmpty()) { if (!context.substring(0, 1).equals(START_PREFIX)) toTranslateSet.add(context); } startCount = labelStart + 6; } continue; } } } } } /** * * 方法描述:放入翻译map中 * * @author liming * @time 2011-6-29 上午10:24:13 * * @param set */ public static void putToMap(Set<String> set) { for (String s : set) { // 如果已经翻译了此内容 if (!existPropertiesMap.containsValue(s)) { existPropertiesMap.put(START_PREFIX + (LAST_PREFIX++), s); } } } /** * * 方法描述:根据value返回key * * @author liming * @time 2011-6-29 上午10:35:52 * * @param value * @return */ public static String getFromMapByValue(String value) { if (!value.isEmpty()) for (Iterator<Entry<String, String>> i = existPropertiesMap.entrySet().iterator(); i.hasNext();) { Entry e = i.next(); if (e.getValue().equals(value)) return e.getKey().toString(); } return null; } /** * * 方法描述:将资源文件中已经国际化过的属性放入map中 * * @author liming * @time 2011-6-29 下午02:01:55 * * @param p */ public static void propertiesMap(Properties p) { List<Integer> l = new ArrayList<Integer>(); for (Iterator<Entry<Object, Object>> i = p.entrySet().iterator(); i.hasNext();) { Entry e = i.next(); existPropertiesMap.put(e.getKey().toString(), e.getValue().toString()); l.add(Integer.parseInt(e.getKey().toString().substring(1))); } Collections.sort(l); LAST_PREFIX = l.get(l.size() - 1) + 1; } }
此工具支持特定目录下所有文件的翻译,我没有做jsp文件过滤,所以请各位自己酌情使用。
下面我说下用法,首先准备一个或者已有的资源文件:
例如message_zh_CN.properties
内容为:
$1001=居然还有人踩 $1002=我真想不通你们是什么心理
这样在运行程序的过程,会根据这个资源文件已经有的标签继续向下添加,如此文件最大的为$1002则程序添加的下一个就是$1003,如果jsp文件中的中文在资源文件中已经存在,则不会生成新的而直接采用已有的标签。
最后生成一个新的资源文件保存所以已经翻译的内容。
注:生产message.properties文件后,如果你想转成ISO-8859-1的格式可以用native2ascii,这个工具在jdk的bin目录下,执行命令为:
native2ascii -encoding utf-8 message.properties