bidi(双向文字)与RTL布局总结
BIDI
双向文字就是一个字符串中包含了两种文字,既包含从左到右的文字又包含从右到左的文字。
大多数文字都是从左到右的书写习惯,比如拉丁文字(英文字母)和汉字,少数文字是从右到左的书写方式比如阿拉伯文(ar)跟希伯来文(he)。对于需要国际化支持的web应用来说,由于阅读习惯的不同在web页面排版和布局中会给开发人员带来麻烦。这种情况多数出现在从右到左的文字中,比如字符串中出现阿拉伯文、英文字母、数字以及标点符号。本文就是我在工作中遇到该类问题的总结。
在现代计算机应用中,最常用来处理双向文字的算法是 Unicode 双向算法(Unicode Bidirectional Algorithm),在后面的文章中我们将 Unicode 双向算法简称为 bidi 算法。
我们的web产品中使用的字符都属于unicode字符,而unicode字符的方向属性总共包含三类:强字符、中性字符、弱字符。
强字符的方向属性是确定的,与上下文的bidi属性无关,而且强字符在bidi算法中可能会对其前后的中性字符产生影响。大部分的字符都属于强字符,比如拉丁字符、汉字、阿拉伯字符。
中性字符的方向性并不确定,受其上下文的bidi属性影响(前后的强字符)。比如大部分的标点符号(“-”,“[]”,"()"等)跟空格。
弱字符的方向性是确定的,但不会对其上下文的bidi属性产生影响。比如数字以及跟数字相关的符号。
一个区域内有总体方向,决定从这个区域的哪边开始书写文字,通常称为基础方向。浏览器会根据你的默认语言来设置默认的基础方向,如英语、汉语的基础方向为从左到右,阿拉伯语的基础方向为从右到左。
方向串是指在一段文字中具有相同方向性的连续字符,并且其前后没有相同方向性的其它方向串。
下方假设大写字母为从右到左的文字。

<p dir="ltr">The apple is called <bdo dir="rtl">APPLE</bdo> in ar.</p>
在这个例子中,包含了三个方向串。该句子以从左到右的方向串开始,然后是从右到左的方向串,最后以从左到右的方向串结尾。
要注意的是,方向串的排列顺序和数目往往会受到全局方向的影响。上面的例子中采用是从左到右的全局方向,如果该全局方向变为从右到左,那这个例句中方向串的排列顺序将如下图所示:

<p dir="rtl">The apple is called <bdo dir="rtl">APPLE</bdo> in ar.</p>
Web中控制文字方向的方式有三种:html实体(‎ ‏)、bid与bdo标签+dir属性、css属性(direction + unicode-bidi)
‎与‏可以用来打断方向串的连续性,影响中性字符的方向

下面这段文本中共有四个中性字符:"."、 "+"、 "("、")";受从左到右基础方向影响这几个字符的方向性都表现为从左到右,界面也是正常的。
<p dir="ltr">My first paragraph.U+202(C)</p>

如果将基础方向设置为从右到左
<p dir="rtl">My first paragraph.U+202(C)</p>
最右边的")"受基础方向影响会出现我们不想要的结果,而其他三个中性字符受上下文方向性影响依旧保持从左到右的方向性。

我们可以使用‎实体来改变")"的方向性。
<p dir="rtl">My first paragraph.U+202(C)‎</p>

在上文介绍方向串时已经看到大写字母变成从右到左的方向这就是bdo元素+dir的作用,覆盖元素内文本的方向性。
bdi元素的目的是设置一个隔离区域。如果不设置dir则使用上下文的基础方向。
<ul>
<li>Username Bill:80 points</li>
<li>Username <bdi><bdo dir="rtl">Steve</bdo></bdi>: 78 points</li>
</ul>

大家可以试试把bdi元素去掉是什么效果,试着分析一下里面的方向串。
如果设置dir属性那么就为这个隔离区域设置了一个基础方向。
<p dir="rtl">These fruits <bdi dir="ltr">are called <bdo dir="rtl">APPLE</bdo>, </bdi><bdo dir="rtl">PEAR</bdo> and <bdo dir="rtl">ORANGE</bdo> in Arabic.</p>

