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

Android 自动化生成多语言strings文件 基于jxl.jar解析Excel xls文件自动化生成android多语言strings.xml 立即下载

一、工具起源

       

       如果在做一个产品的过程当中,可能会涉及到多个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 plain copy
  1. package com.i18n.i18nbuilder;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileOutputStream;  
  6. import java.io.InputStream;  
  7. import java.util.ArrayList;  
  8. import java.util.List;  
  9.   
  10. import org.apache.poi.hssf.usermodel.HSSFCell;  
  11. import org.apache.poi.hssf.usermodel.HSSFRow;  
  12. import org.apache.poi.hssf.usermodel.HSSFSheet;  
  13. import org.apache.poi.hssf.usermodel.HSSFWorkbook;  
  14. import org.apache.poi.ss.usermodel.Cell;  
  15. import org.apache.poi.ss.util.CellRangeAddress;  
  16. import org.dom4j.Document;  
  17. import org.dom4j.DocumentHelper;  
  18. import org.dom4j.Element;  
  19. import org.dom4j.io.OutputFormat;  
  20. import org.dom4j.io.XMLWriter;  
  21.   
  22. import com.mltsagem.i18nbuilder.model.ArrayEntity;  
  23. import com.mltsagem.i18nbuilder.model.StringEntity;  
  24.   
  25. /** 
  26.  * 读取EXCEL语言模板文件,生成各个国家语言的资源文件 
  27.  * @author yangxin 
  28.  */  
  29. public class ResourcesBuilder {  
  30.       
  31.     public static final String DEFAULT_LANGUAGE_FLAG = "values";  
  32.       
  33.     /** 
  34.      * 各个国家的语言 
  35.      */  
  36.     public static final String[] LANGUAGE = {  
  37.         DEFAULT_LANGUAGE_FLAG,  
  38.         "en-rGB","de-rDE","fr-rFR","es-rES","it-rIT","pt-rPT","nl-rNL","sv-rSE","no-rNO",  
  39.         "fi-rFI","da-rDK","hu-rHU","pl-rPL","cs-rCZ","tr-rTR","ru-rRU","el-rGR","ro-rRO"  
  40.     };   
  41.       
  42.     // 读取需要生成strings.xml的sheet  
  43.     public static final String[] STRINGS_SHEETS = {  
  44.         "MusicPlayer",  
  45.         "VideoPlayer"  
  46.     };  
  47.       
  48.     // 读取需要生成arrays.xml的sheet  
  49.     public static final String[] ARRAYS_SHEETS = {  
  50.         "MusicPlayer",  // 读取MusicPlayer_arrays sheet中的数据  
  51.         //"VideoPlayer"  
  52.     };  
  53.       
  54.     /** 
  55.      * 资源文件生成的临时目录 
  56.      */  
  57.     public static final String I18N_TEMP_DIR = "/tmp/i18n/";  
  58.       
  59.     /** 
  60.      * 语言文件夹前缀 
  61.      */  
  62.     public static final String RESOURCES_DIR_PREFIX = "values-";  
  63.       
  64.     /** 
  65.      * 资源文件名 
  66.      */  
  67.     public static final String STRING_RESOURCES_FILE_NAME = "strings.xml";  
  68.     public static final String ARRAY_RESOURCES_FILE_NAME = "arrays.xml";  
  69.   
  70.     public static void main(String[] args) {  
  71.         try {  
  72.             String file = "/Users/yangxin/Desktop/language.xls";  
  73.             // 清除以前生成的文件和目录  
  74.             clearDir(new File(I18N_TEMP_DIR));  
  75.             // 创建语言文件夹  
  76.             createI18nDir();  
  77.             // 生成各个模块中各个国家的strings.xml语言资源文件  
  78.             builderStringResources(new FileInputStream(file));  
  79.             // 生成各个模块中各个国家的arrays.xml语言资源文件  
  80.             builderArrayResources(new FileInputStream(file));  
  81.             System.out.println("全部生成成功:" + I18N_TEMP_DIR);  
  82.         } catch (Exception e) {  
  83.             e.printStackTrace();  
  84.         }  
  85.     }  
  86.       
  87.     /** 
  88.      * 创建语言文件夹 
  89.      */  
  90.     public static void createI18nDir() {  
  91.         for (int i = 0; i < STRINGS_SHEETS.length; i++) {  
  92.             // 创建模块所对应的目录  
  93.             File parent = new File(I18N_TEMP_DIR,STRINGS_SHEETS[i]);  
  94.             parent.mkdirs();  
  95.             // 创建各个国家语言的资源目录  
  96.             for (int j = 0; j < LANGUAGE.length; j++) {  
  97.                 String language = null;  
  98.                 if (j == 0) {  
  99.                     language = LANGUAGE[j];  
  100.                 } else {  
  101.                     language = RESOURCES_DIR_PREFIX + LANGUAGE[j];  
  102.                 }  
  103.                 File file = new File(parent,language);  
  104.                 if (!file.exists()) {  
  105.                     file.mkdirs();  
  106.                 }  
  107.             }  
  108.         }  
  109.     }  
  110.       
  111.     /** 
  112.      * 生成strings.xml资源文件 
  113.      */  
  114.     public static void builderStringResources(InputStream is) throws Exception {  
  115.         HSSFWorkbook book = new HSSFWorkbook(is);  
  116.         for (int i = 0; i < STRINGS_SHEETS.length; i++) {  
  117.             HSSFSheet sheet = book.getSheetAt(book.getSheetIndex(STRINGS_SHEETS[i]));  
  118.             System.out.println("build strings for " + sheet.getSheetName());  
  119.             int rowNum = sheet.getLastRowNum();  
  120.             for (int j = 0; j < LANGUAGE.length; j++) {  
  121.                 String language = LANGUAGE[j];  
  122.                 ArrayList<StringEntity> stringEntitys = new ArrayList<StringEntity>();  
  123.                 File dir = null;  
  124.                 if (DEFAULT_LANGUAGE_FLAG.equals(language)) {   // 创建默认语言  
  125.                     dir = new File(I18N_TEMP_DIR + STRINGS_SHEETS[i] + File.separator + language);  
  126.                 } else {  
  127.                     dir = new File(I18N_TEMP_DIR + STRINGS_SHEETS[i] + File.separator + RESOURCES_DIR_PREFIX + language);  
  128.                 }  
  129.                 File file = new File(dir,STRING_RESOURCES_FILE_NAME);  
  130.                 for (int k = 1; k <= rowNum; k++) {  
  131.                     HSSFRow row = sheet.getRow(k);  
  132.                     if (row.getLastCellNum() < 1)  
  133.                         continue;  
  134.                     String resId = row.getCell(0).getStringCellValue().trim();          // resId  
  135.                     HSSFCell cell = row.getCell(j+1);  
  136.                     String value = null;  
  137.                     if (cell != null) {  
  138.                         value = cell.getStringCellValue();          // 某一个国家的语言  
  139.                         if (value == null || "".equals(value.trim())) {  
  140.                             continue;  
  141.                         }  
  142.                         StringEntity entity = new StringEntity(resId, value.trim());  
  143.                         stringEntitys.add(entity);  
  144.                     }  
  145.                 }  
  146.                 // 创建资源文件  
  147.                 builderStringResources(stringEntitys,file);  
  148.             }  
  149.         }  
  150.         is.close();  
  151.         System.out.println("------------------strings.xml资源文件生成成功!------------------");  
  152.     }  
  153.       
  154.     private static void builderStringResources(List<StringEntity> stringEntitys,File file) throws Exception {  
  155.         OutputFormat format = OutputFormat.createPrettyPrint();  
  156.         format.setEncoding("utf-8");  
  157.         XMLWriter writer = new XMLWriter(new FileOutputStream(file),format);  
  158.         Document document = DocumentHelper.createDocument();  
  159.         Element root = document.addElement("resources");  
  160.         for (StringEntity stringEntity : stringEntitys) {  
  161.             Element stringElement = root.addElement("string");  
  162.             stringElement.addAttribute("name", stringEntity.getResId());  
  163.             stringElement.setText(stringEntity.getValue());  
  164.         }  
  165.         writer.write(document);  
  166.         writer.close();  
  167.     }  
  168.       
  169.     /** 
  170.      * 生成arrays.xml资源文件 
  171.      */  
  172.     public static void builderArrayResources(InputStream is) throws Exception {  
  173.         HSSFWorkbook book = new HSSFWorkbook(is);  
  174.         for (int i = 0; i < ARRAYS_SHEETS.length; i++) { // 功能模块  
  175.             HSSFSheet sheet = book.getSheetAt(book.getSheetIndex(ARRAYS_SHEETS[i]+"_arrays"));  
  176.             System.out.println("build arrays for " + sheet.getSheetName());  
  177.             int rowNum = sheet.getNumMergedRegions();   // sheet.getLastRowNum();  
  178.             for (int j = 0; j < LANGUAGE.length; j++) {      // 语言  
  179.                 String language = LANGUAGE[j];  
  180.                 ArrayList<ArrayEntity> arrayEntities = new ArrayList<ArrayEntity>();  
  181.                 File dir = null;  
  182.                 if (DEFAULT_LANGUAGE_FLAG.equals(language)) {   // 创建默认语言  
  183.                     dir = new File(I18N_TEMP_DIR + ARRAYS_SHEETS[i] + File.separator + language);  
  184.                 } else {  
  185.                     dir = new File(I18N_TEMP_DIR + ARRAYS_SHEETS[i] + File.separator + RESOURCES_DIR_PREFIX + language);  
  186.                 }  
  187.                 File file = new File(dir,ARRAY_RESOURCES_FILE_NAME);  
  188.                 for (int k = 1; k <= rowNum; k++) {  
  189.                     CellRangeAddress range = sheet.getMergedRegion(k-1);  
  190.                     int mergedRows = range.getNumberOfCells();  
  191.                     int lastRow = range.getLastRow();  
  192.                     int rowIndex = (lastRow - mergedRows) + 1;  
  193.                     String resId = sheet.getRow(rowIndex).getCell(0).getStringCellValue().trim();           // resId  
  194.                     ArrayEntity entity = new ArrayEntity(resId);  
  195.                     ArrayList<String> items = new ArrayList<String>();  
  196.                     for (int z = rowIndex; z <= lastRow; z++) {  
  197.                         HSSFCell cell = sheet.getRow(z).getCell(j+1);  
  198.                         String value = getValue(cell);  
  199.                           
  200.                         if (value == null || "".equals(value.trim())) { // 如果该语言没有对应的翻译,默认使用英语  
  201.                             HSSFCell defaultCell = sheet.getRow(z).getCell(1);  
  202.                             value = getValue(defaultCell);  
  203.                         }  
  204.                           
  205.                         if ("temp".equalsIgnoreCase(value.trim())) {  
  206.                             continue;  
  207.                         }  
  208.                           
  209.                         items.add(value);  
  210.                     }  
  211.                     entity.setItems(items);  
  212.                     arrayEntities.add(entity);  
  213.                 }  
  214.                 // 创建资源文件  
  215.                 builderArrayResources(arrayEntities,file);  
  216.             }  
  217.         }  
  218.         System.out.println("------------------arrays.xml资源文件生成成功!------------------");  
  219.     }  
  220.       
  221.     /** 
  222.      * 获取单元格的值 
  223.      * @param cell 单元格 
  224.      * @return 单元格对应的值 
  225.      */  
  226.     private static String getValue(HSSFCell cell) {  
  227.         String value = "";  
  228.         if (cell != null) {  
  229.             switch (cell.getCellType()) {  
  230.             case Cell.CELL_TYPE_NUMERIC:  
  231.                 value = String.valueOf((int)cell.getNumericCellValue()).trim();  
  232.                 break;  
  233.             case Cell.CELL_TYPE_STRING:  
  234.                 value = cell.getStringCellValue().trim();   
  235.                 break;  
  236.             case Cell.CELL_TYPE_BOOLEAN:  
  237.                 value = String.valueOf(cell.getBooleanCellValue()).trim();  
  238.                 break;  
  239.             default:  
  240.                 value = cell.getStringCellValue().trim();   
  241.                 break;  
  242.             }  
  243.         }  
  244.         return value;  
  245.     }  
  246.       
  247.     private static void builderArrayResources(ArrayList<ArrayEntity> arrayEntities, File file) throws Exception {  
  248.         OutputFormat format = OutputFormat.createPrettyPrint();  
  249.         format.setEncoding("utf-8");  
  250.         XMLWriter writer = new XMLWriter(new FileOutputStream(file),format);  
  251.         Document document = DocumentHelper.createDocument();  
  252.         Element root = document.addElement("resources");  
  253.         for (ArrayEntity arrayEntity : arrayEntities) {  
  254.             Element arrayElement = root.addElement("string-array");  
  255.             arrayElement.addAttribute("name", arrayEntity.getName());  
  256.             List<String> items = arrayEntity.getItems();  
  257.             for (String item : items) {  
  258.                 Element itemElement = arrayElement.addElement("item");  
  259.                 itemElement.setText(item);  
  260.             }  
  261.         }  
  262.         writer.write(document);  
  263.         writer.close();  
  264.     }  
  265.   
  266.     /** 
  267.      * 清除以前生成的文件和目录 
  268.      */  
  269.     public static void clearDir(File dir) {  
  270.         if (!dir.exists()) return;  
  271.         File[] files = dir.listFiles();  
  272.         for (File file : files) {  
  273.             if (file.isDirectory()) {  
  274.                 clearDir(file);  
  275.             } else {  
  276.                 file.delete();  
  277.             }  
  278.         }  
  279.         dir.delete();  
  280.     }  
  281. }  

