xml扩展包
本地化包是全球化体系结构中的关键元素之一。 XML是本地化包的推荐源格式,因为它是跨平台的,Unicode编码的并且结构灵活。 本文介绍了两种使用XPath和XSLT实现基于XML的本地化包管理器的方法:嵌入和扩展。 通过embed ,本地化包管理器模块被嵌入到主程序中,而通过扩展 ,本地化包管理器模块在主程序之外工作。 两者都有各自的优势,可以根据不同的要求应用于实际开发。
注意:为了更好地理解本文,您应该熟悉XML,XSL,XPath和XSLT。 此外,如果您不熟悉与全球化相关的术语(例如语言环境,本地化包和本地化包管理器),请参考“ 相关主题 ”中的“全球化体系结构要务”以进行快速概述。
走向全球
在当今的全球电子商务环境中,企业需要为来自许多不同国家的客户和供应商提供支持。 如以前的国家语言支持模型所定义的,软件不再足以同时支持一种语言和地区。 在软件世界中,趋势是走向全球化,特别是对于电子商务系统。 本文提出了一种支持多种语言的重要全球化技术,即基于XML的本地化包实现。
什么是本地化包?
在常规编程模型中,非全球化的软件产品在可执行文件中包含各种与语言和文化有关的元素,例如国家语言翻译资源,用于格式化消息的与语言和文化有关的模板以及与文化有关的业务逻辑。 这些元素不是与程序代码隔离的。
本地化包是一种用于通过单个可执行文件支持多种语言和语言环境的软件的标准化方法。 与语言和文化相关的元素在源代码级别以及已编译和静态链接的模块级别与软件的核心逻辑分开。 与语言和文化无关的部分称为核心模块 ,而与语言和文化无关的部分称为本地化包 。 在要求软件同时支持多个语言环境的情况下,该软件需要依赖于语言环境的服务,这些服务可以基于用户明确指定或由应用程序隐式指定的语言环境ID进行切换。
这是本地化包的一个非常简单的示例:在Windows程序中,单个键(“ Msg1”)与单个字符串值(“ Hello”)关联,该字符串值根据特定语言版本(法语)的不同而不同(“ Bonjour”) 。
XML源格式
因为本地化包需要独立于平台的格式和多合一字符存储库,所以IBM的全球化组织建议使用XML作为本地化包的源格式。 XML是跨平台和Unicode编码的,因此能够保存多语言数据而不会丢失数据。 它还提供了一种灵活的树状结构,可满足对各种结构化语言环境数据的需求。 XML是W3C的推荐和Internet标准,因此可以满足大多数Web应用程序的要求。
XML本地化包示例
使用XML,您可以使用多个层次结构描述复杂的语言环境数据。 这是两个包含问候消息的基于XML的本地化包的简单示例。
图1.美国本地化包(en_US)

图2.中国本地化包(zh_CN)
什么是本地化包管理器?
本地化包管理器是用于管理本地化包资源的定位,加载和访问的代码模块。 现有的平台服务为许多本地化包功能提供了基本支持。 图3描述了本地化包管理器的工作流程。
图3.本地化包管理器的工作流程

您可以使用多种方法来实现本地化包管理器,即使对于单个本地化包格式(例如XML)也是如此。 本地化包管理器使用XSLT或XPath的路径表达式来访问本地化包XML文件中的指定节点。 许多公共程序或API实现XSLT和XPath建议。 在本文中,我使用来自Apache XML Project的开源Xalan-Java包(请参阅参考资料 ),将XSLT和XPath完全实现为本地化包管理器的基本构建块。 Xalan-Java是功能强大且功能强大的XSLT处理器,用于将XML文档转换为HTML,文本或其他XML文档类型。 您可以从命令行,在applet或servlet中或在另一个程序中作为模块使用它。 在这里,我将其用作演示程序中的本地化包管理器。
典型的电子商务应用程序主要使用HTML作为显示给用户的界面。 在多语言的电子商务环境中,基于XML的本地化包管理器可以根据HTML生成方法的不同而具有不同的实现(即使它是XML解析器的一种)。 本文讨论了本地化包管理器的两种实现: 嵌入模式和扩展模式 。 两者在典型的J2EE环境中都能很好地工作。 应用程序表示层由JSP,Servlet或Portlet组成,这些JSP,Servlet或Portlet从后端(或业务逻辑)获取程序数据并创建布局HTML(或另一种标记语言)。
嵌入模式实施
本节讨论上述应用程序环境中基于XML的本地化包管理器的实现。 访问本地化包XML的代码嵌入在程序中(例如JSP,Servlet或Portlet),其中HTML源代码逐行生成。 图4说明了这种方法的工作流程。
图4.嵌入本地化包工作流

