Obeta

css3中的unicode-bidi与direction使用

平时很少接触过需要说改变文字的顺序需求,但是不排除不会用到,如果需要做国际化支持,文本中包含了 <strong>左->右</strong> 与 <strong>右->左</strong> 的语言,这时候浏览器的默认bidi算法无法满足我们的需求,于是就需要使用复杂的Unicode算法来决定如何显示文本.

古时候文人写毛笔字也是从上至下,从右往左的顺序写的.至于为什么会这样,大多数人的解释就是古人写字要悬肘举腕,刚写出来的字墨水并没有干,因此自上而下,从右往左不会触碰到墨水,影响笔迹.暂且就这么认为吧,毕竟很难考究了.

目前国际上有部分语言是右->左,比如希伯莱语,阿拉伯语,伊朗波斯语都是从右到左的,如果左->右的文字中包含了右->左的文字,则需要我们开发人员通过bidi算法来控制文本的显示方式.

Unicode

关于 Unicode 的内容可以移步到Unicode 控制字符阅读,注意这部分很重要,否则你可能无法理解下面相关知识点.

相关的 css3 属性

directionunicode-bidi是两个唯一不受所有速记属性影响的css3属性.因此如果你需要覆盖这两个属性的,你需要给他们两个单独设置值.也不受all属性的影响(all属性可以重置当前元素所有的属性值).

速记属性是 CSS 属性的一种,平时我们也有使用,比如margin,background等等,允许我们同时设置多个其他 CSS 属性的值(类似margin-bottom,background-color).使用速记属性,您可以编写更简洁(通常更易读)的样式表,节省时间和精力.

direction

用来设置块级元素的文本,表列和水平溢出的方向,也会同时设置块级元素文本的默认对齐方向(类似于text-align).通常使用它来设置全局方向,如果全局方向是右->左,那么第一个文字显示的位置就在显示区域的最右侧.语法如下:

/* Keyword values */
direction: ltr;
direction: rtl;

/* Global values */
direction: inherit;
direction: initial;
direction: unset;

ltr也就是left to right, 同理rtlright to left.

direction类似的 html 属性还有一个是dir,大多数情况下建议使用这个属性,因为某些情况下用户可能会屏蔽 css 样式.具体表现可以看下面的例子:

<p style="direction: ltr;">welcome to obeta blog.,"'[]-=+</p>
<p style="direction: rtl;">welcome to obeta blog.,"'[]-=+</p>
<p style="direction: ltr;">
	1 2 3 4 5 6 7 8 9 0 12345 6789 this is very strange.,"'[]-=+
</p>
<p style="direction: rtl;">
	1 2 3 4 5 6 7 8 9 0 12345 6789 this is very strange.,"'[]-=+
</p>
<p dir="rtl">welcome to obeta blog.,"'[]-=+</p>
<hr />
<span style="direction: ltr;">welcome to obeta blog.,"'[]-=+</span><br />
<span style="direction: rtl;">welcome to obeta blog.,"'[]-=+</span>
<span style="direction: rtl;display: block;"
	>welcome to obeta blog.,"'[]-=+</span
>

也就是下面这样,可以发现direction对内联元素无效的.directiontext-align区别就是后者只关注对齐方向,并没有改变文字的书写顺序.

welcome to obeta blog.,"'[]-=+

welcome to obeta blog.,"'[]-=+

1 2 3 4 5 6 7 8 9 0 12345 6789 this is very strange.,"'[]-=+

1 2 3 4 5 6 7 8 9 0 12345 6789 this is very strange.,"'[]-=+

welcome to obeta blog.,"'[]-=+


welcome to obeta blog.,"'[]-=+
welcome to obeta blog.,"'[]-=+ welcome to obeta blog.,"'[]-=+

上面的direction在数字中是我们符合预期的,但是书写英文的时候并没有改变书写顺序,这是为什么呢?

这是因为英文字母是强字符类,因此简单的通过设置全局方向并不会改变它的显示方向,而数字与数字相关的符号(弱类型)和大部分标点符号和空格(中性类型),因此全局方向是可以影响这类字符的.也就是说一般情况下如果你不设置额外的其他属性,那么弱类型字符会被文中强类型的字符改变方向.

而对于需要重写强字符的方向,我们现在还是要看下一个unicode-bidi属性.

HTML5 中给 dir 属性加入了一个新的值auto.这个值与上面提到的ltrrtl不同,它并不明确相应元素内的全局方向,而需要根据不同的情况进行判断.这个判断依据就是该元素内的第一个强字符.如果第一个强字符是从左到右的属性,那么dir="auto"的结果就等于dir="ltr".同理,如果第一个强字符是从右到左的属性,那么dir="auto"的结果就等于dir="rtl".需要注意的是,这里的判断条件的第一个强字符,而会忽略之前遇到的弱字符和中性字符.这种模式可以被称为自动方向性或者上下文方向性.

unicode-bidi

应该很多人都没有见过这个属性,通常情况下,它与direction是一个同胞兄弟,两个会经常一起出现.