[java]  view plain copy
  1. package com.i18n.i18nbuilder.model;  
  2.   
  3. import java.util.List;  
  4.   
  5. public class ArrayEntity {  
  6.   
  7.     private String name;  
  8.     private List<String> items;  
  9.       
  10.     public ArrayEntity() {  
  11.         super();  
  12.     }  
  13.     public ArrayEntity(String name) {  
  14.         super();  
  15.         this.name = name;  
  16.     }  
  17.     public ArrayEntity(String name, List<String> items) {  
  18.         super();  
  19.         this.name = name;  
  20.         this.items = items;  
  21.     }  
  22.     public String getName() {  
  23.         return name;  
  24.     }  
  25.     public void setName(String name) {  
  26.         this.name = name;  
  27.     }  
  28.     public List<String> getItems() {  
  29.         return items;  
  30.     }  
  31.     public void setItems(List<String> items) {  
  32.         this.items = items;  
  33.     }  
  34.       
  35. }  
[java]  view plain copy
  1. package com.i18n.i18nbuilder.model;  
  2.   
  3. public class StringEntity {  
  4.       
  5.     private String resId;  
  6.     private String value;  
  7.     public StringEntity() {  
  8.         super();  
  9.     }  
  10.     public StringEntity(String resId, String value) {  
  11.         super();  
  12.         this.resId = resId;  
  13.         this.value = value;  
  14.     }  
  15.     public String getResId() {  
  16.         return resId;  
  17.     }  
  18.     public void setResId(String resId) {  
  19.         this.resId = resId;  
  20.     }  
  21.     public String getValue() {  
  22.         return value;  
  23.     }  
  24.     public void setValue(String value) {  
  25.         this.value = value;  
  26.     }  
  27. }  

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

