构建 RTL 意识的 Web 应用程序和网站:第 1 部分

让更多人以更多语言访问网络是一个持续的努力,也是我们在 Mozilla 非常重视的一项任务。

这篇文章是系列文章中的第一篇,旨在解释 Web 开发中最被忽视和最鲜为人知的角落之一:RTL(从右到左)开发。在 Web 开发环境中,这意味着使您的 Web 内容与阿拉伯语、希伯来语、波斯语和乌尔都语等 RTL 语言兼容,这些语言都是从右到左书写的。

由于缺乏教学资源,开发人员经常忽视这个领域。在本系列文章中,我们希望提供足够的信息,使开发人员能够创建可供更广泛的全球受众访问的内容,从而利用大多数 Web 浏览器当前提供的 RTL 功能。

什么是 RTL?

为了使事情更清楚,语言和脚本之间存在差异。脚本是指文本或书面形式——而语言是指口语形式。因此,从技术上讲,从右到左描述的是语言书写的脚本,与英语或法语等从左到右的脚本类似,许多西方读者对其更为熟悉。

例如,“Marhaban”——意思是你好——是一个用英语脚本书写的单词,但其语言是阿拉伯语,而“مرحبا”则是阿拉伯语脚本和阿拉伯语。

如前所述,在 Web 上,术语从右到左不仅表示书写脚本。它代表着开发 Web 应用程序和网站的所有方面,这些应用程序和网站在使用阿拉伯语、乌尔都语或任何其他 RTL 脚本时不会中断或变得不可读。

在继续之前,让我们澄清一下关于 RTL 的一些误解。

首先,RTL 不仅仅是将文本翻译成阿拉伯语:它意味着不仅仅将您的网站翻译成阿拉伯语,然后就完事了。它涉及使 UI 和布局的各个方面都与 RTL 兼容。正如我总是说的一样——并且再强调也不为过——要么正确地做 RTL,要么不要做!半途而废只会让您的受众和信誉受损。

其次,RTL 不仅仅是“翻转所有东西”:我不确定这个问题是否已修复,但在 Gnome 中将您的区域设置为阿拉伯语会导致时间显示为下午 33:12 而不是下午 12:33。

好吧,事实并非如此。我是一个阿拉伯语母语人士,但这并不意味着我会倒着报时。在数字方面有一些例外情况和需要注意的地方,我们将在本系列文章中介绍。

我为什么要关心 RTL?

您可能认为 RTL 很难、很吓人,并且会给您的项目带来大量工作!实际上,它并不那么难。一旦您理解了基本知识,所需的额外工作量并不多。

您应该非常重视为您的 Web 应用程序或网站添加从右到左的支持,原因有很多。截至 2010 年,全球有超过 4.1 亿 RTL 母语人士(这是一个庞大的数字!注意:该评估基于维基百科的语言列表。)它也是数百万潜在用户以及您的应用程序或网站的巨大潜在市场。大多数公司现在都在其软件(例如 Windows、iOS、Android)和网站中添加了 RTL 支持,因此 RTL 母语人士期望您的网站满足其对 RTL 支持的预期。如果没有这种支持,访问您 Web 应用程序的访问者可能不会成为回头客。

就 Firefox OS 而言,我们只在我们完全实施了系统的 RTL 支持后才开始向中东地区发货设备,因此我们的合作伙伴 Orange 能够销售装有该操作系统的设备。这显然为 Firefox OS 开辟了一个全新的用户市场。

RTL 最佳实践

