RTL布局的Sass样式解决方案

1,510 阅读4分钟

背景

在实际业务场景中,产品面向的用户不限国籍,所以大部分PC端产品都需要兼容多种语言,移动端产品也有单独的国际化版本。本文将介绍两种关于国际化样式处理的解决方案,主要解决如何满足不同阅读习惯的人群对样式的需求。本文的方案既适用于移动端,也适用于PC端。

在一般语言中,阅读习惯是从左往右(Left-To-Right, LTR),在处理国际化兼容性时,只需要处理字符长度的兼容性。但是部分语言,例如阿拉伯语,阅读方式是从右往左(Right-To-Left, RTL),除了字符长度,还需要考虑文字对齐方式和页面布局等问题。

本文给出的方案旨在基于已有的中文样式,最大程度兼容多国语言(同时支持LTR和RTL),减少二次开发时间以及降低维护成本。

对于业务和组件库,分别提供两种解决办法和思路。

1. 业务中的解决方案

在实际的开发中,通常主线的业务已经开发出一套完整的样式,在不影响已有版本样式的前提下,如何使其兼容多种语言。如下图所示,我们将保留主线文件,在此基础上,添加国际化相关配置,以此减少冗余、重复代码。

image

1.1 Sass配置文件以及目录结构

在LTR样式文件中,文字的展示是从左到右,也会出现left、right、padding-left、margin-left等和左右相关的属性。但是在RTL的方案中,文字展示方式以及这些相关属性都和LTR中的相反。

可以先定义,在LTR中起和止分别为left和right。RTL与之相反。

首先创建两个方向配置文件文件分别命名为direction-ltr.scss和direction-rtl.scss。

以LTR为例(RTL同理),目录结构如下:

image

在LTR文件里面,定义如下变量:

/*** LTR ***/
$direction           :ltr;
$opposite-direction   :rtl;

$start-direction     :left;
$end-direction       :right;

$transform-direction :1;
$rotate-direction :0;

在RTL文件里面,定义如下变量:

/*** RTL ***/
$direction           :rtl;
$opposite-direction   :ltr;

$start-direction     :right;
$end-direction       :left;

$transform-direction :-1;
$rotate-direction :1;

然后创建文件index.scss,在里面引入所有业务或者组件相关样式文件,但是不引入方向配置文件。

然后创建对应的入口文件main-ltr.scss和main-rtl.scss。

在LTR文件里面:

@import "config/direction-ltr.scss";
@import "index.scss";

在RTL文件里面:

@import "config/direction-rtl.scss";
@import "index.scss";

1.2 如何使用方向配置文件中的变量

直接用变量值,替代文件里面的方向相关的属性值。

body {
  direction: $direction;
  text-align: $start-direction;
}

CSS Positions:

position: absolute;
#{$end-direction}: 0;

CSS Margins/Paddings/Borders:

margin-#{$end-direction}: 0;
padding-#{$start-direction}: 320px;
border-#{$start-direction}: 1px solid #d0d2da;

CSS Floats

float: $end-direction;

CSS Transform: translateX/scaleX/rotate

transform: translateX($transform-direction * -100%);
transform: scaleX($transform-direction); // 使元素沿着中轴进行水平翻转
transform: rotate(180deg * $rotate-direction); // 水平翻转(上下对称的图标)

例如,RTL语言中,返回按钮的方向需要单独用变量控制。可以用scaleX或者rotate,同时兼容LTR和RTL。效果如下。
LTR:

RTL (without direction):

RTL (with direction):

注意,如果Margin/Padding是如下写法, 需要做对应修改:

padding: 20px 10px 20px 5px;

修改为:

padding:20px 0;
padding-#{$start-direction}:10px;
padding-#{$end-direction}:5px;

如果部分属性在LTR和RTL里面的值相同,直接保留原有属性值,不需要用变量替换。

2. 基础UI组件库中的解决方案

由于组件库各个组件之间模块划分非常明确,样式部分也是只作用于各个独立的组件,所以组件库方面需要做RTL的方向支持,考虑的是只写一套方向变量,然后通过切换组件最顶层元素(dom)的dir属性,利用样式的属性选择器进行样式的区分。

2.1 首先定义一套方向变量

lib_direaction.scss

/** LTR **/
$start: right;
$left: left;

/** RTL **/
$rtl_right: left;
$rtl_left: right;

2.2 然后是组件样式

利用dir=['rtl']属性选择器来区分两个阅读方向的样式

button.scss为例:

.button{
	text-align: $right;
	padding-#($right): 10px;
	padding-#($left): 20px;
}

.button[dir='rtl']{
	text-align: $rtl_right;
	padding-#($rtl_right): 10px;
	padding-#($rtl_left): 20px;
}

样式的效果,只是作为简单示例:

<Button>LTR</Button>
<Button dir=['rtl']></Button>

06457521-F830-4996-A673-F163CCA93B81

3. 两种方案的总结

业务解决方案定义了两套scss变量文件,然后分别再创建两个业务逻辑样式文件,分别引入不同的变量文件,再引入业务逻辑样式表,最后通过手动或JS逻辑去控制“LTR”和“RTL”样式文件的切换以达到切换样式的效果。

好处:只需要写一套业务样式,然后通过切换不同的变量文件去控制样式的切换。

组件的解决方案定义了一套包含LTR和RTL两个方向的变量,然后在样式文件中使用属性选择器去写对应的样式,使用两种方向的变量。

好处:只需要一套顶层的关于方向的变量定义,切换样式的时候也不需要切换加载不同的文件,而只需要修改组件的dir属性即可

4. 待优化的部分

  • 目前的规则都是人为遵循,难免会有遗漏的部分,后续为了多人维护项目,可以考虑加入类似tslint的检验工具,对涉及方向的变量进行统一校验。
  • 组件库的样式解决方案上还需要在语义化,以及代码冗余问题上进行优化。