[java]  view plain copy
  1. package com.i18n.i18nbuilder;  
  2. import java.io.FileInputStream;  
  3. import java.io.InputStream;  
  4. import java.util.List;  
  5.   
  6. import org.dom4j.Attribute;  
  7. import org.dom4j.Document;  
  8. import org.dom4j.Element;  
  9. import org.dom4j.io.SAXReader;  
  10.   
  11. /** 
  12.  * 解析strings.xml和array.xml字符串资源文件,获取资源文件中的键和值 
  13.  * @author yangxin 
  14.  */  
  15. public class ParseStringResources {  
  16.       
  17.     public static void main(String[] args) {  
  18.         try {  
  19.             getStringsIds();  
  20.             getArraysIds();  
  21.         } catch (Exception e) {  
  22.             e.printStackTrace();  
  23.         }  
  24.     }  
  25.       
  26.     public static void getStringsIds() throws Exception {  
  27.         System.out.println("-------------------MusicPlayer----------------------------");  
  28.         ParseStringResources.parseStringsResourcesKey(new FileInputStream("musicplayer_strings.xml"));      // 解析id  
  29.         ParseStringResources.parseStringsResourcesValue(new FileInputStream("musicplayer_strings.xml"));    // 解析value  
  30.           
  31.         System.out.println("-------------------VideoPlayer----------------------------");  
  32.         ParseStringResources.parseStringsResourcesKey(new FileInputStream("video_strings.xml"));  
  33.         ParseStringResources.parseStringsResourcesValue(new FileInputStream("video_strings.xml"));  
  34.     }  
  35.       
  36.     public static void getArraysIds() throws Exception {  
  37.         InputStream is = null;  
  38.         is = new FileInputStream("musicplayer_arrays.xml");  
  39.         ParseStringResources.parseArraysResources(is);  
  40.     }  
  41.       
  42.     /** 
  43.      * 解析strings.xml中的key 
  44.      */  
  45.     public static void parseStringsResourcesKey(InputStream is) throws Exception {  
  46.         SAXReader saxReader = new SAXReader();  
  47.         Document document = saxReader.read(is);  
  48.         Element rootElement = document.getRootElement();  
  49.         List<Element> elements = rootElement.elements();  
  50.         for (Element element : elements) {  
  51.             String resid = element.attribute("name").getValue();  
  52.             System.out.println(resid);  
  53.         }  
  54.         System.out.println("-----------------------------------key解析完成---------------------------------");  
  55.     }  
  56.       
  57.     /** 
  58.      * 解析strings.xml中的value 
  59.      */  
  60.     public static void parseStringsResourcesValue(InputStream is) throws Exception {  
  61.         SAXReader saxReader = new SAXReader();  
  62.         Document document = saxReader.read(is);  
  63.         Element rootElement = document.getRootElement();  
  64.         List<Element> elements = rootElement.elements();  
  65.         for (Element element : elements) {  
  66.             String text = element.getTextTrim();  
  67.             System.out.println(text);  
  68.         }  
  69.         System.out.println("-----------------------------------value解析完成---------------------------------");  
  70.     }  
  71.       
  72.     /** 
  73.      * 解析arrays.xml文件 
  74.      */  
  75.     public static void parseArraysResources(InputStream is) throws Exception {  
  76.         SAXReader saxReader = new SAXReader();  
  77.         Document document = saxReader.read(is);  
  78.         Element rootElement = document.getRootElement();  
  79.         List<Element> elements = rootElement.elements();  
  80.         for (Element element : elements) {  
  81.             Attribute attribute = element.attribute("name");  
  82.             if (attribute == null) continue;  
  83.             String resid = attribute.getValue();  
  84.             System.out.println(resid);  
  85.             List<Element> items = element.elements();  
  86.             for (Element item : items) {  
  87.                 String text = item.getTextTrim();  
  88.                 System.out.println("      " + text);  
  89.             }  
  90.         }  
  91.     }  
  92. }  