以下是一些在开发 RTL 意识的网站和 Web 应用程序时需要注意的一般经验法则

  • 从右边思考:在使用 RTL 脚本和语言的区域,书籍从右侧打开。因此,与可读性相关的多数 UI 元素都将被镜像。
  • 硬件遗留 UI 也是一个问题:在中东和北非 (MENA) 以及其他使用 RTL 语言的地区,人们也使用小工具。音频硬件的控制布局与世界其他任何地方都相同。因此,播放、快进、下一首、上一首等按钮始终相同。正因为如此,镜像任何这些按钮不是一个好主意。请不要将媒体播放器按钮设置成 RTL。硬件遗留的另一个例子是实体功能手机键盘;在黑莓等手持设备键盘出现之前,有简单的实体数字键盘——直到最近,它在 MENA 国家非常流行。因此,强烈建议不要镜像数字键。Firefox OS 就是一个很好的例子。由于其最初的目标是发展中国家,我们确保在 RTL 中没有将拨号器镜像。
  • Firefox OS 联系人应用程序从右到左思考并不意味着左撇子优先:请记住,不要将开发 RTL UI 与左撇子优先 UI 混淆。在世界上的 RTL 地区,右撇子人占多数,就像世界其他地方一样。因此,与用户在页面上阅读实际内容的方式无关的任何内容都不应该被镜像。例如,Firefox OS A-Z 滚动条(右侧图像)在联系人应用程序中。它位于右侧,因为用右手滚动更容易,因此当您的页面处于 RTL 模式时,这样的东西不应该放在另一侧。

言归正传:如何设置 RTL 内容

使 Web 页面成为 RTL 的第一步是将代码 dir="rtl" 添加到 <html> 标签中

<html dir="rtl">

dir 代表方向,将其值设置为 rtl 会将元素的水平起始点默认为右侧而不是左侧。

仅当页面为 RTL 时,才使用 CSS 针对元素

  1. 创建一个针对它的任何常规 CSS 规则的副本。
  2. html[dir="rtl"] 属性选择器添加到选择器链的前面。
  3. 接下来,无论何时在 RTL 规则中找到表示水平定位的属性或值,请使用其相反的值。例如,如果您找到 float: left,则应将其更改为 float: right

例如,如果原始规则如下——

.someClass {
    text-align: left;
    padding: 0 10px 0 0;
    text-decoration: underline;
}

我们将在原始规则之后粘贴它的副本,并将其更新为如下

html[dir="rtl"] .someClass {
    text-align: right;
    padding: 0 0 0 10px;
}

请注意,text-decoration: underline; 声明已被删除。我们不需要为 RTL 版本覆盖它,因为它不影响任何基于方向的布局元素。因此,原始规则中的值仍然会应用。

以下是一个更详细的示例,您可以直接进行操作

查看 CodePen 上 Ahmed Nefzaoui (@anefzaoui) 编写的 Pen dYGKQZ

当然,现实情况不会像这样简单。有些应用程序包含大量的 CSS 规则和选择器,您可以采用多种策略。以下是一种推荐的方法

  1. 复制并清理:首先,将样式表的 内容复制到另一个文件,将 html[dir="rtl"] 添加到所有规则中,然后清除与水平方向设置无关的任何属性。您应该最终得到一个更轻量的文件,专门处理 RTL。
  2. 镜像样式:将新文件中剩余的所有属性更改为它们的相反值:例如 padding-left 变成 padding-rightfloat: right 变成 float: left,等等。注意:如果您最初有 padding-left 并将其更改为 padding-right,请记住原始的 padding-left 仍然存在于原始规则中。您必须padding-right 旁边添加 padding-left: unset,否则浏览器将计算这两个属性:原始 CSS 规则中的 padding-left 和 RTL 规则中的新 padding-right。对于具有多个方向变体的任何属性(如 margin-left|rightborder-left|right ...)也是如此。
  3. 粘贴回去:完成上述步骤后,将新创建的规则粘贴回原始文件,放在原始规则之后。我个人喜欢在之后添加一些注释,例如:/* **** RTL Content **** */,以便更容易区分样式文件中的两部分。

更好的方法:完全隔离左右样式

有时您会发现自己面临着会导致代码冲突的边缘情况。这涉及属性从其他属性继承值,这意味着有时您不确定要覆盖哪些属性,以及哪些属性不要覆盖。也许您已将 margin-right 添加到 RTL 规则中,但您不确定将原始 margin-left 设置为哪个值?在所有实际情况下,建议采用另一种方法,这种方法总体上更好,但更冗长。对于这种方法,我们完全将基于方向的属性与 LTR 和 RTL 情况的规则隔离开来。以下是一个示例:假设我们有 .wrapper .boxContainer .fancyBox 这样列出

.wrapper .boxContainer .fancyBox {
    text-align: left;
    padding-left: 10px;
    text-decoration: underline;
    color: #4A8CF7;
}

与其为 RTL 添加另一个具有 padding-leftpadding-right 的属性,您可以这样做

