随着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
分享到:
相关推荐
"android国际化工具"是开发者用来自动化处理字符串翻译过程的利器,它极大地简化了Strings.xml和arrays.xml等资源文件的多语言支持工作,从而提升了开发效率。 Android的国际化(i18n,即“internationalization”...
Swift-iOS国际化工具是iOS应用开发中的一个重要组成部分,它使得应用程序能够支持多种语言,从而适应全球用户的需求。在iOS开发中,国际化不仅仅是翻译文本那么简单,它涉及到资源的本地化、日期时间格式、货币符号...
国际化工具PropertiesEditor国际化工具PropertiesEditor国际化工具PropertiesEditor国际化工具PropertiesEditor国际化工具PropertiesEditor
1. **MessageFormat**:Java内置的格式化工具,支持复杂的字符串格式化,包括日期、数字和消息的国际化。 2. **I18N Lib**:开源库,提供了更方便的API来处理国际化,包括动态加载资源、自动处理编码转换等。 3. **...
FastGettext就是其中一款高效的Ruby国际化工具,它以其内存占用小、命名空间简洁以及线程安全性等特性,深受开发者喜爱。下面将详细介绍FastGettext的主要功能、用法及其优势。 1. **主要功能** FastGettext提供...
《国际化工具 Translater.java 深入解析》 在软件开发过程中,为了满足不同地区用户的需求,实现软件的多语言支持是至关重要的。国际化的工具(Internationalization Tool)扮演着关键角色,它使得开发者能够方便地...
总结来说,Aura.Intl 是一个针对 PHP 开发者的强大国际化工具,它提供了全面的翻译管理功能,包括消息格式化、动态参数处理和复数规则支持。通过使用 Aura.Intl,开发者可以轻松地为他们的应用添加多语言支持,提升...
可以方便实现Delphi和BCB开发的软件实现国际化目标. 全部为开源免费. 工具集中包含如下几个部分: 1. gnugettext.pas的使用说明 -- manual(GnuGetText.pdf); 2. gnugettext.pas源代码; 3. ggt-translate-setup.exe --...
eclipse properties editor i18 国际化工具 All packages of your project are grouped in one project tree even if you prefer to use Java Properties Editor Standalone edition.
JsTalks JsTalks 是一个 JavaScript 国际化工具。 任何使用此工具的 JavaScript 网络应用程序都将能够完全国际化。 此工具将在从 Web 服务器托管您的站点或在本地运行文件时工作。如何使用在文件夹js/jsTalks 中包含...
Java国际化的工具propedit是Java开发中用于处理国际化(i18n)和本地化(l10n)过程的一款实用程序。在Java应用程序中,为了支持不同地区的用户,我们需要提供多语言支持,这就涉及到了国际化和本地化。propedit正是...
国际化处理工具(i18nTools)是一种专为软件开发设计的工具,旨在简化多语言支持过程,使得应用程序能够适应全球不同地区的用户需求。在现代Web应用开发中,国际化(i18n)是一个关键的考虑因素,因为它允许程序与...
这里主要介绍两种关键工具:gettext和Poedit,它们在Django国际化过程中起着至关重要的作用。 **1. Gettext** Gettext是Unix/Linux系统下广泛使用的文本翻译工具,它可以帮助开发者将应用程序中的字符串提取出来,...
Java 的资源包(Resource Bundle)是实现国际化的核心工具。资源包是一组按语言环境组织的文本字符串,用于存储应用中的可本地化信息。每个语言环境对应一个 `.properties` 文件,如 `messages_en.properties`...
在IT行业中,`gettext`是一个广泛使用的工具,用于软件的多语言化,即国际化(Internationalization)和本地化(Localization)。这个工具集主要是为Unix-like系统设计的,但也能在其他平台,包括Python的Django框架...
5. **自动化处理**:可能包含自动化工具,如批量导入/导出,或者自动将未翻译的键值复制到其他语言文件。 文件名称"myPropertiesEdit"可能是指这个插件提供的特定的.properties文件编辑器,或者是该插件的配置或...
多语言化便利工具减少时间浪费,快速拥抱21世纪发展生态的先进文化! 在管理超过三种语言时,添加新字符串或请求更正现有短语并不困难。 在没有工具的情况下工作时很难感觉到,但是当您通过工具了解这项工作的便利性...
Java应用程序国际化(i18n)的开源工具。 密钥的编辑器,语言资源的值对。 此外,还可以定义注释,并具有反向和拼写检查功能。 还收集有关您的应用程序中翻译键使用情况的统计信息。 与Properties实现完全兼容。
6. ** gettext** 和 **poedit**:用于Python项目的国际化工具,poedit是翻译编辑器。 四、国际化与本地化的最佳实践 1. **尽早规划**:在项目开始时就考虑国际化,避免后期大量重构。 2. **模块化设计**:将语言...