3、各国语言缩写映射关系

[html]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <date_formats>  
  3.     <date_format>  
  4.         <language>cs</language>  
  5.         <format>dd.M.yyyy</format>  
  6.         <english>Czech</english>  
  7.         <chinese>捷克文</chinese>  
  8.     </date_format>  
  9.     <date_format>  
  10.         <language>da</language>  
  11.         <format>dd-MM-yyyy</format>  
  12.         <english>Deutsch</english>  
  13.         <chinese>丹麦文</chinese>  
  14.     </date_format>  
  15.     <date_format>  
  16.         <language>de</language>  
  17.         <format>dd.MM.yyyy</format>  
  18.         <english>German</english>  
  19.         <chinese>德文</chinese>  
  20.     </date_format>  
  21.     <date_format>  
  22.         <language>en</language>  
  23.         <format>dd/MM/yyyy</format>  
  24.         <english>English</english>  
  25.         <chinese>英文</chinese>  
  26.     </date_format>  
  27.     <date_format>  
  28.         <language>es</language>  
  29.         <format>dd/MM/yyyy</format>  
  30.         <english>Spanish</english>  
  31.         <chinese>西班牙文</chinese>  
  32.     </date_format>  
  33.     <date_format>  
  34.         <language>fr</language>  
  35.         <format>dd/MM/yyyy</format>  
  36.         <english>French</english>  
  37.         <chinese>法文</chinese>  
  38.     </date_format>  
  39.     <date_format>  
  40.         <language>it</language>  
  41.         <format>dd/MM/yyyy</format>  
  42.         <english>Italian</english>  
  43.         <chinese>意大利文</chinese>  
  44.     </date_format>  
  45.     <!--没有-->  
  46.     <date_format>  
  47.         <language>hu</language>  
  48.         <format>yyyy.MM.dd</format>  
  49.         <english>Hungarian</english>  
  50.         <chinese>匈牙利文</chinese>  
  51.     </date_format>  
  52.     <date_format>  
  53.         <language>nl</language>  
  54.         <format>dd-M-yyyy</format>  
  55.         <english>Dutch</english>  
  56.         <chinese>荷兰文</chinese>  
  57.     </date_format>  
  58.     <!-- 没有 android.mk也没有-->  
  59.     <date_format>  
  60.         <language>no</language>  
  61.         <format>dd.MM.yyyy</format>  
  62.         <english>Norwegian</english>  
  63.         <chinese>挪威文</chinese>  
  64.     </date_format>  
  65.     <date_format>  
  66.         <language>pl</language>  
  67.         <format>yyyy-MM-dd</format>  
  68.         <english>Polish</english>  
  69.         <chinese>波兰文</chinese>  
  70.     </date_format>  
  71.     <date_format>  
  72.         <language>pt</language>  
  73.         <format>dd-MM-yyyy</format>  
  74.         <english>Portuguese</english>  
  75.         <chinese>葡萄牙文</chinese>  
  76.     </date_format>  
  77.     <!--没有-->  
  78.     <date_format>  
  79.         <language>ro</language>  
  80.         <format>dd.MM.yyyy</format>  
  81.         <english>Romanian</english>  
  82.         <chinese>罗马尼亚文</chinese>  
  83.     </date_format>  
  84.     <!--没有-->  
  85.     <date_format>  
  86.         <language>fi</language>  
  87.         <format>dd.M.yyyy</format>  
  88.         <english>Finnish</english>  
  89.         <chinese>芬兰文</chinese>  
  90.     </date_format>  
  91.     <date_format>  
  92.         <language>sv</language>  
  93.         <format>yyyy-MM-dd</format>  
  94.         <english>Swedish</english>  
  95.         <chinese>瑞典文</chinese>  
  96.     </date_format>  
  97.     <date_format>  
  98.         <language>tr</language>  
  99.         <format>dd.MM.yyyy</format>  
  100.         <english>Turkish</english>  
  101.         <chinese>土耳其文</chinese>  
  102.     </date_format>  
  103.     <date_format>  
  104.         <language>el</language>  
  105.         <format>dd/M/yyyy</format>  
  106.         <english>Greece</english>  
  107.         <chinese>希腊文</chinese>  
  108.     </date_format>  
  109.     <date_format>  
  110.         <language>ru</language>  
  111.         <format>dd.MM.yyyy</format>  
  112.         <english>Russian</english>  
  113.         <chinese>俄文</chinese>  
  114.     </date_format>  
  115.     <date_format>  
  116.         <language>zh</language>  
  117.         <format>yyyy-M-dd</format>  
  118.         <english>Chinese</english>  
  119.         <chinese>中文</chinese>  
  120.     </date_format>  
  121. </date_formats>  

