利用 Sass 优雅解决 RTL 语言布局适配

October 07, 2021

前言

不同于国内的活动,国际化活动投放大量地区,这些地区有着不同的语言体系、阅读风格,意味着界面布局上要进行相应的适配,以获得更好的用户体验。

本文介绍如何利用 Sass(CSS 预处理器)@mixin and @include 特性去优雅解决国际化 UI 布局。

适配 RTL

首先需要考虑到 RTL(right-to-left) 语言的适配,其文字的阅读书写方向是从右向左的,例如阿拉伯语(Arabic)、希伯来语(Hebrew)、波斯语(Persian)等。在这些语言环境下的用户界面,需要从右向左「镜像 Mirroring」设计。

什么是镜像设计呢?所谓一图胜千言:

从示例图中可以看出,从右到左的布局进行了水平翻转,使 LTR 和 RTL 沿着中轴线对折,能完全贴合,即镜像设计。

想要阅读更多有关 RTL 的设计,可查阅 RTL 镜像指南

下面举一场活动中真实的 🌰,为了实现对照,我同时放上了正常布局和 RTL 布局:

codepen demo:

核心的 Sass 代码如下:

scss
@mixin dir($dir: "rtl") {
[dir="#{$dir}"] & {
@content;
}
}
.logo {
/* ... */
border-bottom-right-radius: 6px;
left: 0;
@include dir {
left: unset; /* 还原 LTR 模式下的 left 属性 */
right: 0; /* 为实现镜像效果,将 left 方向进行翻转,即 right */
border-bottom-right-radius: unset;
border-bottom-left-radius: 6px;
}
}
scss
@mixin dir($dir: "rtl") {
[dir="#{$dir}"] & {
@content;
}
}
.logo {
/* ... */
border-bottom-right-radius: 6px;
left: 0;
@include dir {
left: unset; /* 还原 LTR 模式下的 left 属性 */
right: 0; /* 为实现镜像效果,将 left 方向进行翻转,即 right */
border-bottom-right-radius: unset;
border-bottom-left-radius: 6px;
}
}

unset 的作用非常之大,可以用它来还原样式属性。

因为对于所有 CSS 属性,都存在三个可选值:

  • initial,该属性使用默认值
  • inherit,该属性继承父元素的值
  • unset,如果该属性是可继承属性,该值等同于 inherit;如果该属性是非继承属性,该值等同于 initial

最后,来看编译后的 CSS 代码:

css
[dir="rtl"] .container .logo {
left: unset;
right: 0;
border-bottom-right-radius: unset;
border-bottom-left-radius: 6px;
}
css
[dir="rtl"] .container .logo {
left: unset;
right: 0;
border-bottom-right-radius: unset;
border-bottom-left-radius: 6px;
}

不知道大家发现没有,虽然我没有显式设置 display-direction: row-reverse,但实际上 flex 的布局方向自动变成了从右到左。

根据 MDN 的解释,这是由于值 row 和 row-reverse 受 flex 容器的方向性的影响, 如果它的 dir 属性是 ltr,row 表示从左到右定向的水平轴,而 row-reverse 表示从右到左; 如果 dir 属性是 rtl,row 表示从右到左定向的轴,而 row-reverse 表示从左到右。

切记在你的 HTML 结构最外层的 DOM 节点加上 dir 属性,否则该 mixin 不会生效。

适配字号

其次需要考虑到文字字号的适配,同样的一句话:”user daily tasks“,在不同语言环境下的字符长度不一,需要做 font-size 的适配。

先看反面例子,俄语翻译文案由 local 同学提供,如果不对 font-size 做适配,就会造成文字溢出内容的情况,这是非常低级的错误。

codepen demo:

经过适配后,表现如下:

核心的 Sass 代码如下:

scss
@mixin lang($languages...) {
@for $i from 0 to length($languages) {
[lang="#{nth($languages, $i + 1)}"] & {
@content;
}
}
}
p {
/* ... */
@include lang("en") {
color: Crimson;
}
@include lang("ru-RU") {
/* 支持传入多个语言,如 lang("vi-VN", "ko-KR") */
color: DeepSkyBlue;
font-size: 12px;
}
}
scss
@mixin lang($languages...) {
@for $i from 0 to length($languages) {
[lang="#{nth($languages, $i + 1)}"] & {
@content;
}
}
}
p {
/* ... */
@include lang("en") {
color: Crimson;
}
@include lang("ru-RU") {
/* 支持传入多个语言,如 lang("vi-VN", "ko-KR") */
color: DeepSkyBlue;
font-size: 12px;
}
}

同样来看编译后的 CSS 代码:

css
[lang="en"] p {
color: Crimson;
}
[lang="ru-RU"] p {
color: DeepSkyBlue;
font-size: 12px;
}
css
[lang="en"] p {
color: Crimson;
}
[lang="ru-RU"] p {
color: DeepSkyBlue;
font-size: 12px;
}

切记在你的 HTML 结构最外层的 DOM 节点加上 lang 属性,否则该 mixin 不会生效。

Reference


B2D1 (包邦东)

Written by B2D1 (包邦东)