注意里面的空格跟标点符号的方向性。
direction跟unicode-bidi这两个是css属性,通常放在一起来控制文本的方向,大家可以自己查看一下css手册。
direction+unicode-bidi:embed 的效果类似于bdi元素; direction+ unicode-bidi: bidi-override 的效果类似于bdo元素。
实际项目中我遇到阿拉伯语下在表格中显示负数问题,看起来的效果是:“88-”;使用以上属性direction:ltr,unicode-bidi:embed,可以改变显示效果:“-88”。
RTL布局
工作中遇到的另一个跟语言相关的问题就是页面布局问题。阿拉伯文(ar)跟希伯来文(he)的页面布局同英语下的页面布局刚好是镜像关系。这一点大家可以试试把浏览器的语言设置为阿拉伯语,观察一下浏览器上的控件布局(要保证你能再设置回来)。
首先判断用户设置的语言,如果是ar跟he则将全局基础方向设置为rtl,这时基本可以解决大多数问题。
function localeIsSame(locale1, locale2){
return locale1.indexOf(locale2) > -1 || locale2.indexOf(locale1) > -1;
}
function _setRTL(locale){
var rtlLocales = ["ar", "he"];
var dirNode = document.getElementsByTagName("html")[0];
var isRTLLocale = false;
for (var i = 0; i < rtlLocales.length; i++) {
if (localeIsSame(rtlLocales[i], locale)) {
isRTLLocale = true;
}
}
if (isRTLLocale) {
dirNode.setAttribute("dir", "rtl");
dirNode.className += " esriRtl jimu-rtl";
dirNode.className += " " + locale + " " +
(locale.indexOf("-") !== -1 ? locale.split("-")[0] : "");
}else {
dirNode.setAttribute("dir", "ltr");
dirNode.className += " esriLtr jimu-ltr";
dirNode.className += " " + locale + " " +
(locale.indexOf("-") !== -1 ? locale.split("-")[0] : "");
}
window.isRTL = isRTLLocale;
}
然后将float和text-align以及控制间距的margin、padding从所有的css class中抽离出来单独成类,如:

在RTL时将他们左右互换