.wrapper .boxContainer .fancyBox {
    text-decoration: underline;
    color: #4A8CF7;
}

html[dir="ltr"] .wrapper .boxContainer .fancyBox {
    text-align: left;
    padding-left: 10px;
}

html[dir="rtl"] .wrapper .boxContainer .fancyBox {
    text-align: right;
    padding-right: 10px;
}

此解决方案由 3 部分组成

  1. 原始规则/选择器,包含非基于方向的属性,因为它们由 LTR 和 RTL 布局共享。
  2. 从左到右的情况——html[dir="ltr"]——包含基于方向的 CSS 属性,它们的值设置为与您的 LTR 布局相匹配。
  3. 从右到左的情况——html[dir="rtl"]——与 LTR 情况具有相同的属性,只是它们的值根据您在 RTL 布局中想要的内容设置。

请注意,在第二个规则中,我们只有padding-left,在第三个规则中,我们只有padding-right。这是因为它们中的每一个都是专门针对在dir属性中给定的方向。这是一种简洁明了的方法,不需要取消设置属性。这正是我们在为 Firefox OS 添加 RTL 支持时使用的方法。(注意:unset 关键字 并非所有浏览器都支持。为了获得最佳兼容性,您可能需要显式地重新声明要取消设置的任何属性的所需值。)

我们如何使用 JavaScript 获取和设置页面的方向?

这相当容易,只需要一行代码:document.documentElement.dir
您可以将此行分配给一个变量,并将其输出到控制台以供您自己查看: 

var direction = document.documentElement.dir; console.log(direction);

或者试试这个例子

查看代码笔 WQxWQQ by Ahmed Nefzaoui (@anefzaoui) on CodePen.

要设置页面的方向,只需使用以下命令
document.documentElement.dir = "rtl"

以下是一个同时获取和设置网页方向值的示例

查看代码笔 gaMyPN by Ahmed Nefzaoui (@anefzaoui) on CodePen.

自动 RTL 解决方案

关于手动 RTL 方法的讨论就到此为止。现在让我们看看一些自动化方法。

css-flip

Twitter 开发了一种解决方案来自动化整个过程,使大型项目更容易实现 RTL。他们将其开源,您可以在 GitHub 上 找到它。

这个 NPM 插件涵盖了您在处理 RTL 时可能遇到的几乎所有情况,包括边缘情况。它的一些功能包括

  • no-flip:要指示镜像引擎不要翻转某个属性,只需在属性开头添加/* @noflip*/。例如,如果您写/* @noflip*/ float: left,它会在运行 css-flip 后保持float: left
  • @replace:当您拥有在 LTR 和 RTL 之间不同的背景图片时,可以通过在原始属性之前包含/*@replace: url(my/image/path) 来指定替换图片。假设您有background-image: url(arrow-left.png)。如果您将该行更新为
    /*@replace: url(arrow-rightish.png) */ background-image: url(arrow-left.png);
    最终您将在 RTL 布局中获得background-image: url(arrow-rightish.png);

您可以通过命令行使用 css-flip,方法是执行
css-flip path/to/file.css > path/to/file.rtl.css,或者在您的页面中引用 css-flip。有关更多信息,请访问他们的 GitHub 存储库。css-flip 正在 Twitter 属性中积极使用。

rtlcss