五、资源文件生成效果


1、控制台打印信息

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


工具使用注意事项:

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

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

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


相关资源下载地址:

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


转自:http://blog.youkuaiyun.com/xyang81/article/details/8945701

uniapp实战:mui-player插件如何完美适配m3u8/flv直播流(附完整代码) 本文详细介绍了如何在Uniapp中使用mui-player插件完美适配m3u8和flv直播流,提供从基础配置到高级优化的全流程解决方案。通过动态加载策略、响应式布局和内存管理,实现跨平台稳定播放,适用于企业级直播应用开发。 阅读详情

相关推荐

状态机与RTOS的融合实践:如何避免‘状态爆炸’并提升系统响应

本文探讨了状态机与RTOS的融合实践,重点介绍了如何通过层次化状态机、表驱动设计等策略避免状态爆炸问题,并优化任务调度与事件处理机制,从而显著提升嵌入式系统的响应速度和可靠性。

net55的博客 106

安卓开发 多语言strings.xml转excel表格,excel转strings.xml, excel转json (ExcelConversion)

ExcelConversion 一个java写的excel转多语言翻译的工具,目前已支持安卓的strings.xml与excel互转 支持多语言转换,将excel转换为多版本的strings.xml 支持将多个语言的strings.xml转换为一个excel表格 支持将strings.xml转换为json格式,用于Flutter或其它游戏项目 应用场景: 当你需要将翻译文件交给其他人翻译APP时,将一份待翻译的excel交给他,然后将翻译之后的excel转换为APP所需的代码文件。 使用方法 本工具使用

