Skip to content

Commit 8c40b23

Browse files
committed
update
1 parent 704f116 commit 8c40b23

File tree

3 files changed

+273
-14
lines changed

3 files changed

+273
-14
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,4 @@ cd code/xxxx/
4343

4444
* [深入理解 CSS 属性 z-index](https://github.com/happylindz/blog/issues/11)
4545
* [纯 CSS 实现多行文字截断](https://github.com/happylindz/blog/issues/12)
46-
46+
* [国际化 - 通用 LTR/RTL 布局解决方案](https://github.com/happylindz/blog/issues/16)

markdown/ltr-rtl-new.md

+267
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
# 国际化 - 通用 LTR/RTL 布局解决方案
2+
3+
[原文地址](https://github.com/happylindz/blog/issues/16)
4+
5+
在英文或者中文的网站,我们习惯的阅读方式都是从左往右的,所以你在访问国内外的网站的时候会发现,不管是文字还是布局,都是从左往右进行排版,而我们也熟悉和适应了这种阅读习惯,但是在中东地区,有很多国家,诸如像阿拉伯语、希伯来语,他们的阅读习惯却是从右到左的,恰好跟我们是相反的,我也查阅了大量阿拉伯语的网站的设计,感兴趣也可以点击下面的网站看看:
6+
7+
* [http://wam.ae/ar](http://wam.ae/ar)
8+
* [https://www.emaratalyoum.com/](https://www.emaratalyoum.com/)
9+
10+
通过上面的网站,可以很直观地看出像阿拉伯语,典型 RTL 布局网站的特点:
11+
12+
1. 文字都是右对齐,并且是从右往左阅读的
13+
2. 排版都是从右到左的,在一个产品列表中,右边第一个商品是第一个
14+
3. 箭头代表的意义刚好相反,比如在轮播图中,向左箭头代表下一帧,而向右箭头则代表查看上一张图片
15+
16+
知道了 RTL 布局的特点,我们在使用场景上需要考虑:
17+
18+
1. 如何以较低的成本,可维护,兼容地改造线上已有的场景支持 RTL 布局网站
19+
2. 对于未来新的场景,怎么样在编码的环节可以快速支持 LTR、RTL 布局特点的网站
20+
21+
所以本文探究的是**在假定语言文案,图片等信息正确的情况下,如何使用一套代码,不仅可以支持像英文,中文等 LTR 布局的网站,也可以支持像阿拉伯,希伯来语等 RTL 布局的网站。**
22+
23+
## "神奇" 的 direction
24+
25+
在做 RTL 布局的时候,我们自然而然就会想到 direction 这个 CSS 属性,它与在 html 标签上直接添加 ```dir="rtl"``` 的作用一样,可以改变我们网站的布局特点,CSS 手册中对 direction 属性是这样描述的:**该属性指定了块的基本书写方向,以及针对 Unicode 双向算法的嵌入和覆盖方向。**
26+
27+
讲的很绕口,看的云里雾里的,通俗点讲,它改变了部分元素的书写特点:
28+
29+
1. 定义过 ```direction:rtl``` 的元素,如果没有预先定义过 text-align,那么这个元素的 text-align 的值就变成了 right,如果设置了 left/center 则无效
30+
2. 对于数字和标点符号以外的编码,顺序仍然是从左到右的
31+
3. 改变了 inline-block 元素的书写顺序
32+
33+
通过下面几个简单例子就可以理解:
34+
35+
```html
36+
<style>
37+
span {
38+
display: inline-block;
39+
}
40+
</style>
41+
<div style="direction: rtl;">1 2 3 4 5 6</div>
42+
<div style="text-align:left;">1 2 3 4 5 6</div>
43+
<div style="text-align:right;">1 2 3 4 5 6</div>
44+
<div style="direction: rtl;"><span>This is </span><span>my blog</span></div>
45+
<div style="direction: rtl;">这是我的博客。</div>
46+
<div style="text-align:right;">这是我的博客。</div>
47+
<div style="direction: rtl;">هذا هو بلدي بلوق.</div>
48+
<div style="text-align:right;">هذا هو بلدي بلوق.</div>
49+
```
50+
展示效果:
51+
52+
![](https://img.alicdn.com/tfs/TB1G.MzlkvoK1RjSZFNXXcxMVXa-800-378.png)
53+
54+
## direction 真的是万能的吗?
55+
56+
上面介绍了一些 direction 的基本用法,那是不是就可以认为只要使用 ```direction: rtl``` 之后网站就可以做到兼容阿拉伯语/希伯来语等排版从右往左的网站了呢?答案是否定的。 direction 的功能并没有你想象中那么强大。
57+
58+
在 PC 网页上,页面布局是千变万化的,比如我们常使用的布局有:flex,内联,浮动,绝对定位等布局方式。
59+
60+
我也对一些常用的布局方式进行测试:
61+
62+
(1) flex 布局:
63+
<iframe width="100%" height="300" src="//jsfiddle.net/0srfqgnp/1/embedded/" allowfullscreen="allowfullscreen" allowpaymentrequest frameborder="0"></iframe>
64+
65+
(2) inline-block 布局:
66+
67+
<iframe width="100%" height="300" src="//jsfiddle.net/t7kn9dap/embedded/" allowfullscreen="allowfullscreen" allowpaymentrequest frameborder="0"></iframe>
68+
69+
(3) float 布局:
70+
<iframe width="100%" height="300" src="//jsfiddle.net/y0tdv7hn/embedded/" allowfullscreen="allowfullscreen" allowpaymentrequest frameborder="0"></iframe>
71+
72+
(4) 绝对定位布局:
73+
<iframe width="100%" height="300" src="//jsfiddle.net/yopreL9z/embedded/" allowfullscreen="allowfullscreen" allowpaymentrequest frameborder="0"></iframe>
74+
75+
通过上述的测试可以发现 direction 只能改变 display: flex/inline-block 元素的书写方向,对于 float/绝对定位布局就无能为力,更别谈复杂的页面布局,比如 BFC 布局、双飞翼、圣杯布局等等。
76+
77+
另外 direction 无法改变 margin, padding, border 的水平方向,也就是说除非你的元素是居中的,否则当你的元素是不对称的话,即使你改变了元素的书写方向和顺序,margin-left 还是指向左边的,它并不会留出右边的空白。从下面的图对比就可以看出:在左右间距不对称的时候,直接使用 direction 会对我们本来设计的布局产生效果上的偏差。
78+
79+
![LTR 布局效果](https://img.alicdn.com/tfs/TB1iz9slmzqK1RjSZFHXXb3CpXa-2518-824.jpg)
80+
![RTL 布局效果](https://img.alicdn.com/tfs/TB1RNWolb2pK1RjSZFsXXaNlXXa-2526-808.jpg)
81+
82+
## 基于 direction 通用布局方案设计
83+
84+
在知道了 direction 的特点和不足之后,那么如何围绕 direction 打造一套通用的布局方案呢?
85+
86+
从上面的分析,对于布局/间距翻转能力的缺失,我们可以对 CSS 进行后处理来达到我们需要的效果,举个例子,可以在 Github 上搜 [rtlcss](https://rtlcss.com/learn/usage-guide/string-map/) 这个模块,它的原理就是对 CSS 文件进行处理,比如将 CSS 属性中的 left 改为 right,right 改为 left。
87+
88+
通过这种能力,无论是 float/绝对定位布局,还是 margin/padding 间距,都可以很好地改变书写方向。举个简单例子:
89+
90+
```css
91+
.test {
92+
direction: ltr;
93+
float: left;
94+
position: relative;
95+
left: 20px;
96+
margin-left: 100px;
97+
padding-right: 30px;
98+
}
99+
```
100+
101+
通过 rtlcss 模块处理后的 CSS 将变成:
102+
103+
```css
104+
.test {
105+
direction: rtl;
106+
float: right;
107+
position: relative;
108+
right: 20px;
109+
margin-right: 100px;
110+
padding-left: 30px;
111+
}
112+
```
113+
114+
通过这样的处理,大部分场景下的布局都可以都可以得到很好的处理,比如简单对比像绝对定位这样的布局:
115+
116+
![LTR 绝对定位布局](https://img.alicdn.com/tfs/TB1wOaxlbPpK1RjSZFFXXa5PpXa-3108-870.png)
117+
118+
经过 rtlcss 处理后的页面效果:
119+
120+
![RTL 绝对定位布局](https://img.alicdn.com/tfs/TB1Y9KplmrqK1RjSZK9XXXyypXa-3090-880.png)
121+
122+
**上面是基于 direction 布局方案原理,当然它也有一些能力上的不足和值得去思考的地方:**
123+
124+
**首先这是针对 CSS 的,也就是页面的初始化展示效果,但是涉及到 JS 就无能为力了**,比如在轮播图中,通过 JS 去控制图片的下一帧,在不同的 LTR、RTL 布局中就产生额外的兼容代码。
125+
126+
**其次,它无法处理 html 中内嵌在标签中的样式**,比如我们在写 React 组件中可以能会写出这样的代码:
127+
128+
```javascript
129+
function SomeComponent({ isSomething }) {
130+
return <div style={{ marginLeft: isSomething ? 20 : 10 }} ></div>;
131+
}
132+
```
133+
134+
像这样书写的方式以后就要改成基于 class 切换:
135+
136+
```javascript
137+
function SomeComponent({ isSomething }) {
138+
const cls = classNames({
139+
marginLeft20: isSometing,
140+
marginLeft10: !isSometing
141+
})
142+
return <div className={cls}></div>;
143+
}
144+
```
145+
146+
这部分内容可以通过规范去避免写内联样式,也可以通过正则去修改替换修改样式。
147+
148+
**第三点需要考虑的是图标库**,上面的问题解决了布局,文字排版的问题,但是对于图标来说仅仅只是布局上的移动,根据 Google 的 [Material Design在双向性一章](https://www.mdui.org/design/usability/bidirectionality.html#bidirectionality-rtl-mirroring-guidelines) 的内容可以看出,有些图标是需要翻转的,有些图标不用,再比如左右箭头,在不同布局中的意义也是不一样的,所以针对 RTL 的布局,我们需要重新设计一套字体库用于 RTL 布局,真正给使用诸如像阿拉伯语、希伯来语的用户带来本地化的体验。
149+
150+
第四点是需要有更加细粒度的控制,因为在 RTL 布局中,不是所有的内容都一定是从右到左进行排版的,**我们需要在整体 RTL 的页面中忽视掉某些模块,使其仍然是以从左往右顺序的能力。**
151+
152+
这部分怎么做呢?可以给不需要翻转的模块的 CSS 文件中添加像 ```/* rtl:ignore */```,然后让像 rtlcss 在处理的时候可以忽略掉对该模块的处理,从而让该模块在 RTL 布局中保持已有的展示效果。
153+
154+
在真正实现的过程中,肯定还会遇到其它更多的问题,比如像:CSS 的命名规则(直接加 -rtl 或其它来保证非覆盖发布),还是说如何进行 CDN 部署发布等等一系列的工程实践问题,相信在不久的将来,经过实践上线后会产出基于 direction 通用布局的最佳工程实践方案。
155+
156+
## "神奇" 的 transform 镜像翻转
157+
158+
上面介绍完基于 direction 的布局方案,最后通过一套代码编译成一套 html,多套 css,一套 js 文件,区分国家用户来进行访问。那么有没有可能通过一套代码,生成一套 html,css,js 文件供用户去访问呢?请听下文分解。
159+
160+
想必前端工程师都使用过 CSS3 的 transform 属性,通过 ```transform: scaleX(-1)``` 可以使页面沿着中轴进行水平翻转(关于 ```transform scaleX/rotateY``` 水平翻转用法可以看 [CSS垂直翻转/水平翻转提高web页面资源重用性
161+
](https://www.zhangxinxu.com/wordpress/2011/05/css%E5%AE%9E%E7%8E%B0%E5%90%84%E4%B8%AA%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E7%9A%84%E5%9E%82%E7%9B%B4%E7%BF%BB%E8%BD%AC%E6%B0%B4%E5%B9%B3%E7%BF%BB%E8%BD%AC%E6%95%88%E6%9E%9C/)
162+
163+
通过水平翻转,原本 LTR 的布局页面:
164+
165+
![LTR 布局页面](https://img.alicdn.com/tfs/TB1Dq9LlXYqK1RjSZLeXXbXppXa-3150-1510.png)
166+
167+
经过水平翻转之后就变成 RTL 布局页面:
168+
169+
![RTL 布局页面](https://img.alicdn.com/tfs/TB1o8yRlhTpK1RjSZFMXXbG_VXa-3168-1436.png)
170+
171+
并且这种方式在布局上具有良好的兼容性,跟 direction 改变方向不同,**你根本无需考虑你的布局:flex/浮动/绝对定位等等,都可以很好地从 LTR 布局变成 RTL 布局。**
172+
173+
解决了布局问题,但是也引入的新的问题,就是文字,图片等等信息全部都翻转了,所以我们在文字部分需要将文字再翻转回来,比如说在文字的容器上加上 ```transform: scaleX(-1)```,这样就可以保持内容的正确书写顺序。
174+
175+
基于这样的思路,一种通过 transform 镜像翻转来实现 RTL 布局的方案设计就应运而生。
176+
177+
## 基于 transform 镜像翻转通用布局方案设计
178+
179+
通过 transform 的镜像翻转,可以很好地解决了布局翻转的问题,基于 transform 设计通用布局我的思路是这样的:
180+
181+
首先编写一个 npm 模块,它是一个 React 组件,使用它的时候需要引入它的 CSS 文件和 JS 组件。
182+
183+
如果页面需要支持,在阿拉伯语页面上添加上全局翻转:
184+
185+
```css
186+
// xxxxx/index.css
187+
html[lang="ar"] {
188+
transform: scaleX(-1);
189+
}
190+
```
191+
192+
接下来只需要考虑页面上不需要翻转的内容,比如:文字,部分图片,一些图标等等元素。
193+
194+
对于这些元素,可以通过 React 组件进行包裹,用法如下:
195+
196+
```javascript
197+
import{ NoFlipOver } from 'xxxxx';
198+
199+
function SomeComp({ title, imgUrl }) {
200+
const comp1 = <NoFlipOver>
201+
{ title }
202+
</NoFlipOver>;
203+
const comp2 = <NoFlipOver>
204+
<Icon type="clock" />
205+
</NoFlipOver>;
206+
const comp3 = <NoFlipOver>
207+
<img src={ imgUrl } />
208+
</NoFlipOver>;
209+
const comp4 = <NoFlipOver>
210+
<SomethingYouDontKnow />
211+
</NoFlipOver>;
212+
}
213+
```
214+
215+
通过这种轻量级的入侵代码,开发者无需关心具体的翻转逻辑,只需要将页面中不需要翻转的内容进行包裹即可。而我们需要做的是如何编写一个通用的不翻转 React 组件,举个例子,如果接受到的内容是一段文字,就可以像这样进行处理:
216+
217+
```javascript
218+
// xxxxx/index.js
219+
const NoFlipOver = function({ children, ...props }) {
220+
if(typeof children === 'string') {
221+
return <span { ...props } className="no-flip-over">{ children }</span>;
222+
}
223+
}
224+
```
225+
```css
226+
// xxxxx/index.css
227+
html[lang="ar"] .no-flip-over {
228+
transform: scaleX(-1);
229+
}
230+
```
231+
232+
对文字的处理比较简单,只需要通过 span 标签进行包裹(保证文字向右对齐,如果原本是左对齐的话)这样简单的文字处理组件就完成,当然这里只是举一个简单的例子,在设计通用布局 React 容器组件的时候肯定需要考虑到各个方面,这里需要等我具体实践之后才能产出更多的经验。
233+
234+
基于这样的思路,可以很好,**更加细粒度地去控制页面模块的展示形态,需要翻转的内容,无需处理,不需要的翻转的内容,需要用一个 React 容器组件进行包裹,从而达到页面自适应 LTR/RTL 布局效果。**
235+
236+
前面介绍了基于 direction/transform 镜像翻转来实现通用布局方案,下面我就对比来谈谈 transform 镜像翻转方案相对于 direction 方案具有哪些优势呢?
237+
238+
首先它不只是针对 CSS 展示效果,因为是将整个页面沿中轴进行翻转,margin-left 在浏览器理解上是属于向右的,所以 **transform 方案是兼容 JS 逻辑的,也就是说无需修改 JS 逻辑,而 direction 方案只是针对 CSS,JS 逻辑需要调整兼容。**
239+
240+
**第二点,它可以直接使用一套图标库,一套图片即可,需要翻转的无需处理,不需翻转的就使用 NoFlipOver 进行包裹。**比如说像下面这样一个图文分离的 banner:
241+
242+
![LTR 布局](https://img.alicdn.com/tfs/TB1_j7tlgHqK1RjSZFEXXcGMXXa-3128-384.png)
243+
244+
经过镜像翻转之后,就变成了:
245+
246+
![RTL 布局](https://img.alicdn.com/tfs/TB1eDcslkzoK1RjSZFlXXai4VXa-2804-296.png)
247+
248+
如果是通过 direction 方案的话就需要准备两张图片了(当然如果是图文不分离的话也是需要老老实实准备两套图片)
249+
250+
**第三点,它不需要考虑 CSS 命名,CDN 部署等一系列工程问题**,因为它是划分 CSS 作用域的方式,针对 LTR/RTL 布局进行隔离适配。
251+
252+
**第四点,内嵌样式 transform 方案也可以很好地做到兼容,而 direction 方案是针对 CSS 文件的,如果要针对 html 文件则需要另外额外的工作。**
253+
254+
说了一些 transform 方案对比与 direction 方案的优势,下面就讲讲其缺点:
255+
256+
第一,**它需要对我们已有的业务场景进行改造,入侵业务代码**,也就是说,如果你的场景相对比较分散,公用模块复用率较低,那么在使用 transform 方案的时候就需要对每个场景单独进行修改适配,当然如果你的场景公用组件多,对公共模块修改可以很好在各个场景中复用,这样一次性的成本就相对比较容易。
257+
258+
第二点,对于一些页面滚动组件需要做额外的兼容操作,经过我的实践发现,滚动组件在经过翻转之后存在着一些问题,初步认为是因为翻转之后带来一些高度属性值的变化,具体原因需要等兼容适配时候才清楚。
259+
260+
## 总结
261+
262+
本文通过对 direction/transform 属性使用和剖析,设计了两款不同思路的 LTR/RTL 通用布局方案,两套方案各有千秋,有优势,也有自身不足的地方。
263+
264+
在动手准备改造之前,最好先跟 UED 确认好 RTL 布局的设计规范,避免因为主观认知导致错误的视觉偏差,这样可以给中东地区的用户提供更加本地化的体验,这里也有关于页面布局双向性的设计规范,感兴趣的可以看一看:[MATERIAL DESIGN - Bidirectionality](https://material.io/design/usability/bidirectionality.html)
265+
266+
最后针对不同的业务场景选择,运用合适的通用布局方案,才能有效地降低开发和维护成本。
267+

markdown/ltr-rtl.md

+5-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 国际化 - 通用 LTR/RTL 布局解决方案
1+
[原文地址](https://github.com/happylindz/blog/issues/16)
22

33
在英文或者中文的网站,我们习惯的阅读方式都是从左往右的,所以你在访问国内外的网站的时候会发现,不管是文字还是布局,都是从左往右进行排版,而我们也熟悉和适应了这种阅读习惯,但是在中东地区,有很多国家,诸如像阿拉伯语、希伯来语,他们的阅读习惯却是从右到左的,恰好跟我们是相反的,我也查阅了大量阿拉伯语的网站的设计,感兴趣也可以点击下面的网站看看:
44

@@ -57,18 +57,10 @@
5757

5858
我也对一些常用的布局方式进行测试:
5959

60-
(1) flex 布局:
61-
<iframe width="100%" height="300" src="//jsfiddle.net/0srfqgnp/1/embedded/" allowfullscreen="allowfullscreen" allowpaymentrequest frameborder="0"></iframe>
62-
63-
(2) inline-block 布局:
64-
65-
<iframe width="100%" height="300" src="//jsfiddle.net/t7kn9dap/embedded/" allowfullscreen="allowfullscreen" allowpaymentrequest frameborder="0"></iframe>
66-
67-
(3) float 布局:
68-
<iframe width="100%" height="300" src="//jsfiddle.net/y0tdv7hn/embedded/" allowfullscreen="allowfullscreen" allowpaymentrequest frameborder="0"></iframe>
69-
70-
(4) 绝对定位布局:
71-
<iframe width="100%" height="300" src="//jsfiddle.net/yopreL9z/embedded/" allowfullscreen="allowfullscreen" allowpaymentrequest frameborder="0"></iframe>
60+
1. flex 布局:https://jsfiddle.net/0srfqgnp/1/
61+
2. inline-block 布局:https://jsfiddle.net/t7kn9dap/
62+
3. float 布局:https://jsfiddle.net/y0tdv7hn/
63+
4. 绝对定位布局:https://jsfiddle.net/yopreL9z/
7264

7365
通过上述的测试可以发现 direction 只能改变 display: flex/inline-block 元素的书写方向,对于 float/绝对定位布局就无能为力,更别谈复杂的页面布局,比如 BFC 布局、双飞翼、圣杯布局等等。
7466

0 commit comments

Comments
 (0)