作为一名为全球客户工作的前端开发人员,我一直难以处理多语言网站,尤其是在同时使用从右到左 (RTL) 和从左到右 (LTR) 的情况下。也就是说,我在此过程中学到了一些东西,并将在这篇文章中分享一些技巧。
让我们使用阿拉伯语和英语,不仅因为阿拉伯语是我的母语,还因为它是在使用中的 RTL 的一个经典示例。
向网站添加 RTL 支持
但是在此之前,我们需要在我们的网站上添加对 RTL 语言的支持。我们可以通过两种方式来实现这一点,这两种方式都不是理想的。
不理想:为每个方向使用特定的字体
我们可以采取的第一种方法是依赖于任何给定元素上的方向 (dir) 属性(通常是 <html> 元素,因此它全局设置方向)
/* For LTR, use Roboto */
[dir=ltr] body {
font-family: "Roboto", sans-serif;
}
/* For RTL, use Amiri */
[dir=rtl] body {
font-family: "Amiri", sans-serif;
}
PostCSS-RTL 使为每个方向生成样式变得更加容易,但是这种方法的问题在于您只使用了一种字体,如果您在一个段落中有多种语言,则这不是理想的。
原因如下。您会发现多语言段落会弄乱 UI,因为阿拉伯语字形被赋予了与元素不匹配的默认字体。
在某些浏览器中,情况可能会更糟。
也不理想:使用支持两种语言的单个字体
第二个选项可能是使用 提供对两种方向支持的字体。但是,根据我的个人经验,仅对两种语言使用一种字体会限制创造力和使用每种方向的不同字体的自由。根据设计要求,它可能不会那么糟糕。但我确实参与过一些项目,在这些项目中,它会产生影响。
好的,那接下来呢?
我们需要一个更简单的解决方案。根据 MDN
字体选择不仅仅停留在用户系统上列表中的第一个字体。相反,字体选择是逐个字符进行的,因此,如果可用字体没有所需字符的字形,则会尝试后面的字体。
这意味着,我们仍然可以使用 font-family 属性,但在第一个字体没有字符的字形的情况下使用回退。这实际上解决了上述两个问题,一石二鸟!
body {
font-family: 'Roboto', 'Amiri', 'Galada', sans-serif;
}
为什么这有效?
就像 flexbox 和 CSS 网格使 CSS 布局更加灵活一样,字体匹配算法使处理不同语言的内容变得更加容易。以下是 W3C 关于如何将字符与字体匹配 的说法
当文本包含组合标记等字符时,理想情况下,基本字符应使用与标记相同的字体呈现,这可以确保标记的正确放置。出于这个原因,集群的字体匹配算法比单独匹配单个字符的一般情况更专业。**对于包含变体选择器的序列(指示要用于给定字符的确切字形),用户代理始终尝试系统字体回退以找到合适的字形,然后再使用基本字符的默认字形。**
(我强调的部分)
字体是如何匹配的?规范概述了算法执行的步骤,我将在这里进行概括。
- 浏览器查看文本集群并尝试将其与 CSS 中声明的字体列表进行匹配。
- 如果它找到一个支持所有字符的字体,那就太好了!这就是使用的字体。
- 如果浏览器找不到支持所有字符的字体,它会重新读取字体列表以找到一个支持未匹配字符的字体,并将其应用于这些特定字符。
- 如果浏览器在列表中找不到既不匹配集群中的所有字符也不匹配单个字符的字体,那么它就会使用 默认系统字体 并检查它是否支持所有字符。
- 如果默认系统字体匹配,那就太好了!这就是使用的字体。
- 如果系统字体不起作用,那就是浏览器呈现损坏的字形的地方。
让我们谈谈性能
我们刚刚看到的序列可能会对网站的性能造成影响。想象一下浏览器必须遍历每个定义的回退、将特定字符与字形匹配以及根据它找到的内容下载字体文件。这可能会增加很多工作量,更不用说 FOUT 和其他渲染怪异现象了。
目标是让 字体匹配算法 决定要应用于每个文本的字体,而不是依赖于两种语言的一种字体或添加额外的 CSS 来处理不同的方向。如果永远不会将字体应用于任何内容(例如,特定页面为 RTL 并且碰巧没有任何 LTR 文本,反之亦然),则不会下载堆栈中未使用的字体。
要实现这一点,需要选择合适的多种语言字体。合适的多种语言字体是指在页面上预期使用的尽可能多的字符都有字形的字体。如果您无法找到支持所有字符的字体,则使用支持大多数字符的字体,然后回退到另一字体也是一种有效的方法。如果碰巧是默认系统字体,那也同样好,因为它减少了一个要下载的字体文件。
让 font-family 属性决定每个字形的字体(而不是为每个方向创建额外的 CSS 选择器)的好处在于,该行为已经存在,正如我们前面概述的那样——我们只需要利用它即可。
不错的文章,伙计,
作为英语和阿拉伯语使用者,有时我必须处理在同一行中合并这两种语言的情况,这在大多数聊天和消息应用程序中都会中断。
这个概念将解决这个问题。
我将尝试在 React Native 上测试它,看看如何实现它。
我没有检查其他浏览器,但在 Chrome 版本 80(在 Windows 上运行)上,似乎在您的第一个 RTL 示例中(其中英语字形应该回退到 sans-serif),英语文本实际上是用衬线字体呈现的!
没错,Tom。我确实查了一下,发现这是因为我使用了来自 Google Fonts 的 Amiri,它默认支持拉丁字形,因此如果您使用的是 Google Fonts,则必须将拉丁字体作为第一个选项以避免此类冲突。
好文章,清晰、简洁易懂。谢谢。
这听起来可能像个愚蠢的评论,但在一个代码块中混合使用多种语言时,难道不应该使用相应的语言标记不同语言的片段,以实现语义标记吗?(所以在你的阿拉伯语段落中,将“exercitation”标记为exercitation)
你是指引号吗?
如果是这样,我同意你的观点,我认为这需要手动处理。
从标题来看,我以为这篇文章会包含关于带有特殊字符(如拉丁字符)的字体的考虑。谷歌字体中的许多字体不包含这些ÑÇÃÁÀÉÍÓÕÚ字符,并且网站经常使用这些字体。如果有人输入带重音的字符,使用它们的输入字段和动态文本将显示奇怪的字符。
浏览器从2008年开始就支持这个功能。我的朋友在我们运营一个非英语科技博客时告诉我这件事,这个博客中缅甸语段落中出现了英语的计算机术语。我对缅甸语字体中的拉丁字符不满意,当时我的朋友建议使用有序字体列表。