bidi实际是英文单词bidirectional双向的缩写,也可以理解为双向文字,同一段文字中包含了两种不同方法的文字.对于 web 前端工程师来说,理解这些非常有必要,不同的方向可能会导致理解上的不一致.

在现代计算机应用中,最常用来处理双向文字的算法是Unicode双向算法(Unicode Bidirectional Algorithm),这也就是为什么会有这个unicode-bidi属性,这个属性是用来重写这个算法的. Unicode双向算法也可以简称为bidi算法.

The unicode-bidi CSS property, together with the direction property, determines how bidirectional text in a document is handled. For example, if a block of content contains both left-to-right and right-to-left text, the user-agent uses a complex Unicode algorithm to decide how to display the text. The unicode-bidi property overrides this algorithm and allows the developer to control the text embedding. ----unicode-bidi

google 翻译:unicode-bidi 与 direction 属性一起确定如何处理文档中的双向文本.例如,如果内容块包含从左到右和从右到左的文本,则用户可以使用复杂的 Unicode 算法来决定如何显示文本. unicode-bidi 属性会覆盖此算法,并允许开发人员控制文本嵌入.

/* Keyword values */
unicode-bidi: normal;
unicode-bidi: embed;
unicode-bidi: isolate;
unicode-bidi: bidi-override;
unicode-bidi: isolate-override;
unicode-bidi: plaintext;

/* Global values */
unicode-bidi: inherit;
unicode-bidi: initial;
unicode-bidi: unset;

direction改变的是全局方向,而unicode-bidi根据全局方向来改变文字的排列方向.下面这些值都会读取当前direction或者继承来的direction值.

  • normal: 默认浏览器行为,会根据lang,font-family以及判断当行文字中 Unicode 字符中的方向来决定书写方向.
  • embed: 对內联元素进行重新排列(根据自己的direction属性值或继承值).此属性值无法修改强字符的默认方向.
  • bidi-override: 对于内联元素此属性值将直接根据direction方向值来创建覆盖,不管是什么类型的字符.对于块容器元素,根据direction属性该值将为不在另一个块容器元素内的内联级别的后代创建一个覆盖,严格按direction重新排序.双向算法隐式部分被忽略.
  • isolate: 这个关键字表示计算元素容器的方向时,不考虑这个元素的内容.因此,这个元素就从它的兄弟姐妹中分离出来了.当应用它的向分辨算法的时候,它的容器元素将其视为一个或多个U+FFFC Object Replacement Character,即像image一样.
  • isolate-override: 这个关键字将isolate关键字的隔离行为应用于周围的内容,并将bidi-override关键字的覆盖行为应用内部内容.
  • plaintext: 这个关键字在计算元素方向的时候,不考虑父元素的双向状态,也不考虑direction属性的值.它是使用Unicode双向算法的 P2 和 P3 规则计算的. 这个值允许按照Unicode双向算法显示已经格式化的数据.

embed,bidi-override看起来是不是似曾相识,是的,这与Unicode 控制字符及其有关的双向算法中所讲到的控制字符名字非常相似.可以看一下下面的几个例子,加深一下对他们对认识.

  • 例子 1: 使用embed,isolate打开额外指定的方向对中性字符有效,对强字符无效
<p style="direction: ltr;">
	welcome to
	<em style="unicode-bidi: embed; direction: rtl;">1 2 3 4 obeta</em> blog.
</p>
<p style="direction: ltr;">
	welcome to
	<em style="unicode-bidi: isolate; direction: rtl;">1 2 3 4 obeta</em> blog.
</p>

welcome to 1 2 3 4 obeta blog.

welcome to 1 2 3 4 obeta blog.

  • 例子 2: 使用bidi-override,isolate-override才可强制修改各种字符的方向
<div style="direction: rtl;unicode-bidi: bidi-override;">
	welcome to <em>obeta 1234 1 2 3 4</em> blog.
</div>
<p style="direction: rtl;unicode-bidi: isolate-override;">
	welcome to <em>obeta 1234 1 2 3 4</em> blog.
</p>
welcome to obeta 1234 1 2 3 4 blog.

welcome to obeta 1234 1 2 3 4 blog.

  • 例子 3: bidi-override,isolate-override无法修改在自己子后代中的块元素以及块元素中的内联元素
<div style="direction: rtl;unicode-bidi: bidi-override;">
	welcome to <em>obeta 1 2 3 4</em> blog.
	<p>obeta 1 2 3 4</p>
	<p><em>obeta 1 2 3 4</em></p>
</div>
<div style="direction: rtl;unicode-bidi: isolate-override;">
	welcome to <em>obeta 1 2 3 4</em> blog.
	<p>obeta 1 2 3 4</p>
	<p><em>obeta 1 2 3 4</em></p>
</div>
welcome to obeta 1 2 3 4 blog.

obeta 1 2 3 4

obeta 1 2 3 4

welcome to obeta 1 2 3 4 blog.

obeta 1 2 3 4

obeta 1 2 3 4

  • 例子 4: embed,与isolate的主要区别在于是否会影响旁边的中性字符.
<p style="direction: ltr;">
	welcome to <em style="unicode-bidi: embed;">ولدت </em>12 -+ 234 blog.
</p>
<!-- 可以看到使用isolate后强字符无法影响旁边的中性字符,这就是isolate的主要作用 -->
<p style="direction: ltr;">
	welcome to <em style="unicode-bidi: isolate;">ولدت </em>12 -+ 234 blog.
</p>

welcome to ولدت 12 -+ 234 blog.

welcome to ولدت 12 -+ 234 blog.

isolate,isolate-override,plaintext都是 css3 新增属性.isolate也可以使用相应的<bdi>标签来控制文本,还有相当于bidi-override<bdo>标签,这类标签都使用dir属性控制元素內的字符方向性.需要注意的是<bdo>标签中的dir不能设置auto,而对于使用<bdi>标签显示用户输入时候建议使用dir="auto"让浏览器根据该区域上下分自动判断使用的方向.

目前 chorme 与 firefox 已经实现上述的值,但是其余的浏览器都不支持,比如 edge 与 safari 不支持(safari 支持部分).这些属性的详细的官方文档在这里关于 unicode-bidi 的 css 草案.

一般来说浏览器会根据文字的Unicode码来自动判断文字的方向性,如果是阿拉伯语,希伯莱语这类文字,只要设置dircetion浏览器会自动修改文本的为正确的阅读格式.

总结

调查了一些阿拉伯的语言的网站,对于他们来说只需要设置两个简单的属性direction,dir即可,也不需要做其他的事情,毕竟是强字符,不需要重写unicode-bidi属性.

<html dir="rtl">
	<head>
		<style>
			body {
				direction: rtl;
			}
		</style>
	</head>
	<body>
		<p style="text-align: initial;">
			وأفادت تحقيقات النيابة العامة بأن المرأة التي تبلغ من العمر 31 عاماً،
			تقدمت إلى مركز شرطة في دبي برفقة طفلين، مشيرة إلى أنهما لا يملكان أوراقاً
			رسمية وتريد المساعدة في تسجيلهما، لافتة إلى أن الطفلة الأولى ثلاث سنوات
			ولدت في أحد المستشفيات، فيما ولد شقيقها الأصغر 22 شهراً في منطقة النهدة
			بمساعدة امراة أخرى.
		</p>
	</body>
</html>

属性direction,dir可以二选一,但是为了在有些用户禁用样式的情况下也能正常阅读,因此dir是必选的.设置text-align: initial;是为了防止文本方向被覆盖.

وأفادت تحقيقات النيابة العامة بأن المرأة التي تبلغ من العمر 31 عاماً، تقدمت إلى مركز شرطة في دبي برفقة طفلين، مشيرة إلى أنهما لا يملكان أوراقاً رسمية وتريد المساعدة في تسجيلهما، لافتة إلى أن الطفلة الأولى ثلاث سنوات ولدت في أحد المستشفيات، فيما ولد شقيقها الأصغر 22 شهراً في منطقة النهدة بمساعدة امراة أخرى.

上面的阿拉伯语根据谷歌翻译为: 检察官说,这名 31 岁的女子带着两个孩子来到迪拜的一个警察局,说他们没有官方文件,想帮助他们登记.第一个孩子三年前在一家医院出生,在另一位女士的帮助下,在 Al Nahda 地区待了 22 个月.

很多人都以为阿拉伯数字是阿拉伯发明的,其实是由印度人发明的,然后被阿拉伯地区引进并且广泛传播到西方世界,因此才被叫做阿拉伯数字,真正阿拉伯地区使用的数字是叫做阿拉伯文数字,那边使用两套数字.而且很有意思的是:同阿拉伯文不一样,当代的阿拉伯文数字一般从左向右排列.而在一些古典作品里是从右向左排列的.

阿拉伯电话
阿拉伯电话

数值中东阿拉伯文数字标准阿拉伯文数字东阿拉伯文数字
其它国家阿拉伯国家伊朗、阿富汗、巴基斯坦、和部分印度
0٠‎۰‎
1١‎۱‎
2*٢‎۲‎
3٣‎۳‎
4٤‎۴‎
5٥‎۵‎
6٦‎۶‎
7٧‎۷‎
8٨‎۸‎
9٩‎۹‎

*在埃及,"2"通常用另一种写法.

发现一个很有意思的事情,你可以尝试使用鼠标选中上面的阿拉伯语,你会发现跟我们之前的选中方式不太一样.这是因为阿拉伯语是自右向左的语言,而且可以从翻译可以基本看出阿拉伯语是自右向左,自上而下的顺序.

如果你选中这行中文文字,可能还更容易一些.正确选取自左向右的文字的规律就是需要从右上->左下选取(或者从左下->右上),如果你从顶部最左开始向底部最右选取是很难选中最顶部的一行,因此从右上->左下选取是这类语言正常的做法.

引用

个人随笔记录,内容不保证完全正确,若需要转载,请注明作者和出处.