您的位置:首页 > 移动开发 > Android开发

Android国际化资源文件自动化生成工具

2014-03-18 23:17 531 查看
一、工具起源

如果在做一个产品的过程当中,可能会涉及到多个apk的开发,而且要求实现多国语言。而这些apk可能会由多人分工共同完成。但如果这样的话,每个人都需要整理各自apk所要显示的文字交给专人来翻译。专人负责收集和翻译文字,翻译完了之后再交给每个开发者。比如这个产品中的所有apk都需要支持10国语言,也就是说每个开发人员,要拿着翻译好的文字,在各自负责的项目中创建这10个语种的资源文件,并且将这10个语种的文字依次放入到不同语种目录下的资源文件中。而且当apk写完后,后续有修改,并在界面上添加了新的文字显示,又需要翻译,并要修改各国语言的资源文件。可想而知这是一件多么烦锁的事情。。。(负责翻译的人要收集所有文字,整理并翻译,开发人拿到翻译后的文字之后,要修改各个语言下的资源文件

)!所以为了管理方便,由一个人负责收集所有apk中需要翻译的所有文字,并统一交给负责翻译的人。最后拿到翻译后的文字,用一个工具来统一管理并生成所有apk所需要的各国语言的资源文件,这样是不是很爽呢??那是肯定的。不但能减少团队成员之间的工作量,还能提高工作效率。下面将介绍一下这个工具的使用,希望日后你也遇到类似的项目,对你有所有帮助。

二、国际化实现方式

实现Android国际化,分为两步:

1、在工程的res目录下创建不同国家和语种的资源目录(values或drawable),系统会根据设备当前的语言环境自动选择相应的资源文件。

2、翻译各国语言的文字,放入不同国家和语句的资源目录,即strings.xml或arrays.xml。



三、工具实现原理介绍

1、准备一个存放各个apk各国语言文字的excel模板文件。模板数据格式说明:

1> 每个sheet代表一个apk

2> sheet中的第一列存放strings.xml或arrays.xml文件中的id

3> 第二列存放默认文字(当设备找不到当前语言的文字时,使用默认的)

4> 第三列存放各个国家的语言缩写(列名使用语言缩写,比如中文:cn)





5> sheet命名注意:

a、生成strings.xml文件:直接用模块名即可,如:MusicPlayer

b、生成arrays.xml文件:用模块名+_arrays,如:MusicPlayer_arrays

2、使用Apache的开源框架POI解析excel读取各个apk的语言文字,并通过dom4j生成strings.xml和arrays.xml

四、工具类源代码

1、生成资源文件工具类

[java] view
plaincopy

package com.i18n.i18nbuilder;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.util.ArrayList;

import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFCell;

import org.apache.poi.hssf.usermodel.HSSFRow;

import org.apache.poi.hssf.usermodel.HSSFSheet;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import org.apache.poi.ss.usermodel.Cell;

import org.apache.poi.ss.util.CellRangeAddress;

import org.dom4j.Document;

import org.dom4j.DocumentHelper;

import org.dom4j.Element;

import org.dom4j.io.OutputFormat;

import org.dom4j.io.XMLWriter;

import com.mltsagem.i18nbuilder.model.ArrayEntity;

import com.mltsagem.i18nbuilder.model.StringEntity;

/**

* 读取EXCEL语言模板文件,生成各个国家语言的资源文件

* @author yangxin

*/

