以文本方式查看主题

-  中文XML论坛 - 专业的XML技术讨论区  (http://bbs.xml.org.cn/index.asp)
--  『 XML基础 』  (http://bbs.xml.org.cn/list.asp?boardid=1)
----  本地化和 XML/DHTML 菜单  (http://bbs.xml.org.cn/dispbbs.asp?boardid=1&rootid=&id=8914)


--  作者:diy930
--  发布时间:7/21/2004 1:43:00 PM

--  本地化和 XML/DHTML 菜单
编者按:以下文章仅用于示范,并没有反映某一 Microsoft 产品的任何开发过程。有关 XML 和 XSL 的详细信息,请访问新的 MSDN XML 开发者中心(英文)。

请下载本文的示例代码(压缩文件,14.3K)

“DXML”系列文章
这是有关使用 XML 和 XSL 为 Web 站点开发动态 HTML (DHTML)用户界面的系列文章中的第四篇,也是最后一篇。在第一篇文章中,我们使用 XML 文件存储站点的目录 (TOC) 信息,使用 XSL、CSS 和脚本输出 DHTML TOC。在第二篇文章中,我们使用同一 XML 数据生成 DHTML 菜单,而在 microsoft.com 等 Web 站点中就用到了这些菜单。在第三篇文章中,我们讨论了在 Web 站点上实现 DHTML 菜单的三种特殊方式。

如果要掌握这最后一篇文章中的大部分内容,需要您熟悉前面文章中提及的概念和代码。如果您还没有阅读前面的文章,我们建议您在阅读这一篇文章之前,先阅读前三篇。

本地化
本月,我们将实现 DHTML 菜单的日语和希伯来语版本。以这两种语言为例,我们将会看到将 XML 和 DHTML 针对非西欧字符集进行本地化要采用的一些有趣的方法,而且在希伯来语中,我们还将看到从右向左的(即,双向)的阅读顺序。

这篇文章的目的是演示如何通过对我们的核心文件(menus.xml、menus.xsl、menus.jscript 和 menus.css)进行极小的改动(除了翻译 XML 文档中的内容之外),即能以浏览器所支持的任一语言发布 DHTML 菜单。

以下是由 DHTML 实现的两个菜单的外观:



图 1. 日语菜单



图 2. 希伯来语菜单

我们将提取 XML 文件的本地化版本,有两种编码形式:一种是特定语言的字符集编码,一种是 UTF-8 Unicode,并将提交这两种编码方式的 HTML 输出结果。我们将讨论动态和“脱机”生成 #include 文件所需的设置,并了解为了使我们的输出结果按所需方式实现,我们应告知浏览器的内容。

这不是一篇讨论如何对代码进行本地化(这个主题相当耗人精力)的内容详尽的论文。与之相反,我们将了解与这一特定案例相关的基本概念,检查示例代码,并在进行过程中为您指出查阅详细内容的位置。我们将多次引用可供下载的示例代码,所以在继续之前,您可能会下载示例代码。请参阅压缩文件中的 README.txt 文件,以获取指导信息。

Unicode 字符集与特定语言的字符集的比较
显示文字的方式有两种:一种是特定语言的字符集,一种是 Unicode。Unicode 有很多优点:每一个独立的字符集在字符集中有自己的“空间”,所以支持 Unicode 的查看程序能够同时查看多种语言。正是因为同一原因,Unicode 允许您在无需指定字符集的情况下对一种语言进行处理。然而,因为对许多应用程序来说 Unicode 支持功能相对较新,所以指定特定语言的字符集仍很普遍。因为 Internet Explorer 4.0 和 Internet Explorer 5 都支持 Unicode — 所以如果这两种是您的目标浏览器设置,我们强烈建议您采用 Unicode。

如果要快速了解 Unicode,请参阅 Web Workshop 中的 Unicode(英文) 以及在 Microsoft 全球软件开发站点上阅读 Unicode、字符集和代码页(英文) 详细内容,请参阅 Microsoft Typography 站点上的 字符集(英文)。

在 XML 文档中进行本地化
除了翻译内容之外,对于我们在这一系列文章中使用的英语 XML 数据,我们只需要进行两处改动。第一处是指定文档的编码方式。对于希伯来语菜单,如果以特定语言的字符集(即 windows-1255)编写 XML,XML 处理指令如下所示:

<?xml version="1.0" encoding="windows-1255"?>

对于以 Unicode UTF-8 编码的 XML,处理指令如下所示:

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

因为 Internet Explorer 5 支持这两种字符集,在浏览器中查看时,这些“未经处理”的 XML 文件的外观效果是一样的:

(如果您的浏览器不支持inline frames,请点击这里在单独的页面观看此图)

图 3. 在 Internet Explorer 5 中查看的 windows-1255 编码的希伯来文 XML 文件

(如果您的浏览器不支持inline frames,请点击这里在单独的页面观看此图)

图 4. 在 Internet Explorer 5 中查看的 UTF-8 编码的希伯来文 XML 文件

Internet Explorer(以及 XML 分析程序)所支持的所有字符集和代码页的列表,请参阅 Web Workshop 中的字符集识别(英文)。

对我们文件的第二处修改是在 XML 中指定 DIR 节点内容的阅读顺序。在希伯来语(以及阿拉伯语)中,阅读顺序是从右到左。在日语中,阅读顺序是从左到右。在 XSL 中我们会利用这一点告知浏览器提交内容的顺序。

在 XML 自身中只需要进行这些改动。我们将一点一点地查看对 XSL 和脚本进行的改动。首先,我们看一看需要在服务器上进行的操作。

在 Web 服务器上进行的本地化
通过 Windows 在 Web 服务器处理本地化的文字非常简单直观。在客户机中,如果您对 XML 进行 ASP 处理,您只需要保证在操作系统上安装了相应的语言包。(如果您只是向下发送已本地化的 HTML 或 XML,而不进行 ASP 处理,则不需要语言包)。如果您需要使用英语版本,则系统中已安装了西欧语言支持功能。至于日语和希伯来语等其他语言包,则可以下载。如果您在服务器上运行的是 Internet Explorer 5(无论什么情况,我们都强烈建议您使用这一浏览器),则浏览器自身即可进行语言包的升级工作,方法是:转到“工具”、“Windows 更新”,选择所需语言包。

Internet Explorer 4.0 同样支持通过“Windows 更新”添加语言包,但所支持的语种没有版本 5 多。例如,一篇希伯来语文档,可能能够在安装了 Internet Explorer 5 的任一台 Windows 机器上运行,但包括 Internet Explorer 4.0 在内的其他浏览器就需要相应语言版本的 Windows。

使用非默认字符集的任何 Active Server Pages (ASP) 文件都需要使用 @CODEPAGE 命令在页首指明这一信息。这一命令必须包含在 ASP 页的首行内;多个命令也必须位于同一行内。在下面的例子中,我们指定日语代码页以及该页默认的脚本语言。

<% @ CODEPAGE="932" LANGUAGE="JScript" %>

运行以 Unicode UTF-8 编码的日语(或任一语言)需要以下代码页。

<% @ CODEPAGE="65001" LANGUAGE="JScript" %>

每一篇文档(包括所有的 #includes)只能指定一个代码页。

在服务器上动态转换 XML
因为页中所有的 include 文件都继承宿主文档的代码页,我们指定了代码页后,include 能够自行进行设置。针对我们为动态 include 所举的示例(示例代码中的示例 3 和 8),我们只需照常进行 transformNode()。我将 XML 文件作为参数传递给 include, 而后者只需要指定 Request.QueryString 即可。

<%
  // MENUS_DYN.INC

  var sXml = String(Request.QueryString("xml"));
  var sXsl = "menus.xsl";
  
  var oXmlDoc = Server.CreateObject("Microsoft.XMLDOM");
  oXmlDoc.async = false;
  oXmlDoc.load(Server.MapPath(sXml));
  
  if (0 != oXmlDoc.parseError.errorCode)
  {
    Response.Write('XML parseError on line ' + oXmlDoc.parseError.line);
    Response.End();
  }

  var oXslDoc = Server.CreateObject("Microsoft.XMLDOM");
  oXslDoc.async = false;
  oXslDoc.load(Server.MapPath(sXsl));

  if (0 != oXslDoc.parseError.errorCode)
  {
    Response.Write('XSL parseError on line ' + oXslDoc.parseError.line);
    Response.End();
  }

  Response.Write(oXmlDoc.transformNode(oXslDoc));
%>

无论我们使用的是 UTF-8 代码页还是特定语言的代码页,动态 include 都能够自行设置。

脱机创建静态 Include
就像在上一篇文章中讨论的那样,可能会存在一些正当理由(如服务器负荷),使得我们不希望在运行时将 XML 动态转换到 HTML,而是脱机创建 includes,在运行时调用预先创建的 HTML(我们已演示了如何使用 Scripting.FileSystemObject 进行这一操作)。在处理本地化的 XML 时可以采用同一种方式,但有一点特别重要,您必须使用特定语言的字符集。请参阅示例代码中的示例 4。

XML 内部即可处理 Unicode,默认情况下在转换过程中将传递 Unicode 编码。在进行动态转换时,因为将从宿主文档的 CODEPAGE 命令中获取编码线索,所以这还不是什么问题。但是,脱机情况下转换到一份独立的文件时,就没有这样的线索了。必须告知分析程序我们将使用代码页,而不是 Unicode 或系统默认设置。我们所采取的措施是:加载一个“哑”对象,在要求的编码中带有一篇 XML 文档,将其 documentElement 替换为我们已转换的对象。最后,因 FileSystemObject 使用系统的语言设置,我们需要使用 XMLDocument.save() 方法保存静态文件。这种方式做起来没有听起来那么复杂。

<% @ LANGUAGE="JScript" %>
<%
  // MAKEMENU.ASP

  var sLang = String(Request.QueryString("lang"));
  if ("undefined" == sLang)
  {
    Response.Write('请指定一种语言。 ');
    Response.End();
  }

  var sXml = "menus_" + sLang + ".xml";
  var sXsl = "menus.xsl";
  
  var oXmlDoc = Server.CreateObject("Microsoft.XMLDOM");
  oXmlDoc.async = false;
  oXmlDoc.load(Server.MapPath(sXml));
  
  if (0 != oXmlDoc.parseError.errorCode)
  {
    Response.Write('XML parseError on line ' + oXmlDoc.parseError.line);
    Response.End();
  }

  var oXslDoc = Server.CreateObject("Microsoft.XMLDOM");
  oXslDoc.async = false;
  oXslDoc.load(Server.MapPath(sXsl));

  if (0 != oXslDoc.parseError.errorCode)
  {
    Response.Write('XSL parseError on line ' + oXslDoc.parseError.line);
    Response.End();
  }

  //技巧,确保将需要的编码传递到输出 XML
  //加载带有适当编码的菜单 xml 文件的 XML 对象
  //然后用实际转换的 XML 的文档元素来替换其 documentElement

  var oOutput = Server.CreateObject("Microsoft.XMLDOM");
  oOutput.load(Server.MapPath(sXml));
  var oTempOutput = Server.CreateObject("Microsoft.XMLDOM");
  oXmlDoc.transformNodeToObject(oXslDoc,oTempOutput);
  oOutput.documentElement = oTempOutput.documentElement;
  
  var sIncFileName = "menus_stat_" + sLang + ".inc";
  oOutput.save(Server.MapPath(sIncFileName));
%>

我将双字节语言代码作为参数传递给页,这一参数将成为 sLang 变量,因此,这一 ASP 页能够使用任一种编码方式预先生成 include 文件。注意我们使用了 oTemp,它只是用于保存已转换的 XML,并将其 documentElement 传递到 oOutput,而其中已“填充”了带有适宜编码方式的 menus-xx.xml 文件。因为我们是保存到文件,所以需要再次进行这一操作;这种情况下 CODEPAGE 命令不能为我们提供帮助。

如果只使用 Unicode 进行输出,则不需要添加这一过程。请参阅示例代码的示例 9。

如果您根本没有改动服务器上的 XML(例如,只是直接将 XML 发送给客户机),则不需要在 ASP 文件中指定代码页。

在 Web 浏览器中进行本地化
与在服务器上一样,在客户机上处理本地化的 XML 和 HTML 也需要安装相应的语言包。除此之外,对于客户机端代码还必须进行一些改动:增加 META 内容类型标记,用于指定浏览器提交内容的字符;对所有本地化元素设置 DIR 属性(只有 BiDi 语言有此要求);对于从右向左的语言,还要增加一些 JScript,将菜单右对齐。

META 内容类型
为了使 Internet Explorer 正确地提交内容,需要在文档的 HEAD 部分设置 META 标记。这将指定 HTML 的内容类型。如下所示,我们为希伯来语 windows-1255 和 UTF-8 HTML 文件指定了 META 标记。

<HTML>
<HEAD>
  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1255">

<HTML>
<HEAD>
  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">

这些字符集与 XML 文件中所用的字符集严格对应,简化了操作。

DIR 属性
查看希伯来语菜单的屏幕图片(图 2),您会发现菜单的显示是从右向左,而“常规文档内容” 的显示是从左向右。我们在元素的 DIR 属性中指定“RTL”(从右向左),即能以这种阅读顺序提交该元素。既可对单个元素设置 DIR,也可对 HTML(顶级)元素或以上两者的组合设置 DIR。

以下是希伯来语菜单的菜单栏 <DIV> 元素开始处的 HTML。

<DIV ID="divMenuBar" DIR="RTL"
  ONMOUSEOVER="if (document.all) MenuBar_over();"
  ONMOUSEOUT="if (document.all) MenuBar_out();"
  ONSELECTSTART="return false;" >

如前所述,我们将这些阅读顺序信息存储在 XML 文档的顶部,并通过 XSL 样式表传递给 HTML 输出。请您快速浏览一下上面图 3 或图 4 中的第四行。以下是实现这种效果的代码。

<xsl:attribute name="DIR"><xsl:value-of select="/TOPICLIST/DIR" /></xsl:attribute>

我们只需要在 XSL 中将这一行添加到需要输出阅读顺序敏感型元素的位置,这些元素就能够获取在浏览器中正确显示所需的信息。

在脚本中进行菜单定位
尽管 DIR 属性能够较好地处理元素中文字的阅读顺序,它却无法在需要时自动使菜单对菜单栏进行右对齐。就像我们必须在脚本中对英文菜单的元素进行显式定位一样,对于从右向左的编码,我们也需要进行这一操作。比较方便的是,我们可以检查 DIR 属性值,然后只需要添加一行 JScript 条件语句,即可完成这一操作。

以下是对 menus.js 脚本文件中的 OpenMenu() 函数进行修改后的代码。

eMenu.style.left = eSrc.parentElement.offsetLeft + divMenuBar.offsetLeft;
if ("RTL" == eMenu.getAttribute("dir").toUpperCase())
{
  eMenu.style.left = eMenu.offsetLeft - eMenu.offsetWidth + eSrc.parentElement.offsetWidth;
}

如果元素的 DIR 属性表明其阅读顺序是从右向左,我们可以通过减去菜单的宽度,再加上表单元格的宽度,对菜单的位置进行调整。由此可以将菜单的右边缘与菜单栏中相关项目的右边缘对齐。

如果不希望在菜单中出现文字折行,则不应该像这种情况一样,在 .css 文件中固定菜单的宽度,而是应该作为菜单中最长字符串长度的一个函数,在 OpenMenu() 中进行动态设置(以百分比方式或“em”单位的形式)。我将 这作为一个练习留给读者。

另一处修改
我对 XSL 文件还进行了一项修改,在原来的代码中,我们使用 TOPICLIST 的 TYPE 属性设置用于脚本的表单元格输出的 ID 号。因为这有可能受到字符集的影响,我对代码进行了修改,使用 XSL uniqueID() 方法。这种方法将分配一个独一无二的数字 ID 号,在浏览器中实时运行脚本时能够避免受到字符集的任何干扰。我将原来的代码作为注释保留在 menus.xsl 格式表中。

<xsl:attribute name="ID">tdMenuBarItem<xsl:eval>uniqueID(this)</xsl:eval></xsl:attribute>

总结和其他信息
使用 XML 保存并转换本地化的项目易于反掌。我们只需要在 XML 文件中添加一类属性和一个元素,在 XSL 文件中添加一行,再在脚本文件中添加一行,我们就能够处理浏览器所支持的任一种语言。

因为 Internet Explorer 5 中对语言支持功能进行了加强,它肯定是理想的目标浏览器。如果您的目标受众使用的正是这种浏览器,您就可以谢天谢地,直接使用 Unicode UTF-8 作为编码方式,从而不需要再操心与特定语言相关的代码页,并可获得基于浏览器的最广泛的语言支持功能。对于您来说,它也是一个非常不错的开发环境。


W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
46.875ms