从右到左语言支持的曲折之路

目前还没有读者喜欢这个。
left and right brain

Opensource.com

我观看了 Moriel Schottlender 上个月在 Geelong 举办的 linux.conf.au 2016 上关于这个主题的演讲,并邀请她投稿一篇文章。当您观看她的演讲视频并阅读她的文章时,您就会明白为什么我无法做好笔记来撰写关于这次演讲的文章,所以我很高兴她直接向我们投稿了她的故事。—Rikki Endsley

 

英语是从左到右书写的。希伯来语是从右到左书写的。我们知道这一点。浏览器——在大多数情况下——也知道这一点,就像它们知道网页的默认方向是从左到右 (LTR) 一样,并且如果有一个设置明确定义方向为从右到左,页面应该像镜子一样翻转。浏览器就是这么智能。大多数时候是这样。

但即使是浏览器在决定如何处理混合语言时也会遇到问题,我的朋友们,当您键入和查看双向文本时,这就会成为真正奇怪问题的根源。

字符和字符串的双向性

在我深入探讨一些有趣的混合方向性问题的示例之前,我应该首先回顾一下浏览器是如何考虑方向性的。

我已经说过,英语被认为是“LTR”语言(从左到右),而希伯来语、阿拉伯语、乌尔都语(以及其他一些语言)是 RTL 语言(从右到左)。这些都相当明确,如果您键入一个仅由这些语言组成的字符串,情况或多或少还可以(但我稍后会讨论一些问题)

但并非所有字符串中的字符都是相同的。**

希伯来语和英语(以及其他一些语言)属于“强”方向性类型,它们不仅具有方向性,而且还会影响周围环境。有些字符具有“弱”方向性,即尽管它们内部具有方向性,但它们不会影响周围的字符。有些字符仅仅是中性的,这意味着它们的方向性由其周围环境决定。哦,还有一些字符可能会(并且确实会)根据它们所在的文本在视觉上翻转。

别担心。我将解释 eeeeeeeeeeeeeverything。好吧,我将尝试,所以请继续阅读。

字符方向性类型

Unicode,这是最常见的在线编码系统,为字符组定义了字符类型方向性,分为中性。这些类型控制这些字符在字符串中的呈现方式。

在互联网的早期——很久很久以前,当恐龙在地球上漫游,而你们这些正在阅读这篇文章的人中有一半可能还在穿尿布——互联网几乎认为一切都是从左到右的。

我记得用原始 HTML 构建网页,我们今天大多数人都会对此感到畏缩。那时没有真正的网站,只有静态 HTML 页面的集合,这些页面通常包含可怕的标签,例如 <blink> 和 <marquee>,并且页面中的所有内容都使用一种字体,背景都是平铺的。啊,美好的旧时光。

在那些日子里,希伯来语实际上是倒着输入的。如果我想写希伯来语单词“שלום”,它以希伯来字母“ש”开头,我必须倒着输入它,从字母“ם”开始,并产生“םולש”——因为字母会从左到右依次出现。当键入一两个单词时,这可能是可行的,但如果您有一个完整的段落或一整篇文章,它可能会很快变得令人恼火。

在那些古老的日子里,您可以下载一些工具,这些工具可以将您的文本翻转过来。因为那时我们就是这样做的。

幸运的是,Unicode 出现了并定义了方向性,尽管 Unicode 仍然存在问题,但 RTL 用户至少可以正常键入他们的语言,而不是学习倒着书写。这很有帮助。

强类型

强类型是具有显式方向性的字符集。希伯来语始终是从右到左的。英语始终是从左到右的。** 当我键入这些字符集中的任何一个时,我的字符将按照方向性依次出现。这就是单词“Hello”从左到右出现的方式,而单词“שלום”从右到左出现的方式。

强类型还设置了它们所在空间的方向性,这意味着如果我在您现在正在阅读的句子中间插入任何具有弱或中性方向性的字符(我已经这样做了),它们将采用强类型字符串的方向性——在本例中为英语。因此,强类型不仅仅是关于字符本身,还关于其周围环境

弱类型

弱类型很有趣。这些是可能具有方向性的字符序列,但它不会影响其周围环境,并且可能会根据其周围的文本进行调整。此组中的字符包括数字、加号和减号、冒号、逗号、句点和其他控制字符。

根据 Unicode 双向性算法规范,弱类型根据之前的字符解析

中性类型

中性类型是最有趣的。中性字符类型可以是右到左或左到右,因此它们完全取决于周围的字符串。这些包括换行符、制表符和空格等。

根据 Unicode 双向性算法规范,中性类型根据周围的文本解析其方向性。

隐式级别类型:当您键入的内容与您获得的内容不太一样时

因此,我们有强类型、弱类型和中性类型,但这并不是我们方向性双重考虑的终点。事实上,真正令人困惑的是在 RTL 或 LTR 中以不同方式解析(即,它们字面上呈现不同形状)的字符。

是的,您没看错:当在 LTR 字符串中写入与在 RTL 字符串中写入时,它们实际上字面上并且非常明显地看起来不同。

这方面最好的例子是括号和(我个人最好的朋友)方括号。这些符号实际上已经是方向的图标。键盘上带有“(" 符号的按钮并非完全如此,而是“左括号”的符号。在英语(从左到右)中,符号自然是 ( 表示左括号,而 ) 表示右括号。但在希伯来语和阿拉伯语以及其他 RTL 语言中,“左括号”符号是相反的 ),因为字符串是从右到左的。因此,此符号将在您的屏幕上显示为 (),具体取决于您键入的位置。

我知道,对吧?

两种方式的混合

通常,如果文档(特别是联机文档)中仅使用一个方向,则问题不会那么明显,因为强类型文本默认情况下会包围所有其他弱类型和隐式级别字符类型,使其成为自己的类型。

当我们必须混合语言和方向,或在旨在用于 LTR 的块内使用 RTL 语言时,就会出现问题。这在网上经常发生——如果 HTML 文档中任何地方都没有显式的 dir="rtl",则文档默认为 LTR 方向性。页面的方向性(通过使用 dir='rtl'dir='ltr',或者根本不使用 dir= 属性并依赖于其默认回退到 'LTR')被认为显式设置了预期文本的方向性。因此,任何方向性不明确的字符都将采用该属性设置的方向。

例如,如果我尝试在具有 dir='ltr' 的页面中的文本框中键入 RTL 语言,我可能会遇到很多令人恼火的问题,例如标点符号、句子片段的位置以及混合强类型语言。如果我尝试在 RTL 设置的文本框中键入 LTR 语言(例如英语),也会发生同样的情况。

它可能会变得非常令人困惑,以至于很多时候,当我试图弄清楚如何在 RTL 框中键入 LTR 文本并查看我的文本实际是如何组织自己的时,我的精神状态几乎崩溃了。

好的、坏的和丑陋的

因此,显然,Unicode 的创建远优于之前存在的反向键入(以及需要使用多种单独的字体)。浏览器倾向于遵循 Unicode 规则(尽管执行自己的渲染的应用程序有时不会,但那是另一个问题。)并且此 Unicode 方向性算法为我们在键入不同方向时提供了许多真正的好处,但它也有坏处,有时甚至是真正丑陋的东西。

好处

确实,由于 Unicode 的双向性算法,发生了很多好事。正如我已经提到的,RTL 用户可以正常键入他们的语言(而不是倒着键入)已经是件好事了(并且我从经验中知道,因为我曾经使用过没有这个好功能的系统。)

双向性算法的其他好处是我们可以使用数字(弱类型 LTR)在 RTL 文本中使用。例如,考虑以下文本

ניפגש ב09:35 בחוף הים

从字面上看,这意味着“我们将在 09:35 在海滩见面。” 但是请注意,即使没有任何方向性修复,数字 09 和 35 仍然是从左到右的,因为数字就是这样读取的——但是当我写这个句子时,我真的不需要手动反转我的输入。浏览器为我做了这件事。

这是一个很好的练习。选择该句子。当您这样做时,您可以看到哪个部分具有什么方向性。这引出了...

坏处

选择

选择是双向文本问题的主要部分。正如您从“好处”的示例中看到的那样(我不需要反转键入),也有不好的一面,那就是如何选择我的文本。选择可以是逻辑的或视觉的。光标移动也是如此,我将在稍后介绍。

视觉选择很简单——视觉——这意味着选择将文本段视为一个连续的块,而不管方向如何。

逻辑选择意味着文本被划分为其双向片段。这意味着,如果我从 RTL 文本的开头(在右侧)开始选择,并将鼠标拖动到其末尾(向左),则当我到达数字部分时,选择将拆分,因为数字是从左到右的。

这确实是合乎逻辑的,因为它从逻辑开始到逻辑结束,并且由于文本是双向的,因此这两个指针对于每个部分都是不同的。这很有道理,但可能会令人困惑。

光标移动

同样,光标也可以逻辑或视觉方式移动。这可能有点令人困惑,有时这种行为在不同平台之间不一致。但是,大多数时候,移动是逻辑的。

因此,这是一个快速测试,了解这种行为在何处会变得非常奇怪。考虑以下句子。它在文本框内。因此您可以正确地选择它并在其中移动光标。

 

尝试从开头(左)到结尾(右)选择文本。看看当您悬停在希伯来语单词上时会发生什么?

现在,如果您在给定的文本框中移动标记,光标(例如,在 Windows 中的 Chrome 和 Firefox 中)将视觉而非逻辑地移动。也就是说,您可以像没有两种不同的语言一样从末尾移动到开头。

但尝试将此字符串复制/粘贴到记事本(或等效的简单软件)中,然后从头到尾移动光标。通常,这些编辑器会逻辑地移动鼠标,公平地说,这比视觉移动更有意义。

它还向您展示了 RTL 行为在某种程度上是不可预测的;一些程序以这种方式执行,一些程序以那种方式执行。一些浏览器将采用视觉方式,一些浏览器将采用逻辑方式,并且还有一些 CSS 规则可以覆盖这些决定,因此它可能会在网站之间发生变化。

不错,是吧?

标点符号

好吧,那是一个最初是“LTR”的文本框。但是,如果我在 LTR 框中写一个希伯来语句子,或者反过来——在 RTL 文本框中写一个英语句子,会发生什么?那时,我们可爱的朋友——弱类型标点符号——就会出来玩耍了。

哎呀,最后的句点在哪里?

这是反向版本

那个最后的句点去哪儿了?

两种语言在一起,Kumbaya

但是,还有更棒的事情,它与选择和光标移动(以及渲染和使用等)有关。

上面的示例以强类型(英语或希伯来语)为特色,它与弱类型(数字)混合,并被中性类型(空格)混合。但是,如果我创建一个字符串,其中包含两种相反的强类型,并与中性类型空格和弱类型标点符号混合,该怎么办?

继续,尝试从头到尾选择该句子

或反向

(感谢 Amir Aharoni 提供此信息)

让我们花一点时间回顾一下那个可怕的文本框中发生了什么。首先,第一个文本框中的部分问题是文本框被强制为 RTL,并且由于其中的大多数文本是英文,因此它在奇怪的位置中断了。以下是句子强制为 LTR 时的样子

请记住,英语是强类型 LTR,但 עברית 是强类型 RTL。当将英语和 עברית 混合在一起时,您可能会得到一些令人惊讶的结果。

但是请注意,文本框问题在反向情况下也同样发生,其中框是 LTR,句子主要是 RTL。

对于强制 RTL 文本框(以及大多数文本是强类型 LTR),空格采用了它们周围文本的方向性,即 LTR。然后我们有一个强类型 RTL 希伯来语单词,这使得它内部的空格变为 RTL,但周围的空格(RTL 单词和 LTR 句子之间的空格)仍然受到周围文本的影响,即 LTR。

如果您仍然跟我在一起,这可能有助于说明问题。基本上,您有这个

[ENGLISH_CHUNK 3] hebrew [ENGLISH_CHUNK 2] hebrew [ENGLISH_CHUNK 1]

整个句子结构是从右到左的,但小的英语部分是从左到右的。整个“块”方向是 RTL。每个块都有自己的内部方向。当您阅读它时,它看起来很混乱——因为它确实是。

这在第二个文本框中完全相同(只是相反)。LTR 代替 RTL,反之亦然。

我知道。我... 知道。

丑陋的东西

现在我们进入丑陋的领域,这些东西不仅行为困难,而且还产生视觉上不同的结果。还记得那些弱类型和隐式级别类型吗?这就是它们出现的地方,我告诉你,它们彻底让我们感到困惑。

空格

空格是隐式级别类型,这意味着它们由它们所在的文本定义。您现在正在阅读的句子中的空格是隐式 LTR,因为它们在英语文本中。这里的空格:במשפט הזה יש רווחים ואלה מוגדרים ימין לשמאל 是隐式 RTL,因为它们在希伯来语中,即使页面本身是 LTR。

这很好,但它也产生了一些奇怪的结果。考虑这样一种情况:我在文本中有一组数字。数字之间用空格分隔,空格由周围的文本定义。但是数字本身是“弱”类型,这意味着它们不会影响自己的周围环境(即使它们在内部是 LTR)。空格必须从包围整个数字段的任何单词中获取其方向性。

这听起来很奇怪?这种行为甚至更奇怪。例如,看看这个

我特意将这些数字封装在 LTR 文本中,因此分隔这些数字的空格仍然是 LTR。但是,如果您用希伯来语(RTL)单词替换那些英语单词,您认为会发生什么?好吧,这个例子与之前的句子和数字序列完全相同,顺序也完全相同,唯一的区别是“Start”和“End”被它们各自的希伯来语单词替换了。

数字被反转了!数字... 被... 反转了?您是否已经头晕目眩了?这可能很奇怪,但这是有道理的;空格现在被封装在 RTL 文本中,这意味着它们现在是 RTL。RTL 句子中的空格是从右到左的,因此数字分组从右到左进行。

但我认为您的头脑还没有快速旋转到足以理解。如果我们在数字分组本身内部添加空格会发生什么?我的意思是,数字在内部是 LTR,但空格是 RTL,所以我们将添加一个空格来打破该组,并且... 并且该组将开始旋转?

试试看。在下面的数字组中添加空格。

看到了吗?看到了吗?

是的。没错。

括号和方括号

正如我在这篇文章前面讨论的那样,方括号和括号实际上代表“开始”和“结束”,这意味着根据它们插入的位置,它们可能会在屏幕上以不同的方向显示。

因此,如果我按下键盘上带有漂亮的 [ 符号的按钮(在 { 下面,靠近 P),我将在 LTR 和 RTL 中获得不同的结果。

这意味着这段代码

LTR:
<span dir="ltr">[</span>
RTL:
<span dir="rtl">[</span>

变成这样:LTR: [ RTL: [ 是的,我单击了同一个按钮。是的,我确定。欢迎您查看源代码。

这种效果不仅仅是奇怪的事情,而且当需要在 RTL 文本框中添加一些 html <tags> 时,它会变得非常令人沮丧。是的,这种情况发生在维基百科中,也发生在 RTL 维基百科中。

尝试将 <span style="font-size: 2em"> 添加到下面文本的某个片段。祝您好运,保持理智,并记住呼吸。如果您感到特别有冒险精神,您还可以尝试插入一些维基文本,例如指向页面“Somewhere”(英文链接)的链接,并带有希伯来语标题。

想更疯狂吗?在希伯来语文本后添加一些英语文本,并尝试从希伯来语字符串开始设置一些 <a href="https://open-source.net.cn/something.html"> </a>,并在英语字符串处结束。

全部键入,不要作弊并复制/粘贴。真正尝试一下。去吧,玩吧。尝试。疯狂 RTL。

在线文本编辑器和维基媒体的 VisualEditor

现在我们已经经历了与在线使用从右到左文本相关的一系列可怕的有趣的挑战,我们可以看到这些挑战如何影响在线文本编辑器的开发工作。在维基媒体基金会,我们一直在开发 VisualEditor——一个用于编辑维基百科文章的 WYSIWYG 系统。它不仅可以处理将 HTML 转换为维基百科的“维基文本”语法,而且还必须处理多种语言、多种方向性、平台、浏览器和本地化环境。基本上,我们需要支持我们上面讨论的所有情况,以及其他一些情况。这有多难?

作为文本编辑器,VisualEditor 希望用户在其中键入内容,他们也确实这样做了。他们还使用多种语言这样做,并且更常见的是,在同一篇文章中混合使用多种语言。混合语言非常常见,尤其是在维基百科中,当需要提供从另一种语言借用的单词的原始脚本或其本机脚本的城市名称等时。

但是正如我们所看到的,键入可能很棘手,尤其是在我们混合方向时。我们必须确保允许用户在键入时看到他们在页面中将获得的逻辑结果。我们还必须确保他们的键入有意义,并且如果需要将特定跨度的文本描述为不同的方向,他们可以轻松地做到这一点。我们必须确保正确解释他们的输入,RTL 在 ContentEditable 屏幕中正确显示,然后在保存的文章中正确呈现。

此外,正如您从我上面关于 [ 字符的示例中看到的那样,HTML 代码和最终渲染之间存在差异。也就是说,我键入了 [,但得到了 ],而 [ 出现在代码中,但 ] 出现在我的最终渲染标记中。这应该在 VisualEditor 中发生吗?当您键入的内容预计会被翻转时,WYSIWYG 就大不相同了。

这些事情并非不可能处理,但它们非常具有挑战性,并且通常需要就用户应该期望什么做出决策。大多数在线(和离线)应用程序在处理 LTR/RTL 键入时都存在问题,这使得这些战略决策更加复杂。行为需要根据我们认为的最佳方式进行设计,而不是 RTL 用户所期望的方式,因为正如您可以从当前行为中看到的那样,RTL 用户通常期望可怕的行为

但这是一种好的挑战。很多人都关心找到一种好的方法来解决这个问题。

但是等等,还有更多

双向文本还有许多其他问题,其中一些问题存在于已发布的软件和在线应用程序中,这使得 RTL 用户的生活相当烦人。我可能会在某个时候写关于这个的文章,并分享我的 RTL 挫败感。如果您对这些挑战如何转化为日常生活感兴趣,您还可以访问 http://rtl.wtf 并亲眼目睹 RTL 用户在线经常遇到的情况。

在本文中,我回顾了 LTR 框内的 RTL 字符串问题、方向性不明确的字符问题、选择和光标移动问题以及一般的“嗯”问题。当然,还有更多的 RTL 困难,但这篇文章旨在作为对主要和最常见的双向性问题的介绍。

我希望您喜欢它。至少,我希望您现在了解程序员(和 RTL 用户!)必须处理的事情。

侧边栏

语言和文字

在本文中,我使用术语“语言”来指代英语和希伯来语字母。事实上,我应该使用术语“文字”来指代字母和字符本身。这种差异主要来自这样一个事实,即尽管希伯来语和英语是语言,但它们各自使用的字符也可能在其他语言中使用。例如,英语使用拉丁文字,希伯来文字也可以在意第绪语中使用。

因此,请考虑到这种情况,并且使用的实际字母是 LTR 还是 RTL 实际上是“文字”而不是完全是语言,因为浏览器实际上并不关心您使用这些文字实际键入的单词是什么。

但是,为了简单起见并尽量减少混淆,我做出了一个战术决策,将所有内容都归为最熟悉的术语“语言”。(感谢 MatmaRex 指出我至少应该提及这种差异。)

实用链接

User profile image.
Moriel 是一位从物理学家转行成为软件工程师的人,她说和想都是从右到左的。她于 2011 年在纽约城市学院获得物理学理学学士学位,她的研究重点是“模拟松散弹簧沿斜面弹跳的运动学方程”,或者用通俗的英语来说,就是找到 Slinky 的方程。

1 条评论

哇。双重哇!还有更多:RTL 哇!

恕我直言,问题源于混合书写系统。如果我写一个问题:“¿Que pasa?”,人们会立即认出它,不仅是因为开头的额外疑问符号,而且主要是因为周围的双引号。据我所知,这是传统的语言用法 AFAIK,有助于在正确的上下文中更轻松地解释单词。

因此,无需过度复杂的 AI 来发现正在谈论的内容;我们只需要使用分隔符(也许还需要添加某种约定来注释文本段的语言),生活就会再次充满欢乐。

除了一些真正棘手的问题,例如将印度数字与希伯来数字一起使用。就我个人而言,我认为人们应该选择一种方式……例如,当用希伯来语书写时,“Easy as 123”应该类似于“321 sa ysaE”。就这样。简单。容易。一致。但不,它必须是“123 sa ysaE”……(实际上,正确的书写方式必须是 3-2-1,所以我有点勉强地表达我的观点)。这是无法修复的,但至少,在希伯来语上下文中,复杂的行为在某种程度上是可以预测的……问题仅在 IMHO 中出现,当文本以两种语言/方向书写时,没有明确的标记(例如使用引号)。
现在,对于另一方面,将符号的名称与其功能分开非常重要。例如,正如视频中所示,符号“(" 和 “)" 会引起一个问题,该问题与如何在 LTR 和 RTL 系统中解释它们有关。但是,讨论的术语实际上并没有解决问题:之前/向后、之前/之后、开始/结束……所有这些都与位置有关。因此,它们不会解决这个难题。我们必须使用想法,即期望的功能。就像在“打开/关闭”中一样。这些词更通用,并且不表示任何方向。哪个是 LTR 左括号?它是“("。RTL 的呢?它是 “)"。当在两个系统中阅读文本时,不要说或使用“左括号”,而要使用“打开”……软件需要理解(就像在搜索匹配的结尾一样)所指的内容。箭头等具有方向性质的键带来了一个特殊的问题。视频中通过仅向右移动与向右选择文本之间的差异来说明了这一点。我们还应该有(IMHO)不同的键:前进和后退。一种经济的实现方式是重新定义文本处理上下文中箭头的操作,以便它们在移动和扩展选择时以相同的方式工作。但是,如果有人真的想向右走怎么办?我想可以使用类似于 AltGr+Right 的组合键。区分我们不能使用“前进到下一个字符”作为用于向右的相同键非常重要。用作“前进”的键会根据上下文产生不同的结果。当然,还会有进一步的问题要解决,例如橡皮筋选择的情况。
关于 HTML 编辑,我们又遇到了同样问题的重现,这个问题仍然可以使用引号来转义字符串,使其表示为不同的系统。关于 HTML 语法,我认为它不能双向:要么是 LTR(从左到右)书写,要么是 RTL(从右到左)书写。为了方便起见,我假设我们可以使用标签来标记 HTML 代码的某个部分,以便根据方向进行显示/编辑。如果标准需要很长时间才能拥有这些标记(如果真的会有的话!),一些编辑程序可能会使用它们自己的内部标记。
最后,我想说我发现这个主题很重要,并想赞扬 Moriel 做了非常细致和高质量的准备工作。这无疑强调了主题的重要性。我希望有人能在 Linux 中解决这个问题。
最后,一些观察
1. 表情符号/表情包将不得不重新考虑... D: 真的和 :D 不一样
2. 这只是问题的开始。如果是 RTL(从右到左)和向下(垂直)呢,比如传统的日语?
3. 如果你真的混用英语和希伯来语写作,我猜在某个时候,如果出现疑问,某种人工智能会问“这是英语还是希伯来语?” 它很可能会在文本中为你插入 RTL 或 LTR 标签。就这样。

知识共享许可协议本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.