在代码中与rtl布局相关的部分通过判断全局变量window.isRTL来处理。
html[dir='rtl'] caption,
html[dir='rtl'] th {
text-align: right;
} .jimu-rtl {
direction: rtl;
} .jimu-align-trailing {
text-align: right;
}
.jimu-align-leading {
text-align: left;
}
.jimu-float-trailing {
float: right;
}
.jimu-float-leading {
float: left;
}
.jimu-numeric-value {
direction: ltr;
unicode-bidi: embed;
}
/* if a ltr element is inside a rtl page */
.jimu-ltr .jimu-float-leading {
float: left !important;
} /* RTL alignment */
.jimu-rtl .jimu-align-trailing {
text-align: left;
}
.jimu-rtl .jimu-align-leading {
text-align: right;
} .jimu-rtl .jimu-float-trailing {
float: left;
}
.jimu-rtl .jimu-float-leading {
float: right;
}
/******* margins ******/
.jimu-leading-margin0 {
margin-left: 0;
}
.jimu-leading-margin025 {
margin-left: 0.25em;
}
.jimu-leading-margin05 {
margin-left: 0.5em;
}
.jimu-leading-margin1 {
margin-left: 1em;
}
.jimu-leading-margin15 {
margin-left: 1.5em;
}
.jimu-leading-margin2 {
margin-left: 2em;
}
.jimu-leading-margin25 {
margin-left: 2.5em;
}
.jimu-leading-margin3 {
margin-left: 3em;
}
.jimu-leading-margin35 {
margin-left: 3.5em;
}
.jimu-leading-margin4 {
margin-left: 4em;
}
.jimu-leading-margin5 {
margin-left: 5em;
}
.jimu-leading-margin6 {
margin-left: 6em;
}
.jimu-leading-margin7 {
margin-left: 7em;
}
.jimu-leading-margin10 {
margin-left: 10em;
} .jimu-trailing-margin025 {
margin-right: 0.25em;
}
.jimu-trailing-margin05 {
margin-right: 0.5em;
}
.jimu-trailing-margin075 {
margin-right: 0.75em;
}
.jimu-trailing-margin1 {
margin-right: 1em;
}
.jimu-trailing-margin15 {
margin-right: 1.5em;
}
.jimu-trailing-margin2 {
margin-right: 2em;
}
.jimu-trailing-margin25 {
margin-right: 2.5em;
}
.jimu-trailing-margin3 {
margin-right: 3em;
}
.jimu-trailing-margin35 {
margin-right: 3.5em;
}
.jimu-trailing-margin4 {
margin-right: 4em;
}
.jimu-trailing-margin5 {
margin-right: 5em;
}
.jimu-trailing-margin6 {
margin-right: 6em;
} .jimu-leading-padding05 {
padding-left: 0.5em;
}
.jimu-leading-padding1 {
padding-left: 1em;
}
.jimu-trailing-padding1 {
padding-right: 1em;
} /* RTL related: margins, padding */
.jimu-rtl .jimu-leading-margin0 {
margin-right: 0;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin025 {
margin-right: 0.25em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin05 {
margin-right: 0.5em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin075 {
margin-right: 0.75em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin1 {
margin-right: 1em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin15 {
margin-right: 1.5em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin2 {
margin-right: 2em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin25 {
margin-right: 2.5em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin3 {
margin-right: 3em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin35 {
margin-right: 3.5em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin4 {
margin-right: 4em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin5 {
margin-right: 5em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin6 {
margin-right: 6em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin7 {
margin-right: 7em;
margin-left: auto;
}
.jimu-rtl .jimu-leading-margin10 {
margin-right: 10em;
margin-left: auto;
} .jimu-rtl .jimu-trailing-margin025 {
margin-left: 0.25em;
margin-right: auto;
}
.jimu-rtl .jimu-trailing-margin05 {
margin-left: 0.5em;
margin-right: auto;
}
.jimu-rtl .jimu-trailing-margin075 {
margin-left: 0.75em;
margin-right: auto;
}
.jimu-rtl .jimu-trailing-margin1 {
margin-left: 1em;
margin-right: auto;
}
.jimu-rtl .jimu-trailing-margin15 {
margin-left: 1.5em;
margin-right: auto;
}
.jimu-rtl .jimu-trailing-margin2 {
margin-left: 2em;
margin-right: auto;
}
.jimu-rtl .jimu-trailing-margin25 {
margin-left: 2.5em;
margin-right: auto;
}
.jimu-rtl .jimu-trailing-margin3 {
margin-left: 3em;
margin-right: auto;
}
.jimu-rtl .jimu-trailing-margin4 {
margin-left: 4em;
margin-right: auto;
}
.jimu-rtl .jimu-trailing-margin5 {
margin-left: 5em;
margin-right: auto;
}
.jimu-rtl .jimu-trailing-margin6 {
margin-left: 6em;
margin-right: auto;
} .jimu-rtl .jimu-leading-padding05 {
padding-right: 0.5em;
padding-left: auto;
}
.jimu-rtl .jimu-leading-padding1 {
padding-right: 1em;
padding-left: auto;
}
.jimu-rtl .jimu-trailing-padding1 {
padding-left: 1em;
padding-right: auto;
}
参考文章:
bidi(双向文字)与RTL布局总结的更多相关文章
- HTML/CSS实现文字环绕图片布局
原文: https://blog.csdn.net/yiyelanxin/article/details/75006925 在一个图文并茂的网页上,文字环绕图片可以使布局美观紧凑,如何实现呢?有两种办 ...
- iOS UIButton 图片文字上下垂直布局 解决方案
实现如图所示效果: 这是一个UIButton,需要改变image和title相对位置. 解决如下: //设置文字偏移:向下偏移图片高度+向左偏移图片宽度 (偏移量是根据[图片]大小来的,这点是关键)b ...
- 把div 当文字来进行布局控制
两边对齐 text-align: justify; text-justify: distribute-all-lines;/*ie6-8*/ text-align-last: justify;/* i ...
- CSS 世界中的方位与顺序
在 CSS 中,我们经常会与各种方向方位打交道. 譬如 margin.padding,它们就会有 margin-left.margin-right 或者是 padding-left.padding-r ...
- Android 巧妙实现图片和文字上下布局或者左右布局
最近去了一家新公司,然后开始做新的项目,看其代码发现了一个很巧妙的方法来实现图片在上面文字在下面的布局方式.只需要一个控件——RadioButton. 布局文件很简单,用来展示RadioBUtton的 ...
- 学习iOS设计--iOS8的颜色、文字和布局学习
在去年,Apple针对新时代用户彻底更新了其设计语言.现在的设计语言相对之前大为简化,能够让设计师将精力集中到动画和功能上,而不是繁复的视觉细节上. 很多人都曾问过我:设计应当如何入门?成为一名优秀设 ...
- Android 4.2原生支持从右到左的文字排列格式
Android 4.1(Jelly Bean) 在TextView和EditText 元素里对“双向文字顺序”提供了有限的功能支持,允许应用程序在编辑和显示字符的时候,能够同时支持从左到右(LTR) ...
- CSS.01 -- 选择器及相关的属性文本、文字、字体、颜色、
与html相比,Css支持更丰富的文档外观,Css可以为任何元素的文本和背景设置颜色:允许在任何元素外围设置边框:允许改变文本的大小,装饰(如下划线),间隔,甚至可以确定是否显示文本. 什么是CSS? ...
- Android 本地化适配:RTL(right-to-left) 适配清单
本文首发自公众号:承香墨影(ID:cxmyDev),欢迎关注. 一. 序 越来越多的公司 App,都开始淘金海外,寻找更多的机会.然而海外市场千差万别,无论是市场还是用户的使用习惯,都有诸多的不同. ...
随机推荐
- Mongodb无法访问28107的问题
解压mongodb文件后,放到指定文件,最好别有空格.汉字之类的文件中 此时在mongodb文件夹下,建立一个 db 文件夹,此时执行启动命令,默认27017端口号可以打开,但是28017端口无法打开 ...
- SEO和SEM的区别
SEO是属于SEM的一部分,SEO和SEM最主要的是最终目标的不同: SEO主要是为了关键词的排名.网站的流量.网站的结构.搜索引擎中页面收录的数据. SEM是通过SEO技术基础上扩展为搜索引擎中所带 ...
- MySQL pdo预处理能防止sql注入的原因
MySQL pdo预处理能防止sql注入的原因: 1.先看预处理的语法 $pdo->prepare('select * from biao1 where id=:id'); $pdo->e ...
- 手势估计- Hand Pose Estimation
http://blog.csdn.net/myarrow/article/details/51933651 1. 目前进展 1.1 相关资料 1)HANDS CVPR 2016 2 ...
- create()创建的控件不能映射消息函数的解决
有时,使用create()在运行时创建的控件不能将消息映射到父窗口内,此时需要使用消息转发的机制,主要原理:注册一个全局的消息,针对接收消息的控件编写继承类,在该继承类中响应消息,并将已注册的全局消息 ...
- VS2012常用快捷键
Ctrl+K,D ----格式化全部代码 Ctrl+K,F ----格式化选中的代码 Ctrl+K,C ----注释选定内容 Ctrl+K,U ----取消注释选定内容 Ctrl+J或者 Ctrl+S ...
- adb unknown host service 这个问题的解决,转载
一直没搞明白这个问题咋出现的,但今天看到一个方法,搞定了!原来是豌豆荚占用了 5037 端口导致. 参见原文章:一个豌豆荚引发的血案——关于ADB server didn't ACK的问题 简单来讲, ...
- css中图片等比例缩放
li img{ display: inline-block; max-height: 60px; max-width: 60px; vertical-align: middle; }
- .net 批量打印可实现方案
最近几年一直在教育行业工作,主要负责竞赛类系统的开发工作,包括网上报名,安排考场,在线考试,学业报告书等. 打印功能在这个信息化时代非常普遍,浏览器都自带打印功能,通常的做法是调用 window.pr ...
- Map中的entry
是java中的一个对象,一般可以通过map.entrySet()得到.1,entrySet实现了Set接口,里面存放的是键值对.一个K对应一个V.2,用来遍历map的一种方法.Set<Map.E ...