风的影子的博客 1005

从厘米级到公里级:北斗网格10级精度在ArcPro中的完整应用指南

本文提供了北斗网格十级精度体系在ArcGIS Pro中的完整应用指南。详细解析了从660公里到1.5厘米的十级网格尺度及其编码逻辑,并重点演示了如何在ArcPro中生成网格、计算编码,以及根据项目需求(如滑坡监测)选择合适精度层级的实战策略。指南旨在帮助GIS专业人士利用这套统一的网格框架,高效管理多尺度空间数据并优化分析工作流。

Black的专栏 471

IDEA 新建Android项目创建国际化文件夹方法

Android Studio创建国际化文件夹是src/main/res/values目录下,右键new->Values resource file选项 IDEA在相同目录下右键new->Android Resource Directory选项 再选择Locale,右箭头,语言 ...

weixin_44054505的博客 853

自动化测试-自愈技术

随着人工智能的发展,作为一名软件测试工程师,相信大家都会思考如何把这一前沿技术引入到我们的自动化测试当中,想一万遍不如做一遍。,传递当前页面状态,获取之前成功的定位器路径,比较它们,并生成修复的定位器列表。如果修复成功,我们可以使用 Healenium Idea 插件更新我们的自动化测试代码:插件使用修复定位器寻找修复和更新测试代码。的旧定位器去定位按钮的时候,脚本就会报错,提示 “NoSuchElement”的错误异常。现在,我们假设开发人员变更了应用程序的UI界面,改变了这个按钮的id属性,从。

qq_58223744的博客 2660

用KalmanNet搞定非线性状态估计:当卡尔曼滤波遇上神经网络

本文深入探讨了KalmanNet如何将神经网络与卡尔曼滤波结合,以解决非线性状态估计中的模型失配和噪声不确定性问题。通过保留经典滤波框架并用RNN学习卡尔曼增益,该方法在无人机导航、机器人定位等场景中实现了模型驱动与数据驱动的优势互补,提升了估计的鲁棒性和灵活性。

mango的博客 309

app国际化多语言strings.xml 与 Excel 互相转换的工具,支持iOS和Android

国际化多语言转换工具 方案介绍 当项目涉及到多语言国际化的时候,我们需要把string.xml翻译成其他国家语言,一般翻译公司会需要excel等格式文档,可是这翻译文件实在是不好整,幸好有大神做了个py工具实现 string文件转excel. 目前有两种方式: Localizable.strings2Excel (下载源码,然后在终端输入命令跑脚本进行文件转换) 作者:CatchZeng,https://github.com/CatchZeng/Localizable.strings2Excel L

Simon的专栏 5850

TPL5010硬件看门狗电路

在做一些采集频率很低的产品的时候,会考虑电池供电,比如表、电气表等,为了控制功耗,往往会在设备不需要工作的时候让它进入低功耗模式。为了设备的稳定性,避免死机后无法恢复,必须配置看门狗,一般的看门狗芯片,喂狗间隔时间是几秒钟。这样在喂狗和低功耗之间就产生了一个矛盾,频繁的喂狗就需要频繁的醒过来,这样功耗就很难做到很低。这颗看门狗芯片,特别适合上面的应用。2小时的间隔意味着主控芯片每2小时醒过来喂狗一次即可,而在2.5V供电的情况下,功耗仅仅35nA,几乎可以忽略不计。

zxm8513优快云博客 3306

应用系统 多端不同文件 国际化(i18n) Excel与多种文件之间相互转换

目录 1、使用场景与期待完成目标 2、实现过程之中的两种思维方式 3、实现部分关键源码 4、实现的效果展示 4.1properties文件-->Excel --->properties文件 4.2Android(string.xml)文件-->Excel --->Android(string.xml)文件 4.3、关于IOS的Localizable...

一颗浑身披带雪花的松树---[雪松] 1590

C#实现Socket通信同时监听多客户端(附完整源码)

C#实现Socket通信同时监听多客户端(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 427

ollama如何在自己的服务器上跑Qwen3(小白必看)

在国内下载ollama最新安装包时,使用curl -fsSL https://ollama.com/install.sh | sh命令可能会遇到网络问题导致下载进度停滞。为了解决这一问题,可以通过国内的镜像源进行下载,例如使用modelscope社区提供的最新ollama镜像仓库。具体步骤包括:首先通过pip install modelscope安装modelscope,然后使用modelscope download命令下载ollama-linux镜像,接着进入下载目录并赋予安装脚本执行权限,最后运行安装脚

Ven%的博客 756

android 生成 资源文件,Android国际化资源文件一键生成工具

工具起源最近在做国际化多语言适配,由开发人员收集项目中所有显示的文字交于专人负责整理和翻译,翻译完成后把整理好的Excel交给开发人员进行适配。然而并没有这样简单。。。各种修改、调整、新增文字,每次修改后开发人员都得核对一次,然后各个在strings.xml中修改。可想而知这是一件多么烦锁的事情,为了从这样一个重复、毫无意义的工作中解脱出来,我花了半天的时间撸了一个工具。有了工具后拿到翻译好的Ex...

weixin_30246833的博客 201

手把手教你学Simulink——基于Simulink的机械臂正运动学仿真建模示例

机械臂正运动学概述正运动学的核心在于根据每个关节的角度值,通过坐标变换(如DH参数法或齐次变换矩阵)计算出末端执行器相对于基座的位置和姿态。常用的数学工具包括旋转矩阵、平移向量以及它们组合而成的齐次变换矩阵。仿真目标设计并实现一个机械臂正运动学模型。输入关节角度,输出末端执行器的位置和姿态。分析不同关节角度下的机械臂位置变化。假设我们有一个简单的3自由度(DOF)平面机械臂,其各关节的长度分别为l1l1​、l2l2​和l3l3​。在Simulink中,可以使用。

MHD0815的博客 359

用 gradle 编写翻译自动化转换脚本

在做多语言项目的时候,当涉及地区相对较多的时候,翻译管理是个比较麻烦的事,下面的代码脚本自动将 md 格式的翻译文件转换成 Android 需要的 .xml 格式的文件. 翻译文件格式: 转换之后的格式: 具体代码如下: import groovy.transform.Field def mkGroup = 'translation' @Field def source...

QQ丶的博客 481

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

android 国际化方案 简书,Android国际化资源文件一键生成工具

工具起源最近在做国际化多语言适配,由开发人员收集项目中所有显示的文字交于专人负责整理和翻译,翻译完成后把整理好的Excel交给开发人员进行适配。然而并没有这样简单。。。各种修改、调整、新增文字,每次修改后开发人员都得核对一次,然后各个在strings.xml中修改。可想而知这是一件多么烦锁的事情,为了从这样一个重复、毫无意义的工作中解脱出来,我花了半天的时间撸了一个工具。有了工具后拿到翻译好的Ex...

weixin_39718460的博客 269

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

【工具】自动生成国际化string.xml

介绍下自动生成string.xml的方式

qq_37776700的博客 984

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

android 国际化测试,Android- 国际化

目前的工作需要用到国际化,所以把当前使用的国际化的东西做一个总结。先看看效果图国际化第一步在 Android Studio 中创建对应语言的文件夹 ( 工作中只需要简体和繁体,这边只展示相应的步骤 )步骤一步骤二步骤三方法类似,找到对应的语言,点击确定即可第二步在文件夹下创建对应的资源文件资源文件简体资源文件繁体第三步在 Application 的 onCreate 中处理语言@Overridep...

weixin_29706315的博客 234

Android App国际化与本地化适配指南

Android App的国际化与本地化适配是一项复杂而重要的工作。通过理解国际化和本地化的概念、制定合理的实现策略、解决常见挑战以及进行详尽的测试和优化,开发者可以打造出更具全球竞争力的App。这不仅有助于提升用户体验,还能有效扩大App的市场份额。在未来,随着技术的进步和市场的变化,国际化与本地化适配将不断演化,开发者需持续关注和学习。早期规划:在App开发的早期阶段就考虑国际化与本地化,可以避免后续的重复工作和成本。持续优化:国际化与本地化是一个持续的过程。

2501_92487756的博客 1058

利用python操作android的xml资源文件,让其按照中文自动生成对应中文首字母加起来的名字,并且根据中文在java类里面替代对应的中文

都是国际化的过啊啊啊啊啊

Android_Amelia的专栏 1205

Android 应用国际化全指南:从基础适配到高级实践

本文全面介绍了Android应用国际化的关键技术与实践方法。主要内容包括:1.资源文件组织,通过values-语言代码目录实现多语言适配;2.地区差异处理,涵盖数字、日期、货币等格式转换;3.RTL布局适配,支持阿拉伯语等从右到左语言;4.应用内动态语言切换实现方案;5.测试工具与最佳实践。文章提供了从基础到高级的完整国际化解决方案,帮助开发者构建真正全球化的Android应用。

weixin_37794278的博客 1254

Android Guidelines 中的国际化最佳实践:多语言应用开发指南

在全球化时代,Android 应用的国际化(i18n)和多语言支持已成为开发者的必备技能。无论你的应用面向哪个市场,遵循正确的国际化实践都能让你的应用更易被全球用户接受。本文基于 ribot 团队的 Android Guidelines,为你分享 Android 应用国际化的完整指南和最佳实践。🚀 ## 为什么国际化对 Android 应用如此重要? 国际化不仅仅是翻译文本,更是确保应用在不

gitblog_00654的博客 799

Nextcloud Android国际化支持详解:多语言适配的最佳实践

Nextcloud Android应用作为全球用户广泛使用的开源云存储客户端,其多语言国际化支持是确保用户体验一致性的关键要素。本文将深入探讨Nextcloud Android项目的国际化实现机制,为您提供完整的多语言适配指南。 作为一款面向全球用户的云存储解决方案,Nextcloud Android应用的多语言支持不仅仅是简单的文本翻译,更涉及到界面布局、本地化格式以及用户体验的全面优化。无论

gitblog_00216的博客 827

Muzei国际化测试终极指南:如何确保多语言版本质量

Muzei是一款功能强大的Android动态壁纸应用,它通过**国际化测试**确保全球用户都能享受到完美的使用体验。作为一款支持超过30种语言的全球性应用,Muzei的国际化质量保障流程值得开发者学习和借鉴。本文将深入探讨Muzei国际化测试的关键步骤,帮助您构建高质量的多语言应用。 ## 🌍 Muzei国际化架构解析 Muzei采用标准的Android国际化架构,所有字符串资源都存储在`r

gitblog_00309的博客 692
上一篇: Android代码资源的国际化
下一篇: Eclipse的部分实用设置……后续无限添加中……
android_xiaozhou
博客等级 码龄14年 14粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值