用于UI表示的程序从后端业务逻辑中获取其逻辑数据,并从本地化包XML文件中获取UI消息,然后将它们组合以创建HTML布局。
XPathAPI简介
演示程序需要XML解析器来访问本地化包XML文件中的节点。 Xalan-Java中的Java类XPathAPI
和(更高级的) CachedXPathAPI
可以由诸如XPath处理器之类的程序使用,该程序可以在Xalan-Java中用作独立单元。
XPathAPI
包括几个静态方法,例如selectNodeIterator
, selectNodeList
和selectSingleNode
以通过XPath字符串从XML文档树中获取节点或节点列表:
NodeIterator nl = XPathAPI.selectNodeIterator(doctree, xpathstring);
String myvalue = nl.nextNode().getNodeValue();
您也可以使用eval
方法评估XPath字符串对于文档树访问一个或多个节点是否有效。
将XPathAPI用于本地化包管理器
您可以为嵌入的本地化包管理器定义一个常用的类,名为Embed_LPM.class
, Embed_LPM.class
封装了本地化包XML文档的初始化,评估和选择。 类结构如下所示:
清单1. Embed_LPM.class的类结构
package com.ibm.gcl.lpm;
import org.apache.xpath.*;
import org.w3c.dom.*;
import javax.xml.*;
public class Embed_LPM {
private Document doc;
public Embed_LPM(Locale cLocale) throws Exception {
Inputstream XMLStream = FileConverter.toStream(cLocale);
//Convert the XML file for specific locale to a InputStream.
InputSource in = new InputSource(XMLStream);
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
doc = dfactory.newDocumentBuilder().parse(in);
}
public String SelectNodeValue(String xpathstring) {
NodeIterator nl = XPathAPI.selectNodeIterator(doc, xpathstring);
String myvalue = nl.nextNode().getNodeValue();
Return myvalue;
}
… … … … }
嵌入实施细节
这是一个非常简单的示例来说明该过程。 假设您有一个页面,其中包含问候语“ hello”,并且需要根据特定的语言环境显示“ hello”的不同翻译(例如,zh_CN for China)。 目标HTML是:
详细过程如图5所示。 演示程序获取当前语言环境,使用当前语言环境创建Embed_LMP实例,从用于当前语言环境的本地化包XML构造XML文档树,调用selectNodeValue
方法获取树中的节点,并将其插入右侧结果HTML的一部分。 来自后端业务逻辑的动态程序数据也可以放置在HTML中的指定位置。 图5仅包括本地化包管理器的工作范围。
图5.嵌入本地化包实现

以下三个代码清单演示了JSP,Servlet和Portlet中本地化包管理器的调用过程。 尽管它们都是可行的,但使用JSP是最合适的方法,因为需要页面布局才能在程序中显式编写,并且JSP旨在以方便的方式创建布局。
清单2显示了本地化包管理器在JSP中的工作方式。
清单2. JSP中的本地化包管理器的调用过程
<%@ page language="Java" %>
<%@ page import="...... %>
<html>
<body>
<p>
<%
Locale locale = (Locale)session.getValue("clocale");
Embed_LPM eblpm = new Embed_LPM(locale);
String greetingmsg = eblpm.selectNodeValue("/greetings/Msg1"));
%>
<%=greetingmsg%>
</p>
</body>
</html>
清单3显示了Servlet如何调用本地化包管理器
清单3. Servlet调用本地化包管理器
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, java.net.MalformedURLException {
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
Locale locale = request.getLocale();
Embed_LPM eblpm = new Embed_LPM(locale);
out.write(" < html > < body > < p > ");
out.write(eblpm.selectNodeValue(" / greetings / Msg1 "));
out.write(" < / p > < / bodyl > < / html > ");
out.close();
}
清单4显示了Portlet如何调用本地化包管理器。
清单4. Portlet调用本地化包管理器
public void doView(
PortletRequest request,
PortletResponse response)
throws PortletException, IOException {
PrintWriter pw = response.getWriter();
Locale locale = request.getLocale();
Embed_LPM eblpm = new Embed_LPM(locale);
pw.println(" < html > < body > < p > ");
pw.println(eblpm.selectNodeValue(" / greetings / Msg1 "));
pw.println(" < / p > < / bodyl > < / html > ");
}
如何处理包含变量的消息
本地化包中包含的可翻译字符串可以包含变量-例如,句子“帐号XXX不存在”中的变量XXX 。 您无需在本地化包中将其分成两个字符串。 在使用各种语法将句子翻译成其他语言后,要保留该句子的原始含义,您应该将整个句子视为一个要翻译的字符串,同时保留XXX作为通知翻译者变量的一种方式。 对于正式使用,如果一个句子中存在多个变量,则可以用“#”符号(或“#1”,“#2”等)标记该变量。
当本地化包管理器遇到这种情况时,它可以使用字符串操作将单独的部分组合成一个完整的结果字符串。
清单5.将单独的部分组合成一个完整的字符串
< warning1 > The account number # doesn't exist.< / warning1 >
//Node in localization pack XML
public String stringReplace(
String sourcestring,
String symbol,
String replacement) {
String resultstring =
stringBefore(selectNodeValue("//warning1"), symbol) +
replacement + stringAfter(selectNodeValue("warning1"), symbol);
Return resultstring;
}
// String operations to be added in localization pack manager
print(stringReplace(selectNodeValue(" //warning1"), "#", accountnumber));
//Caller in JSP/…
扩展模式实施
除了Java XML解析器之外,XSL还可以使用XPath表达式解析XML。 在这种方式下,结果HTML不会在程序中逐行生成(就像在JSP,Servlet或Portlet中一样),而是从XML文件(包含业务逻辑数据)和外部XSL文件转换而来。 在图6中,驻留在XSL中的本地化包管理器在主要表示程序之外工作。 由于本地化包管理器的位置和实现不同,我将其称为扩展模式以将其与嵌入模式区分开。
图6.扩展本地化包工作流

XML业务逻辑数据可能具有以下内容,这些内容从后端作为XML格式的整个流从后端请求,或者在表示程序中临时生成以用于转换:
<root>
<userinfo>
<accountnumber>37522109</accountnumber>
</userinfo>
</root>
使用XSL访问XML本地化包节点
您可以使用以下XSL语法从本地化包XML文件访问节点:
清单6.用于访问本地化包节点的XSL
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="lpfile">localizationpack.xml</xsl:param>
<xsl:template match="/">
<html>
<body bgcolor="#6a6dee" text="#000000">
<p>
<xsl:value-of select="document($lpfile)//greetings/Msg1"/>
<p>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
XSL转换
Xalan-Java中包含的XSLTProcessor
和XSLTProcessorFactory
类可以将XML / XSL对转换为结果XML文档。 演示程序可以调用XSLTProcessor
和XSLTProcessorFactory
的方法进行转换。
清单7.使用XSLTProcessor和XSLTProcessorFactory进行转换
XSLTProcessor processor = XSLTProcessorFactory.getProcessor();
processor.process(
new XSLTInputSource(inputXMLStream),
new XSLTInputSource(inputXSLStream),
new XSLTResultTarget(resultStringWriter));
String result = resultStringWriter.toString();
扩展实施细节
图7描述了扩展本地化包管理器的详细工作过程。 演示程序可以获取当前语言环境,并将相应的本地化包XML的URL作为参数发送给XSL。 XSL负责管理本地化包并获取所需的语言环境数据。 为简单起见,生成HTML不包含XML数据中的内容,但是将代码添加到XSL相当容易。
图7.扩展本地化包的实现
一个程序可以创建多个XML业务逻辑数据文件并匹配多个XSL文件,而一个XSL文件可以应用于多个程序以转换其XML数据文件。 因此,XSL文件的数量取决于演示程序的设计要求。
以下三个代码清单演示了如何在JSP,Servlet和Portlet中执行转换。 与嵌入模式相反,在扩展模式下,servlet和portlet比JSP更胜任,因为页面布局隐式包含在XSL中。
清单8显示了JSP的转换过程。
清单8.在JSP中进行转换
<%@ page language="java" %>
<%@ page import="...... %>
<%
Localec clocale = (Locale)session.getValue("clocale");
XSLTProcessor processor = XSLTProcessorFactory.getProcessor();
processor.setStylesheetParam("xsllocale", "clocale" );//send the locale to XSL
processor.process(new XSLTInputSource(inputXMLStream),
new XSLTInputSource(inputXSLStream),
new XSLTResultTarget(resultStringWriter));
%>
<%=resultStringWrite.toString()%>
清单9显示了servlet的转换过程。
清单9.在servlet中进行转换
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, java.net.MalformedURLException {
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
Locale clocale = request.getLocale();
XSLTProcessor processor = XSLTProcessorFactory.getProcessor();
processor.setStylesheetParam("xsllocale", "clocale");//send the locale to XSL
processor.process(
new XSLTInputSource(inputXMLStream),
new XSLTInputSource(inputXSLStream),
new XSLTResultTarget(resultStringWriter));
out.write(resultStringWriter.toString());
out.close();
}
清单10中显示了Portlet的转换过程。
清单10.在portlet中进行转换
public void doView(PortletRequest request, PortletResponse response)
throws PortletException, IOException {
PrintWriter pw = response.getWriter();
Locale clocale = request.getLocale();
XSLTProcessor processor = XSLTProcessorFactory.getProcessor();
processor.setStylesheetParam("xsllocale", "clocale");//send the locale to XSL
processor.process(
new XSLTInputSource(inputXMLStream),
new XSLTInputSource(inputXSLStream),
new XSLTResultTarget(resultStringWriter));
pw.println(resultStringWriter.toString());
}
处理包含变量的消息
包含变量的待翻译句子也需要分别存储在本地化包中,并在从本地化包管理器访问时组合在一起。 因为扩展模式本地化包管理器位于XSL中,所以将它们组合在一起是XSL的责任。
在这里,我使用嵌入部分中用于演示的相同句子:
<xsl:value-of select="substring-before(document($lpfile)//warning1,'#')" />
<xsl:value-of select="//accountnumber" />
<xsl:value-of select="substring-after(document($lpfile)//warning1,'#')" />
同样,它可以使用更复杂的代码处理多个变量。 为了方便起见,您可以将代码打包在通用XSL模板中以进行字符串替换,从而使其可重用。
摘要
嵌入式本地化包管理器可以使典型的J2EE应用程序支持多种语言,而无需对框架设计进行重大更改。 这是通过从演示程序中提取可翻译字符串并用代码替换它们来访问本地化包中的相应内容来完成的,这些本地化包也需要在提取阶段进行组织。
嵌入模式既简单又经典,除本地化包外不包含其他文件。 但是,由于页面布局和程序数据已合并到程序代码中,因此,如果布局更改,则可能需要重新构建程序。
随着使用XML作为表示模块和业务逻辑模块之间的接口变得越来越普遍,前端模块经常使用XSL转换为目标UI(可以是HTML,VXML,WML或其他东西)。 。 在这种情况下,扩展本地化包管理器在支持多种语言中发挥了最佳作用。 XSL的使用将页面布局与程序数据分开,因此您无需编写或使用XML解析器,因为XSL可以帮您实现。 更改布局时,您需要做的是在不重建程序的情况下修改XSL文件。 但是,文件数增加了一倍,因为每个程序至少需要一个对应的XSL文件。
本文讨论的基于XML的本地化包实现方法为全球化软件的设计,开发和部署提供了很大的灵活性。 XML格式的本地化包被组织成单独的包,并存储在核心模块可执行文件之外,而没有被编译为可执行文件。 可以使用与XML内容组织中现有的本地化包相同的结构轻松添加新语言。 这些本地化包更改不会影响系统的运行方式。 除了这里提到的两种模式外,开发人员还可以根据自己的特定场景和要求从许多其他方法中进行选择。
翻译自: https://www.ibm.com/developerworks/xml/library/x-multlang/index.html
xml扩展包