另一个自动转换选项是 Mohammad Younes 的工具 rtlcss - 它也可以在 GitHub 上 获取,并提供一些非常方便的功能。使用此引擎,您可以

  • 重命名规则名称(例如,将#boxLeft 重命名为#boxRightSide)。
  • 完全忽略镜像过程中的规则。
  • 忽略单个属性。
  • 追加/前置新值。
  • 在其他属性值之间插入新的属性值。

基本用法是通过命令行 - 您可以在原始 CSS 文件所在的目录中运行以下命令,以创建 RTL CSS 等效文件

rtlcss input.css output.rtl.css

当然,您也可以在您的页面中引用它。有关更多详细信息,请访问 GitHub

这个项目非常受欢迎,并被包括 WordPress 在内的多个项目使用。

最后的话

虽然还有很多内容需要涵盖,但这篇文章旨在为您提供足够的知识,让您充满信心地开始探索 RTL。在下一篇文章中,我们将介绍更多关于 RTL 的高级主题。

请在评论区中提出您关于 RTL 的任何问题,我们会在这里或在下一篇文章中尝试解答!

关于 Ahmed Nefzaoui

RTL(从右到左)专家。前端网页开发者。Firefox OS UI 黑客和 Mozilla 技术演讲者。

Ahmed Nefzaoui 的更多文章…


10 条评论

  1. Pavel Ivanov

    很棒!:)

    你的 \o/ ;)

    2015 年 9 月 24 日 下午 1:37

  2. Gabriel Mičko

    对 RTL 入门者非常有用。

    2015 年 9 月 25 日 上午 0:37

  3. Steve

    很高兴看到这个主题被很好地涵盖了!感谢您的见解

    一个问题 - 最近有人告诉我阿拉伯语实际上是双向的,因为数字可以用西式 LTR 风格表示。这会有什么影响?

    2015 年 9 月 25 日 上午 2:05

    1. Ahmed Nefzaoui

      基本上,这更多是区域差异,北非使用我们称之为西阿拉伯数字 (123),而中东使用东阿拉伯数字 (١‎٢‎٣‎)。它不完全是双向,而是 Unicode 双向算法 (http://unicode.org/reports/tr9/) 定义的浏览器引擎行为,它强制数字 (0123456789) “粘贴”到它前面的强方向运行,这导致西阿拉伯数字永远不会出现在 RTL 基于短语的右侧,因此看起来像是双向的(从左侧开始)。但幸运的是,这个问题将在未来的 BIDI HTML & CSS W3C 规范中得到解决。
      对当前行为的影响是导致了许多不干净的解决方法,以使其看起来正确。例如,像 +123456 这样的国际电话号码在 RTL 布局中看起来像 123456+(这是不正确的)。

      2015 年 9 月 25 日 上午 11:21

      1. Steve Lee

        太迷人了。

        我非常有限的理解是,数字一百二十三显示为 123,而不是 321,在我看来是 LTR,因此使其成为 BIDI。但也许数字没有像文字一样受到相同的方向区分?

        2015 年 9 月 25 日 上午 11:34

        1. Ahmed Nefzaoui

          实际上,这两个假设都是正确的。数字 - 无论是东方还是西方 - 都是从左到右书写的。而且它们也没有像字母那样受到相同方向行为的影响(这就是为什么 +123 在 RTL 中变为 123+,并且没有保留其原始位置)。

          2015 年 9 月 25 日 上午 11:55

  4. Patrick Brosset

    我不知道这个系列的下一部分是否会深入探讨针对此的开发者工具,但我认为这是一个好主意。例如,是否有可以帮助测试 RTL 的附加组件?

    2015 年 9 月 25 日 上午 7:00

    1. Ahmed Nefzaoui

      未来的文章将以某种方式深入探讨使用开发者工具进行 RTL 操作。
      我现在知道的唯一与双向 Web 开发相关的附加组件是那些强制页面采用 RTL 布局的附加组件,以帮助开发人员在从右到左的环境中测试他们的网页。例如:强制 RTL https://addons.mozilla.org/en-us/firefox/addon/force-rtl/
      将来,我希望帮助将开发者工具改造成更适合 RTL 的开发和测试工具。

      2015 年 9 月 25 日 上午 11:27

  5. SteveLee

    实际上还有另一个方面 - 光标在文字中的移动

    当 ICAN 引入阿拉伯语域名时,我玩了一些,并有趣地注意到光标在 Awesome Bar 中的 URL 中移动时是如何反应的。如果 URL 使用混合文字,光标会以令人不安的方式左右移动,但这确实很合乎逻辑。

    通常您不会在网页上浏览显示的文字,但我知道一些浏览器允许您这样做 - 不记得 FFx 是否支持。显而易见的使用案例是屏幕阅读器,它们支持文字的光标浏览。考虑到许多用户有视觉障碍,同样的奇怪的光标运动也会发生,尽管文字应该依次朗读。

    真有趣!:)

    2015 年 9 月 25 日 上午 11:40

  6. Francis Kim

    我的大脑中增加了新的知识!谢谢 :)

    2015 年 9 月 27 日 上午 1:46

本文的评论已关闭。