分类: 嵌入式
2011-03-07 11:49:29
W3C 对双向字符集语言(即 BiDi)进行了如下的定义:
双向字符集语言通常是指文字可以从左到右(LTR)和从右到左(RTL)双向书写的文字。例如,阿拉伯和希伯来语言的文字书写通常是从右到左,但是其中的其它字符集(例如拉丁文字)依然保持从左到右的格式。当然其它的语言文字,例如英文,如果包含阿拉伯或者希伯来文字摘要的话,也可以是双向书写的。通常将主方向称为全局方向。
对于介绍一种的新的知识或者技术,笔者通常习惯于从“what、why、who、how”几个方面来进行讲述。即 BiDi 是什么,这在本节中作了介绍。为什么需要支持 BiDi,这会在下一节中介绍。随后则偏重于介绍作为一名 Java 软件开发人员在支持 BiDi 的时候要考虑哪些问题,如何做才能实现 BiDi 支持,以及一些常见的问题和解决技巧。
对于使用双向字符集地区的人来说,对于本国文字和其它字符集文字,例如和英语混排的情况,在纸面上书写将是一件需要有规划技巧的事情。如下所示:
CIBARA english CIBARA
CIBARA 代表阿拉伯字符,english 代表英语字符。书写者需要从右到左书写字符“A”“R”“A”“B”“I”“C”,在纸面就变成 CIBARA。随后要书写英文字符 english, 这段字符要从左到右显示,这就需要书写者要先规划合适的空白来书写文字,然后继续下面的阿拉伯字符的显示。否则要么空间不够,写不下那么多文字,要么留下一段空白影响文档的整体结构。
计算机的出现对双向字符集的录入应该是一大福音,上面所述的空白规划将不再是问题。当计算机切换到 BiDi 文字的录入方式后,对于一般的双向字符集字符,例如阿拉伯文字,显示将遵循全局方向从右到左(RTL)。当需要输入英文字符的时候,计算机将自动处理英文字符的显示,将先输入的字符自动向左边排,后输入的字符显示在前面字符的右侧,将先输入的文字顶到了左侧,而录入光标将一直停留在英文录入的最右侧,依次处理随后的文字录入,并显示。这样录入者就不用关心这段英文文字将占据多大空间,而且英文内容保持了从左到右(LTR)的方向。 当用户需要输入阿拉伯文字的时候,阿拉伯字符将自动放置到英文内容的左侧,录入光标也跟随到了阿拉伯字符的左侧,开始正常的从右到左(RTL)的录入,并显示。
计算机软件要在使用双向字符集的地区(主要是中东地区)销售、使用,如果没有 BiDi 的支持,将是一件不可能的事情。对 BiDi 的支持也是软件国际化(globalization)标准重要的组成部分,没有 BiDi 的支持就不能说对软件实现了国际化的支持。世界上各大软件公司都把对 BiDi 的支持作为产品能否发布的一个重要考核指标。
事实上对 BiDi 的支持,要考虑的内容非常多,从底层 BiDi 算法(例如 ICU4J)、编码(Unicode)、存储、输入法、直至开发工具的支持。然而作为一名开发人员(本文主要针对 Java 开发人员),其实不需要了解太多底层实现的细节。针对不同的软件图形库,只需要很少的代码层面的处理就可以实现对 BiDi 的支持。如果读者想要了解更多 BiDi 层面的知识、术语,例如 logical order、visual order 等等可以参照 developeWorkss 或者 W3C 上的其它文章。
Java SDK 不同的版本对 BiDi 的支持能力也是不一样的。本文的示例以及代码片段主要是针对 JDK 1.5 的版本。
Java 提供了 AWT 和 Swing 两种图形库,这两种图形库对 BiDi 的支持是不一样的。软件对 BiDi 展示层面的支持包含整个软件界面的布局从 LTR 到 RTL 的转变,以及控件的布局。例如 ComboBox 的下拉箭头从左边移到右边,控件里面的文字的布局转换,文字的录入等等。
Swing 是一个用于开发 Java 应用程序用户界面的开发工具包。它是 Sun 与 Netscape 合作建立的一个高级图形库。与 AWT 相比,Swing 具有更丰富而且更加方便的用户界面元素集合,Swing 对于底层平台的依赖更少。因此,Swing 可以在不同平台上提供给用户统一的视觉体验。
在 BiDi 支持方面,因为 Swing 组件是纯 Java 编写,所以 Swing 的 BiDi 支持可以完全独立于底层的操作系统。
ComponentOrientation 还提供了常量 , 来指定全局方向的取值。
这个方法可以设置 java.awt.Component 对象以及所有它所包含的子对象的全局方向。
下面的示例代码演示了如何打开 Swing 对 BiDi 的基本支持,并根据系统的区域设置来决定应用程序的全局方向。
public class BiDiApp extends JFrame { ... public static void main(String[] args) { ... new BiDiApp (); ... } public BiDiApp () { ... super(); ... applyComponentOrientation(ComponentOrientation .getOrientation(Locale.getDefault())); ... } } |
通过方法 applyComponentOrientation,Swing 控件以及其包含的子控件的 BiDi 设置,都可以根据计算机当前的区域设置来自动调整。如上一段代码所示,如果计算机当前的区域是在英语区域,则应用程序启动以后,按照正常的英语应用程序显示和处理文字。如果计算机当前的区域是在双向字符集区域,程序启动以后,程序的 UI 布局 、控件的文字方向,以及录入光标的位置等等就会变成 BiDi 地区的使用方式。
注:显示非英文字符需要相应的文字资源以及相应的国际化支持,这些不在本文的介绍范围之内,读者请参阅其它资料查看这方面的内容。
虽然通过上面的方法可以打开对 BiDi 的支持,但是 Swing 中的一些控件对 BiDi 的支持是有缺陷的,这需要开发人员做一些额外的处理才能完整地支持 BiDi。下面笔者将一一介绍这些有 BiDi 支持缺陷的 Swing 控件以及其对应的解决办法。
在应用了 applyComponentOrientation 以后,JFrame 控件的标题栏如图 1 所示,是没有翻转成为从右到左 RTL 方向的。开发人员可以采用下面这段示例程序来使 JFrame 的标题栏变为从右到左 RTL 方向。
if (!ComponentOrientation.getOrientation(Locale.getDefault()).isLeftToRight()) { JFrame.setDefaultLookAndFeelDecorated(true); } |
应用这段代码以后,图 1 的应用程序标题栏将会变成如图 2 所示:
需要指出的是,应用这段代码以后,JFrame 的外观将不再遵循操作系统界面风格。用户需要自己决定是否采用这段代码来实现 JFrame 标题栏的 RTL,还是保持界面整体的风格不变。
javax.swing.Box 是一个轻量级的,可以放置各种简单控件(例如 JButton)的容器。使用 Box 可以比较方便地控制其中控件的布局。然而,使用 Box 也带来了 BiDi 上面的一个缺憾,applyComponentOrientation 设置的全局方向不能应用到 Box 里面包含的各个控件上。
为了使 Box 里面的控件布局符合 ComponentOrientation 的取值,开发人员必须通过如下示例代码来生成 Box 的实例对象。
Box box = new Box(BoxLayout.LINE_AXIS); //将控件加入到 box 对象中 box.add(Btn1); //Btn1,instance of JButton box.add(Btn2); //Btn2,instance of JButton |
这样 Box 里面包含的控件将遵循这个应用程序的全局方向,如下图所示:
即便 ComponentOrientation 应用了从右到左 RTL 的全局方向,javax.swing.JComboBox 控件里面的文字缺省还是按照左对齐的方式显示。如下图所示:
开发人员可以调用如下代码生成 JComboBox 的实例对象,强制 JComboBox 对象的文字遵循右对齐的方向。如图 6 所示:
JComboBox box = new JComboBox(); //align all the comboBox items to RTL in case Arabic/Hebrew locale if (!ComponentOrientation.getOrientation(Locale.getDefault()).isLeftToRight()) { ((JLabel)box.getRenderer()).setHorizontalAlignment(SwingConstants.RIGHT); } |
javax.swing.JTable 的表格里面的内容在缺省情况下也不会按照 ComponentOrientation 取值进行显示。即便 ComponentOrientation 是从右到左 RTL,JTable 还是会如下图所示显示内容:
开发人员可以调用如下代码生成 JTable 的实例对象,强制 JTable 对象里面的文字遵循右对齐的方向。如图 8 所示
JTable table= new JTable(...); //align all the table column data to RTL in case Arabic/Hebrew locale if (!ComponentOrientation.getOrientation(Locale.getDefault()).isLeftToRight()){ ((JLabel)table.getDefaultRenderer(String.class)).setHorizontalAlignment(JLabel.RIGHT); } |
javax.swing.JLabel 是一个 GUI 开发中非常常用的控件,可以用来展示界面上的文字内容。通常来讲,使用 JLabel 控件是遵循 ComponentOrientation 指定的全局方向的。但是如果用户强制使用代码指定了控件的显示方向,新的设置将覆盖 ComponentOrientation 的全局方向。
JLabel label = new JLabel(text, icon, SwingConstants.LEFT); … label.setHorizontalAlignment(SwingConstants.LEFT); |
为了使控件能够按照 ComponentOrientation 指定的全局方向显示内容,开发人员应该避免在生成控件或者指定对齐方式的时候,使用左常量 SwingConstants.LEFT,右常量 SwingConstants.RIGHT。开发人员应该使用专门支持 BiDi 的常量 SwingConstants.LEADING,SwingConstants.TRAILING 来指定对齐方式。
在使用 JLabel 和 JButton 控件的时候,需要注意对齐方式的取值问题。当然对于继承了这两个类的控件,例如 class DefaultTableCellRenderer extends JLabel,也需要注意不要直接使用左常量 SwingConstants.LEFT,右常量 SwingConstants.RIGHT。
Eclipse 是目前开发人员最为推崇的 Java 集成开发工具。Eclipse 的图形开发可以基于 Eclipse 特有的 SWT 和 JFace 架构。
前面已经说过 Swing 是纯 Java 的实现,而 SWT 则有点类似与 AWT,是 Java 与 JNI 的混合,当然具体的实现和 AWT 是完全不同的。至于 Swing 和 SWT 的优劣比较,不在本文的介绍范围之内。这方面的内容在网络上有很多,仁者见仁,智者见智。有兴趣的读者可以参见比较著名的一篇文章“SWT、Swing 或 AWT:哪个更适合您?”。
回到本文,因为 Eclipse 采用了和 Swing 截然不同的图形库,在 BiDi 支持方面也有不同的要求。根据笔者的开发经验,在 Eclipse 上开发图形应用程序,基本上不需要特殊的编程处理,BiDi 的实现细节都已经封装在 SWT/JFace 的图形控件里面。只需要将区域设置设定为双向字符集的地区,启动 Eclipse 应用程序,程序就会自动启动 BiDi 的支持。
总体来说,采用 Eclipse 开发支持 BiDi 的 Java 图形应用,相对于 Swing 来说,工作量大为减轻。
在双向字符集语言中,标点符号的处理是 BiDi 算法中一个需要特别关注的地方。在 BiDi 中,所有的非标点符号被称为“强”字符。而标点符号既可以是从左向右 LTR 也可以是从右向左 RTL。因为不含任何的方向信息,所以被称为“弱”字符。通常是由软件根据 BiDi 算法来决定标点符号放置的方向。
在 BiDi 算法中,如果标点符号放在两段有相同方向文字的中间,标点符号将继承相同的方向。如果标点符号放在两段有不同方向的文字中间,标点符号将继承全局方向。如果一个“弱”字符紧挨着一个“弱”字符,BiDi 算法将根据最近相邻的“强”字符来决定字符方向。在某些情况下,这会导致非预期的情况出现。为了纠正错误,需要使用“伪强”字符(U200E RLM 或者 U200F RLM)插入到文字中间来调整字符的显示。下面就用具体的示例来说明如何使用这两个符号来调整内容的显示。
有如下一段英文“UDDI inquiry URL:”,开发人员要将这段文字的阿拉伯文字(单词 UDDI 和 URL 不需要翻译)在界面上用 JLabel 控件展示出来,并注意 对齐方式 章节涉及的内容。在界面上文字的显示如图 10 所示。
这段文字的阿拉伯资源为“URL \u0644\u0637\u0644\u0628 UDDI:”,标点符号在显示的时候应该在英文单词的左侧,而实际上 Swing 却把这个符号显示在了右侧,甚至把两侧的英文显示也颠倒了。
为了正确的显示这段文字,开发人员需要在上面的阿拉伯资源前增加一个 LRM(\u200F) 来强行指定文字的方向。文字的正确显示如图 11 所示
String text = new String(“ text to be displayed”); if (!ComponentOrientation.getOrientation(Locale.getDefault()).isLeftToRight()){ text = "\u200F " + text;// $NON-NLS-1$ } JLabel label = new JLabel(text, SwingConstants.LEADING); |
在双向字符集文字中,界面的全局方向一般都是从右到左(RTL),但是对于一些特殊的内容 URL(例如 )、文件路径(例如 c:\windows)、XPath(例如 /bookstore/book)等,在任何情况都需要保持从左到右的方向。也就是说这些内容在双向字符集和非双向字符集中的显示都应相同。如有不同,就需要开发人员的处理。
很多情况下,因为大部分开发人员并不认识双向字符集文字,开发人员就不能确定到底是资源的问题还是代码的问题。如果有一个方法能够使开发人员清楚正确的显示是什么样子,那么对于解决问题会很有帮助。下面就具体介绍这样一个方法。
开发人员都知道 Java 中,非英文资源串的存放一般是使用 Unicode 编码(如果对 Java 国际化资源管理不了解,请参看其他文档),例如下面的阿拉伯文字资源:
LABEL_TAG=URL \u0644\u0637\u0644\u0628 UDDI: |
其对应的英文资源是:
LABEL_TAG=UDDI inquiry URL: |
如果希望看到这些阿拉伯资源在 BiDi 下的如何正确显示,请按照下面几个步骤操作:
1) 利用资源分析工具 转换 unicode。将资源“URL \u0644\u0637\u0644\u0628 UDDI:”拷贝到 Mixed input 编辑框中,然后点击按钮 Convert,转换后的字符就显示在下面的各个编辑框中,如下图所示:
查看 图 12 的清晰版。
2)利用 IE 或者 Firefox 浏览器观看 BiDi 字符的显示。在这里把浏览器作为 BiDi 显示的基准软件,将 Unicode Code Converter 软件上的 Characters 编辑框中的内容拷贝出来,然后粘贴到在从右到左 RTL 模式下启动的浏览器中(本文中,笔者使用了 IE)。读者可以看到在浏览器里内容的显示是和图 11 是一致的。如果开发人员开发的应用程序的内容的显示与浏览器里内容显示不一致,就需要开发人员参照本文上面论述过内容,检查代码来进一步处理。
通过阅读本文,Java 开发人员能够从中获得 BiDi 支持相关的知识,并且能够从示例和代码片段中掌握 BiDi 的开发技能以及 BiDi 问题的解决技巧。
学习
讨论