public class ResourcesBuilder {

public static final String DEFAULT_LANGUAGE_FLAG = "values";

/**

* 各个国家的语言

*/

public static final String[] LANGUAGE = {

DEFAULT_LANGUAGE_FLAG,

"en-rGB","de-rDE","fr-rFR","es-rES","it-rIT","pt-rPT","nl-rNL","sv-rSE","no-rNO",

"fi-rFI","da-rDK","hu-rHU","pl-rPL","cs-rCZ","tr-rTR","ru-rRU","el-rGR","ro-rRO"

};

// 读取需要生成strings.xml的sheet

public static final String[] STRINGS_SHEETS = {

"MusicPlayer",

"VideoPlayer"

};

// 读取需要生成arrays.xml的sheet

public static final String[] ARRAYS_SHEETS = {

"MusicPlayer", // 读取MusicPlayer_arrays sheet中的数据

//"VideoPlayer"

};

/**

* 资源文件生成的临时目录

*/

public static final String I18N_TEMP_DIR = "/tmp/i18n/";

/**

* 语言文件夹前缀

*/

public static final String RESOURCES_DIR_PREFIX = "values-";

/**

* 资源文件名

*/

public static final String STRING_RESOURCES_FILE_NAME = "strings.xml";

public static final String ARRAY_RESOURCES_FILE_NAME = "arrays.xml";

public static void main(String[] args) {

try {

String file = "/Users/yangxin/Desktop/language.xls";

// 清除以前生成的文件和目录

clearDir(new File(I18N_TEMP_DIR));

// 创建语言文件夹

createI18nDir();

// 生成各个模块中各个国家的strings.xml语言资源文件

builderStringResources(new FileInputStream(file));

// 生成各个模块中各个国家的arrays.xml语言资源文件

builderArrayResources(new FileInputStream(file));

System.out.println("全部生成成功:" + I18N_TEMP_DIR);

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 创建语言文件夹

*/

public static void createI18nDir() {

for (int i = 0; i < STRINGS_SHEETS.length; i++) {

// 创建模块所对应的目录

File parent = new File(I18N_TEMP_DIR,STRINGS_SHEETS[i]);

parent.mkdirs();

// 创建各个国家语言的资源目录

for (int j = 0; j < LANGUAGE.length; j++) {

String language = null;

if (j == 0) {

language = LANGUAGE[j];

} else {

language = RESOURCES_DIR_PREFIX + LANGUAGE[j];

}

File file = new File(parent,language);

if (!file.exists()) {

file.mkdirs();

}

}

}

}

/**

* 生成strings.xml资源文件

*/

public static void builderStringResources(InputStream is) throws Exception {

HSSFWorkbook book = new HSSFWorkbook(is);

for (int i = 0; i < STRINGS_SHEETS.length; i++) {

HSSFSheet sheet = book.getSheetAt(book.getSheetIndex(STRINGS_SHEETS[i]));

System.out.println("build strings for " + sheet.getSheetName());

int rowNum = sheet.getLastRowNum();

for (int j = 0; j < LANGUAGE.length; j++) {

String language = LANGUAGE[j];

ArrayList<StringEntity> stringEntitys = new ArrayList<StringEntity>();

File dir = null;

if (DEFAULT_LANGUAGE_FLAG.equals(language)) { // 创建默认语言

dir = new File(I18N_TEMP_DIR + STRINGS_SHEETS[i] + File.separator + language);

} else {

dir = new File(I18N_TEMP_DIR + STRINGS_SHEETS[i] + File.separator + RESOURCES_DIR_PREFIX + language);

}

File file = new File(dir,STRING_RESOURCES_FILE_NAME);

for (int k = 1; k <= rowNum; k++) {

HSSFRow row = sheet.getRow(k);

if (row.getLastCellNum() < 1)

continue;

String resId = row.getCell(0).getStringCellValue().trim(); // resId

HSSFCell cell = row.getCell(j+1);

String value = null;

if (cell != null) {

value = cell.getStringCellValue(); // 某一个国家的语言

if (value == null || "".equals(value.trim())) {

continue;

}

StringEntity entity = new StringEntity(resId, value.trim());

stringEntitys.add(entity);

}

}

// 创建资源文件

builderStringResources(stringEntitys,file);

}

}

is.close();

System.out.println("------------------strings.xml资源文件生成成功!------------------");

}

private static void builderStringResources(List<StringEntity> stringEntitys,File file) throws Exception {

OutputFormat format = OutputFormat.createPrettyPrint();

format.setEncoding("utf-8");

XMLWriter writer = new XMLWriter(new FileOutputStream(file),format);

Document document = DocumentHelper.createDocument();

Element root = document.addElement("resources");

for (StringEntity stringEntity : stringEntitys) {

Element stringElement = root.addElement("string");

stringElement.addAttribute("name", stringEntity.getResId());

stringElement.setText(stringEntity.getValue());

}

writer.write(document);

writer.close();

}

/**

* 生成arrays.xml资源文件

*/

public static void builderArrayResources(InputStream is) throws Exception {

HSSFWorkbook book = new HSSFWorkbook(is);

for (int i = 0; i < ARRAYS_SHEETS.length; i++) { // 功能模块

HSSFSheet sheet = book.getSheetAt(book.getSheetIndex(ARRAYS_SHEETS[i]+"_arrays"));

System.out.println("build arrays for " + sheet.getSheetName());

int rowNum = sheet.getNumMergedRegions(); // sheet.getLastRowNum();

for (int j = 0; j < LANGUAGE.length; j++) { // 语言

String language = LANGUAGE[j];

ArrayList<ArrayEntity> arrayEntities = new ArrayList<ArrayEntity>();

File dir = null;

if (DEFAULT_LANGUAGE_FLAG.equals(language)) { // 创建默认语言

dir = new File(I18N_TEMP_DIR + ARRAYS_SHEETS[i] + File.separator + language);

} else {

dir = new File(I18N_TEMP_DIR + ARRAYS_SHEETS[i] + File.separator + RESOURCES_DIR_PREFIX + language);

}

File file = new File(dir,ARRAY_RESOURCES_FILE_NAME);

for (int k = 1; k <= rowNum; k++) {

CellRangeAddress range = sheet.getMergedRegion(k-1);

int mergedRows = range.getNumberOfCells();

int lastRow = range.getLastRow();

int rowIndex = (lastRow - mergedRows) + 1;

String resId = sheet.getRow(rowIndex).getCell(0).getStringCellValue().trim(); // resId

ArrayEntity entity = new ArrayEntity(resId);

ArrayList<String> items = new ArrayList<String>();

for (int z = rowIndex; z <= lastRow; z++) {

HSSFCell cell = sheet.getRow(z).getCell(j+1);

String value = getValue(cell);

if (value == null || "".equals(value.trim())) { // 如果该语言没有对应的翻译,默认使用英语

HSSFCell defaultCell = sheet.getRow(z).getCell(1);

value = getValue(defaultCell);

}

if ("temp".equalsIgnoreCase(value.trim())) {

continue;

}

items.add(value);

}

entity.setItems(items);

arrayEntities.add(entity);

}

// 创建资源文件

builderArrayResources(arrayEntities,file);

}

}

System.out.println("------------------arrays.xml资源文件生成成功!------------------");

}

/**

* 获取单元格的值

* @param cell 单元格

* @return 单元格对应的值

*/

private static String getValue(HSSFCell cell) {

String value = "";

if (cell != null) {

switch (cell.getCellType()) {

case Cell.CELL_TYPE_NUMERIC:

value = String.valueOf((int)cell.getNumericCellValue()).trim();

break;

case Cell.CELL_TYPE_STRING:

value = cell.getStringCellValue().trim();

break;

case Cell.CELL_TYPE_BOOLEAN:

value = String.valueOf(cell.getBooleanCellValue()).trim();

break;

default:

value = cell.getStringCellValue().trim();

break;

}

}

return value;

}

private static void builderArrayResources(ArrayList<ArrayEntity> arrayEntities, File file) throws Exception {

OutputFormat format = OutputFormat.createPrettyPrint();

format.setEncoding("utf-8");

XMLWriter writer = new XMLWriter(new FileOutputStream(file),format);

Document document = DocumentHelper.createDocument();

Element root = document.addElement("resources");

for (ArrayEntity arrayEntity : arrayEntities) {

Element arrayElement = root.addElement("string-array");

arrayElement.addAttribute("name", arrayEntity.getName());

List<String> items = arrayEntity.getItems();

for (String item : items) {

Element itemElement = arrayElement.addElement("item");

itemElement.setText(item);

}

}

writer.write(document);

writer.close();

}

/**

* 清除以前生成的文件和目录

*/

public static void clearDir(File dir) {

if (!dir.exists()) return;

File[] files = dir.listFiles();

for (File file : files) {

if (file.isDirectory()) {

clearDir(file);

} else {

file.delete();

}

}

dir.delete();

}

}

[java] view
plaincopy

package com.i18n.i18nbuilder.model;

import java.util.List;

public class ArrayEntity {

private String name;

private List<String> items;

public ArrayEntity() {

super();

}

public ArrayEntity(String name) {

super();

this.name = name;

}

public ArrayEntity(String name, List<String> items) {

super();

this.name = name;

this.items = items;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public List<String> getItems() {

return items;

}

public void setItems(List<String> items) {

this.items = items;

}

}

[java] view
plaincopy

package com.i18n.i18nbuilder.model;

public class StringEntity {

private String resId;

private String value;

public StringEntity() {

super();

}

public StringEntity(String resId, String value) {

super();

this.resId = resId;

this.value = value;

}

public String getResId() {

return resId;

}

public void setResId(String resId) {

this.resId = resId;

}

public String getValue() {

return value;

}

public void setValue(String value) {

this.value = value;

}

}

2、解析资源文件(strings.xml/arrays.xml)中ID和value的工具类(解决手工复制id和value到excel模板中的问题)

[java] view
plaincopy

package com.i18n.i18nbuilder;

import java.io.FileInputStream;

import java.io.InputStream;

import java.util.List;

import org.dom4j.Attribute;

import org.dom4j.Document;

import org.dom4j.Element;

import org.dom4j.io.SAXReader;

/**

* 解析strings.xml和array.xml字符串资源文件,获取资源文件中的键和值

* @author yangxin

*/

public class ParseStringResources {

public static void main(String[] args) {

try {

getStringsIds();

getArraysIds();

} catch (Exception e) {

e.printStackTrace();

}

}

public static void getStringsIds() throws Exception {

System.out.println("-------------------MusicPlayer----------------------------");

ParseStringResources.parseStringsResourcesKey(new FileInputStream("musicplayer_strings.xml")); // 解析id

ParseStringResources.parseStringsResourcesValue(new FileInputStream("musicplayer_strings.xml")); // 解析value

System.out.println("-------------------VideoPlayer----------------------------");

ParseStringResources.parseStringsResourcesKey(new FileInputStream("video_strings.xml"));

ParseStringResources.parseStringsResourcesValue(new FileInputStream("video_strings.xml"));

}

public static void getArraysIds() throws Exception {

InputStream is = null;

is = new FileInputStream("musicplayer_arrays.xml");

ParseStringResources.parseArraysResources(is);

}

/**

* 解析strings.xml中的key

*/

public static void parseStringsResourcesKey(InputStream is) throws Exception {

SAXReader saxReader = new SAXReader();

Document document = saxReader.read(is);

Element rootElement = document.getRootElement();

List<Element> elements = rootElement.elements();

for (Element element : elements) {

String resid = element.attribute("name").getValue();

System.out.println(resid);

}

System.out.println("-----------------------------------key解析完成---------------------------------");

}

/**

* 解析strings.xml中的value

*/

public static void parseStringsResourcesValue(InputStream is) throws Exception {

SAXReader saxReader = new SAXReader();

Document document = saxReader.read(is);

Element rootElement = document.getRootElement();

List<Element> elements = rootElement.elements();

for (Element element : elements) {

String text = element.getTextTrim();

System.out.println(text);

}

System.out.println("-----------------------------------value解析完成---------------------------------");

}

/**

* 解析arrays.xml文件

*/

public static void parseArraysResources(InputStream is) throws Exception {

SAXReader saxReader = new SAXReader();

Document document = saxReader.read(is);

Element rootElement = document.getRootElement();

List<Element> elements = rootElement.elements();

for (Element element : elements) {

Attribute attribute = element.attribute("name");

if (attribute == null) continue;

String resid = attribute.getValue();

System.out.println(resid);

List<Element> items = element.elements();

for (Element item : items) {

String text = item.getTextTrim();

System.out.println(" " + text);

}

}

}

}

3、各国语言缩写映射关系

[html] view
plaincopy

<?xml version="1.0" encoding="UTF-8"?>

<date_formats>

<date_format>

<language>cs</language>

<format>dd.M.yyyy</format>

<english>Czech</english>

<chinese>捷克文</chinese>

</date_format>

<date_format>

<language>da</language>

<format>dd-MM-yyyy</format>

<english>Deutsch</english>

<chinese>丹麦文</chinese>

</date_format>

<date_format>

<language>de</language>

<format>dd.MM.yyyy</format>

<english>German</english>

<chinese>德文</chinese>

</date_format>

<date_format>

<language>en</language>

<format>dd/MM/yyyy</format>

<english>English</english>

<chinese>英文</chinese>

</date_format>

<date_format>

<language>es</language>

<format>dd/MM/yyyy</format>

<english>Spanish</english>

<chinese>西班牙文</chinese>

</date_format>

<date_format>

<language>fr</language>

<format>dd/MM/yyyy</format>

<english>French</english>

<chinese>法文</chinese>

</date_format>

<date_format>

<language>it</language>

<format>dd/MM/yyyy</format>

<english>Italian</english>

<chinese>意大利文</chinese>

</date_format>

<!--没有-->

<date_format>

<language>hu</language>

<format>yyyy.MM.dd</format>

<english>Hungarian</english>

<chinese>匈牙利文</chinese>

</date_format>

<date_format>

<language>nl</language>

<format>dd-M-yyyy</format>

<english>Dutch</english>

<chinese>荷兰文</chinese>

</date_format>

<!-- 没有 android.mk也没有-->

<date_format>

<language>no</language>

<format>dd.MM.yyyy</format>

<english>Norwegian</english>

<chinese>挪威文</chinese>

</date_format>

<date_format>

<language>pl</language>

<format>yyyy-MM-dd</format>

<english>Polish</english>

<chinese>波兰文</chinese>

</date_format>

<date_format>

<language>pt</language>

<format>dd-MM-yyyy</format>

<english>Portuguese</english>

<chinese>葡萄牙文</chinese>

</date_format>

<!--没有-->

<date_format>

<language>ro</language>

<format>dd.MM.yyyy</format>

<english>Romanian</english>

<chinese>罗马尼亚文</chinese>

</date_format>

<!--没有-->

<date_format>

<language>fi</language>

<format>dd.M.yyyy</format>

<english>Finnish</english>

<chinese>芬兰文</chinese>

</date_format>

<date_format>

<language>sv</language>

<format>yyyy-MM-dd</format>

<english>Swedish</english>

<chinese>瑞典文</chinese>

</date_format>

<date_format>

<language>tr</language>

<format>dd.MM.yyyy</format>

<english>Turkish</english>

<chinese>土耳其文</chinese>

</date_format>

<date_format>

<language>el</language>

<format>dd/M/yyyy</format>

<english>Greece</english>

<chinese>希腊文</chinese>

</date_format>

<date_format>

<language>ru</language>

<format>dd.MM.yyyy</format>

<english>Russian</english>

<chinese>俄文</chinese>

</date_format>

<date_format>

<language>zh</language>

<format>yyyy-M-dd</format>

<english>Chinese</english>

<chinese>中文</chinese>

</date_format>

</date_formats>

五、资源文件生成效果

1、控制台打印信息



2、资源文件存放目录及strings.xml内容



工具使用注意事项:

1>、sheet的命名必须和工具类中”STRINGS_SHEETS“和”ARRAYS_SHEETS“数组存储的元素名称保持一致(区分大小写)

2>、删除sheet中的空白单元格

3>、修改languages.xls模板文件和资源文件生成后的存放路径

相关资源下载地址:

项目源代码 语言文字Excel模板

转自:http://blog.csdn.net/xyang81/article/details/8945701
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: