因为老项目须要进行国际化(翻译英文),其中一些xml内容也须要进行翻译。但这时问题就来了,英文版是有了中文怎么办? java
存两个xml?abc.xml,abc_zh_CN.xml。这也是个方法但这就须要修改读写xml的模块,让它像properties同样能够支持按语言读取。不想存2份xml的另外一个缘由就是并非xml中的全部内容都须要翻译,这样的方式无疑须要维护不少重复配置。 node
xml自己就支持多语言,能够采用xml:lang属性来完成。dom4j是否是也能够按xml:lang来解析? api
<?xml version="1.0" encoding="UTF-8"?> <root> <test xml:lang="zh"> <abc>你好0</abc> <bcd bye="再见0"/> <test>嵌套测试</test> </test> <test xml:lang="en"> <abc>hello0</abc> <bcd bye="goodbye0"/> <test>嵌套测试2</test> </test> <test> <abc>你好1</abc> <abc xml:lang="zh">你好2</abc> <abc xml:lang="en">hello1</abc> <bcd bye="goodbye1" xml:lang="en"/> <bcd bye="再见1"/> <bcd bye="再见2" xml:lang="zh_CN"/> <test xml:lang="en">嵌套测试3 </test> <test>嵌套测试4 </test> <test> <abc xml:lang="en">hello2</abc> <bcd xml:lang="en" bye="goodbye2"/> <abc xml:lang="zh">你好3</abc> <abc xml:lang="zh_TW">你好4</abc> <abc xml:lang="zh_CN">你好5</abc> </test> </test> </root>通常来讲在相对大的节点定义一个xml:lang=就能够了,就像上面的test节点。
package org.noahx.xmli18n; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.XMLFilterImpl; import java.util.HashSet; import java.util.Locale; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created with IntelliJ IDEA. * User: noah * Date: 10/29/12 * Time: 10:37 AM * To change this template use File | Settings | File Templates. */ public class LocaleXMLFilter extends XMLFilterImpl { /** * Locale正则式 */ private static final Pattern LOCALE_PATTERN = Pattern.compile("(^[^_-]*)(?:[_-]([^_-]*)(?:[_-]([^_-]*))?)?"); /** * 默认读取XML使用的Locale */ private Locale defaultLocale; /** * 存放当前xml元素路径 */ private StringBuilder currentPath = new StringBuilder("#"); /** * 存放忽略元素路径 */ private Set<String> ignoreSet = new HashSet<String>(); public LocaleXMLFilter(Locale defaultLocale) { this.defaultLocale = defaultLocale; } /** * 起始元素过滤 * * @param url * @param localName * @param qName * @param att * @throws SAXException */ public void startElement(String url, String localName, String qName, Attributes att) throws SAXException { boolean parentIgnoring = isIgnoreNode(); //判断父节点是否已经被忽略 currentPath.append(localName); //生成xml路径,# => #root/,#root/=>#root/a/ currentPath.append("/"); boolean ignoring = parentIgnoring; //子节点顺延父节点忽略 if (!ignoring) { //判断xml:lang是否与defaultLocale冲突,若是不同,忽略 String lang = att.getValue("xml:lang"); if (lang != null) { Locale xmlLocale = getLocaleFromLocaleString(lang); if (notSameLocale(xmlLocale)) { ignoring = true; } } } if (ignoring) { //忽略 tagIgnoreNode(); } else { //不忽略 super.startElement(url, localName, qName, att); } } /** * 中间字符过滤 * * @param data * @param start * @param length * @throws SAXException */ public void characters(char[] data, int start, int length) throws SAXException { if (!isIgnoreNode()) { //不忽略 super.characters(data, start, length); } } /** * 结束元素过滤 * * @param url * @param localName * @param qName * @throws SAXException */ public void endElement(String url, String localName, String qName) throws SAXException { if (isIgnoreNode()) { //忽略 untagIgnoreNode(); } else { //不忽略 super.endElement(url, localName, qName); } currentPath.replace(currentPath.length() - localName.length() - 1, currentPath.length(), ""); //清除当前路径,#/root/a/ => #/root/ } /** * 判断是否属于同语言,同国家 * * @param xmlLocale * @return */ private boolean notSameLocale(Locale xmlLocale) { boolean same = true; if (xmlLocale.getLanguage().equals(defaultLocale.getLanguage())) { //same lang if (!xmlLocale.getCountry().equals("")) { if (xmlLocale.getCountry().equals(defaultLocale.getCountry())) { //same country if (!xmlLocale.getVariant().equals("") && !xmlLocale.getVariant().equals(defaultLocale.getVariant())) { //diff variant same = false; } } else { same = false; } } } else { same = false; } return !same; } /** * zh_CN字符串转换为Locale * * @param s * @return */ private Locale getLocaleFromLocaleString(String s) { if (s == null) { return null; } Matcher matcher = LOCALE_PATTERN.matcher(s); matcher.find(); String language = matcher.group(1); language = (language == null) ? "" : language; String country = matcher.group(2); country = (country == null) ? "" : country; String variant = matcher.group(3); variant = (variant == null) ? "" : variant; return new Locale(language, country, variant); } /** * 当前节点是否被忽略 * * @return */ private boolean isIgnoreNode() { return ignoreSet.contains(currentPath.toString()); } /** * 标记为忽略 */ private void tagIgnoreNode() { ignoreSet.add(currentPath.toString()); } /** * 撤销标记为忽略 */ private void untagIgnoreNode() { ignoreSet.remove(currentPath.toString()); } }方式就是在startElement,characters,endElement时进行干预从根本上过滤掉不符合的Locale内容。这样就能够把对dom4j的影响下降到最小。
package org.noahx.xmli18n; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Node; import org.dom4j.io.SAXReader; import java.util.List; import java.util.Locale; /** * Created with IntelliJ IDEA. * User: noah * Date: 10/26/12 * Time: 5:19 PM * To change this template use File | Settings | File Templates. */ public class TestLangXml { public static void main(String[] args) { SAXReader saxReader = new SAXReader(); saxReader.setXMLFilter(new LocaleXMLFilter(Locale.SIMPLIFIED_CHINESE)); try { Document document = saxReader.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("org/noahx/xmli18n/test.xml")); List<Node> nodes = document.selectNodes("//root/test"); for (Node n : nodes) { System.out.println(n.asXML()); } System.out.println(nodes.size()); } catch (DocumentException e) { e.printStackTrace(); } } }
从打印的xml的内容中就能够看到,不符合的内容已经被过滤。咱们对dom4j只是加入saxReader.setXMLFilter(new LocaleXMLFilter(Locale.SIMPLIFIED_CHINESE));这一行。 app
这个方法对原有的程序改动较小,符合个人须要。 dom
源代码下载:http://sdrv.ms/Tpr6Op 测试