从右到左。 如何在RTL下打开站点界面

图片


我们最近将2GIS的在线版本翻译成阿拉伯语,并且在上一篇文章中,我谈到了实现这一点所必需的理论-dir dir="rtl" ,根据什么规则显示混合焦点文本以及如何控制自己。


现在是开始练习的时候了-只需最小的努力就可以将整个界面从右转到左,这样即使是真正的阿拉伯人也不会感觉到问题。


在本文中,我将告诉您如何快速原型化CSS组装以及在JS中分解的拐杖,简要介绍翻译和本地化的功能,回顾CSS的逻辑属性,并在CSS-in-JS中触摸RTL主题。


翻转样式


当我将dir =“ rtl”属性应用于标签时,只有元素的隐式顺序发生了变化-例如,表格单元格或flex元素的顺序。 在样式中明确指定的值没有任何反应。

采取位于右下角的某些通知的样式:


 .tooltip { position: 'absolute'; bottom: 10px; right: 10px; } 

dir="rtl"不会以任何方式影响这些样式-在RTL版本中,工具提示也位于右侧,尽管预期在左侧。


怎么办 您需要将right: 10px替换为left: 10px 。 其他所有样式也是如此。 绝对定位,边距/填充,文本对齐-对于阿拉伯版本,所有内容都需要朝相反的方向旋转。


快速原型


首先,您可以毫不犹豫地更改所有从左到右的出现,并用速记值做一点魔术:


  • 左:0→右:0
  • padding-left:4px→padding-right:4px
  • 边距:0 16px 0 0→边距:0 0 0 16px

postcss-rtl插件适用于此。 方便-您只需将其放入所有postcss-project插件列表中。 它用镜像规则替换所有定向规则,并将其包装在[dir="rtl"] 。 例如:


 /* input */ .foo { color: red; margin-left: 16px; } /* output */ [dir] .foo { color: red; } [dir="ltr"] .foo { margin-left: 16px; } [dir="rtl"] .foo { margin-right: 16px; } 

之后,您只需要设置dir="rtl"并且仅会自动应用必要的规则。 一切正常,似乎几乎所有东西都可以投入生产,但是此解决方案仅适用于快速原型:


  • 每个规则的特殊性增加 。 这不一定是问题,但是我想避免它;
  • 这种操作会引起bug 。 例如, 属性顺序可能会中断
  • css文件的大小明显增加[dir]添加到每个选择器,每个有向属性都重复。 在我们的案例中,一个项目的规模增加了21%,另一个项目的规模增加了35%:

原始大小(gzip)双向大小(gzip)肿胀
2gis.ru272.3 KB329.7 KB21%
m.2gis.ru24.5 KB33.2 KB35%
habr.com33.1 KB41.4 KB25%

有更好的选择吗?


必须分别收集LTR和RTL的样式。 这样就不必触摸选择器,并且CSS的大小几乎不会改变。


为此,我选择了:


  1. RTLCSS-该库在postcss-rtl的支持下。
  2. webpack-rtl-plugin是用于通过ExtractTextPlugin构建的样式的交钥匙解决方案。 引擎盖下有相同的RTLCSS。

然后,他开始将RTL和LTR收集到不同的文件styles.rtl.cssstyles.rtl.css 。 组装到不同文件的唯一缺点是,您必须先下载所需文件,才能即时替换dir。


RTLCSS允许您使用指令来控制特定规则的处理,例如:


 .foo { /*rtl:ignore*/ right: 0; } .bar { font-size:16px/*rtl:14px*/; } 

还有什么其他解决方案?


现有的所有解决方案与RTLCSS几乎没有什么不同。


  • 来自Twitter的css-flip
  • 来自Wikimedia的cssjanus
  • 是的,并且postcss-rtl支持onlyDirection参数,使用该参数只能收集一个方向的样式,但是大小仍在增长-例如,对于移动2GIS,它是18%,而不是35%(24.5 kB→29 kB)。

什么时候需要指令?


何时样式不取决于方向


例如,箭头的旋转角度指示风向:


图片
 .arrow._nw { /*rtl:ignore*/ transform: rotate(135deg); } 



或淡入淡出的电话号码-数字始终从左到右书写,这意味着渐变应该始终在右边:


图片


图片


何时将图标居中


这是上一段的特殊情况。 如果通过缩进/定位使不对称图标居中,则将其块移动到一侧,如果它反映了偏移量,则图标将“移动”到另一侧:


图片


在以下情况下,最好将图标放在svg中:



当您需要隔离不应响应RTL的整个小部件时


在我们的情况下,这是一张地图。 当在块指令中组装时,我们将包装所有样式: /*rtl:begin:ignore*/ ... /*rtl:end:ignore*/


还有更好的选择吗?


逆转规则的解决方案很好,但是问题来了-它是拐杖吗? 样式对方向的依赖性是现代Web的自然任务,并且其相关性每年都在增长。 这应该已经在现代标准和方法中得到反映。 发现了!


逻辑性质


为了使布局适应不同的方向, css中有一个逻辑属性标准。 它不仅涉及从左到右和从右到左的方向,而且还涉及从上到下的方向,但我们不会考虑。


我们已经在flexs和grid-row-start使用了类似的东西-例如, flex-startflex-endgrid-row-startgrid-column-end从左/右解开。


建议使用inline-startinline-endblock-startblock-end代替leftrighttopbottom的概念。 而不是widthheight而是inline-sizeblock-size 。 而不是shortcands abcd- logical adcb (逻辑shortes逆时针旋转)。 此外,对于配对现有的缩短项,出现新的配对版本-padding padding-blockmargin-inlineborder-color-inline等。


  • left: 0 → inset-inline-start: 0
  • padding-left: 4px → padding-inline-start: 4px
  • margin: 0 16px 0 0 → margin: logical 0 0 0 16px
  • padding-top: 8px; padding-bottom: 16px → padding-block: 8px 16px
  • margin-left: 4px; margin-right: 8px → margin-inline: 4px 8px
  • text-align: right → text-align: end

并且出现了期待已久的定位速记:


  • left: 4px; right: 8px → inset-inline: 4px 8px
  • top: 8px; bottom: 16px → inset-block: 8px 16px
  • top: 0; right: 2px; bottom: 2px; left: 0 → inset: logical 0 0 2px 2px

在不带标志的Firefox和基于标志的Webkit浏览器中已经可以使用此功能。


优点 -该解决方案是本地的,如果支持所需的浏览器,则完全不需要组装/插件即可使用。 不需要指令-当您指的是物理“左”时,只需left写而不是inline-start


缺点来自专家-没有插件,大多数浏览器中的代码都是无效的,您需要做很多工作来翻译现有的大型项目。


如何连接?


最简单的方法是后逻辑 。 如果没有dir参数,它将以与postcss-rtl相同的方式,使用给定的dir参数,仅针对指定方向收集两个方向的样式:


 .banner { color: #222222; inset: logical 0 5px 10px; padding-inline: 20px 40px; resize: block; transition: color 200ms; } /* becomes */ .banner { color: #222222; top: 0; left: 5px; bottom: 10px; right: 5px; &:dir(ltr) { padding-left: 20px; padding-right: 40px; } &:dir(rtl) { padding-right: 20px; padding-left: 40px; } resize: vertical; transition: color 200ms; } /* or, when used with { dir: 'ltr' } */ .banner { color: #222222; top: 0; left: 5px; bottom: 10px; right: 5px; padding-left: 20px; padding-right: 40px; resize: vertical; transition: color 200ms; } 

如何说服团队开始编写offset-inline-start而不是left?


没办法 但是我们决定在我们的项目中简化它-写start: 0而不是offset-inline-start: 0 ,只要每个人都习惯了,我就会开始施加一个有效的条目:)


RTL + CSS-in-JS =️️<3


CSS-in-JS不需要预先组装。 这意味着在运行时可以确定组件的方向,并选择要翻转的组件和不翻转的组件。 如果您需要插入一些根本不支持RTL的小部件,则很有用。


通常,任务是将{ paddingInlineStart: '4px' }类型的对象(如果无法切换到逻辑属性,则将{ paddingLeft: '4px' }转换为{ paddingRight: '4px' }类型的对象:


  1. 装备bidi-css-jsrtl-css-js 。 它们提供了一个接受样式对象并返回转换为所需方向的函数。
  2. ???
  3. 赢利!

反应实例


将每个样式化的组件包装在可以接受样式的HOC中:


 export default withStyles(styles)(Button); 

他从上下文中获取组件的方向,然后选择最终样式:


 function withStyles(styles) { const { ltrStyles, rtlStyles } = bidi(styles); return function WithStyles(WrappedComponent) { ... render() { return <WrappedComponent {...this.props} styles={this.context.dir === 'rtl' ? rtlStyles : ltrStyles} />; }; }; ... }; } 

提供者将重点放在上下文上:


 <DirectionProvider dir="rtl"> ... <Button /> ... 

Airbnb使用类似的方法: https : //github.com/airbnb/react-with-styles-interface-aphrodite#built-in-rtl-support ,如果项目中已经使用了阿芙罗狄蒂 ,则可以使用此现成的解决方案。


对于JSS,它更容易-您只需要启用jss-rtl即可


 jss.use(rtl()); 

样式化的组件


 const Button = styled.button` background: #222; margin-left: 12px; `; 

如果我们使用模板字符串而不使用对象怎么办? 一切都很复杂,但是有一个解决方案-从props指定的方向计算属性名称:


 const marginStart = props => props.theme.dir === "rtl" ? "margin-left" : "margin-right"; const Button = styled.button` background: #222; ${marginStart}: 12px; `; 

但是从线条切换到对象似乎更容易, 自3.3.0版以来 ,样式化组件就可以执行此操作


翻译和本地化功能


我们找出了技术部分。 他们隔离了不确定方向的内容,镜像的样式,在适当的位置放置了例外,然后将文本翻译成阿拉伯语。 一切似乎准备就绪-切换语言时,整个界面出现在屏幕的另一侧,没有布局,并且一切看上去比任何阿拉伯语站点都好。


我们展示真正的阿拉伯人,并...


事实证明,并非每位讲阿拉伯语的人都知道Twitter是什么。 这几乎适用于所有英语单词。 对于这种情况,存在阿拉伯语音译:“تويتر”。


事实证明,在阿拉伯语中有逗号,并且代码中我们到处都是通过“,”连接的事实,在阿拉伯语中,我们需要通过“,”进行连接。


事实证明,在某些穆斯林国家,官方日历为伊斯兰教。 是月球,通常的翻译公式是必不可少的。


事实证明,在迪拜没有负温度,+ 40预测中的加号没有任何意义。


不只是挑选和反映样式


如果我们在block元素上执行dir="auto" ,并且其内容原来是LTR,则文本将跳到容器的左侧,即使它位于RTL附近。 这可以通过显式设置text-align: right 。 您甚至可以将其应用于阿拉伯语版本的整个页面-该属性的值是继承的。


图标也不会自动镜像。 否则,方向性图标(例如画廊中的箭头)的方向可能会错误。 想象一下,这是唯一通过边界制作的箭头证明自己合理的情况!


一个简单的转换将有助于反映图标:


 [dir="rtl"] .my-icon { transform: scaleX(-1); } 

是的,如果图标包含字母或数字,将无济于事。 然后,您必须制作两个不同的图标并有条件地粘贴它们:


图片


然而,事实证明并非所有接口元素都需要镜像。 例如,在我们的案例中,我们决定将复选框保留为正常状态:


图片


图片


我不知道如何从整个界面中选择此类元素。 只有说母语的人才能在这里提供帮助,他们会说出他所熟悉的和不熟悉的。


用户输入


即使我们完全控制了应用程序的所有数据,也可以由用户输入。 例如,文件名。 您甚至可以设计并以.png格式发布.js文件-Telegram中存在此类漏洞:


cool_picture * U + 202E * gnp.js→cool_picture sj.png

在这种情况下,值得从字符串中过滤掉不合适的utf字符。


翻转脚本


语法在RTL javascript中有所改变。 看起来像这样的循环:


 for (let i = 0; i < arr.length; i++) { 

现在您需要这样写:


 for (++i; length.arr > i; let 0 = i) { 

开个玩笑


您要做的就是避免代码中的“左”和“右”概念。 例如,我们在计算屏幕中心的坐标时遇到了问题-在所有卡悬挂在左侧之前,现在悬挂在右侧,但是应用程序代码对此一无所知。 所有计算和内联样式都必须考虑到基本重点。


快速机智


在某些情况下,很难在系统的某些组件中实现RTL支持。 然后,您需要尝试从外部使该组件适应RTL,但将LTR留在内部。


例如,我们有一个滑块。 它仅支持正序值。 它可以是线性和对数的(在开始时,值的密度小于在结束时的值)。 您需要在保持比例尺行为的同时进行反映。


图片


您可以使用以下transform: scaleX(-1)来翻转滑块transform: scaleX(-1) 。 然后,您必须用鼠标(单击和拖动)相对于滑块的中心反转工作。 不好的选择。


还有另一个选项-沿另一个方向旋转轴,仅更改滑块中值的传递和接收。 如果这是线性比例,则将传递值[N-1000,N-100,N-10]而不是值[10、100、1000]的集合,然后在处理程序中将其转换回去。 对于对数标度,我们传递[1/1000,1/100,1/10]而不是[10,100,1000]:


 function flipSliderValues(values, scale, isRtl) { if (!isRtl) { return values; } if (scale === 'log') { // [A, B] --> [1/B, 1/A] return values.map(x => 1 / x).reverse(); } // [A, B] --> [MAX-B, MAX-A] return values.map(x => Number.MAX_SAFE_INTEGER - x).reverse(); }; 

尽管他本人并不知道,这就是滑块开始支持RTL的方式。


故事书


与某些IE9上的排版相比,您无需运行单独的浏览器即可在RTL下验证排版。 您甚至可以在一个窗口中排版并同时查看LTR和RTL的布局。 为此,例如,您可以在Storybook中制作一个装饰器,该装饰器可一次渲染两个版本的故事:


图片


屏幕快照显示没有隔离text-overflow: ellipsis行为不符合我们的期望-最好立即修复。


在布局期间立即支持RTL比稍后对整个项目进行绝对测试要容易得多。


未解决的问题


理论知识无助于解决所有问题。 我举一个例子。


键入时在文本方向上出现的提示。 当我们谈论多语言输入时,我们无法预先知道显示该提示的方向:




您需要在设计阶段尝试避免此类问题,并有时放弃对LTR显而易见且不适用于RTL的解决方案。 在这种情况下,在浏览提示时,您可以替换整个文本(例如,Yandex或Google这样做)。


结论


RTL不只是“扭转一切”


有必要考虑到语言的特殊性,您不需要翻转某些东西,而需要改编其他东西。 在逻辑上的某个地方,绝对有必要放弃“右” /“左”。


不懂语言是很难做的


您会认为一切准备就绪,直到向真正的母语人士展示您的项目为止。 从一个不懂任何语言的人的角度发展。 毕竟,即使是对于您来说如此明显的单词,例如“ Twitter”,也可能需要翻译。 事实证明,标点符号在整个星球上并不相同。


最终配方


很难在一个列表中描述两篇文章中讨论的所有内容。 不会进行演练,但是主要要做的是:


  • 确保找到以英语为母语的人,并尽早向他们展示原型;
  • 将LTR和RTL的样式收集到不同的文件中。 为此,rtlcss和webpack-rtl-plugin是合适的;
  • 为不需要上交的所有内容添加例外,并清楚反映未上交的内容;
  • 使用<bdi>dir="auto"隔离所有任意内容;
  • 显式设置text-align到整个页面;
  • 意思是开始和结束时,请避免在js代码中left / right

准备在最小的细节上花费最多的时间。


提前准备并不难


对于那些还不打算使站点适应RTL但想要吸管的人的一些提示:


  • 请勿将direction属性用于其他目的;
  • 以防万一,仍然隔离所有任意内容(实际上,即使是在英文界面中,用户也可以用阿拉伯语写东西并破坏所有内容);
  • 如果可能,请使用逻辑css属性
  • 至少出于好奇,不仅在不同的浏览器中,而且有时在RTL中检查布局。 使用故事书之类的工具更好地不受干扰地在RTL下控制布局;
  • 不允许使用硬编码语言构造(例如,用逗号分隔的字符串连接),如果可能,请配置所有内容,包括标点符号。 这不仅对RTL有用,例如,在希腊语中,问号是“;”。

这些规则不应该麻烦。 但是,如果突然想要启动RTL版本,它会比它便宜得多。

Source: https://habr.com/ru/post/zh-CN416635/


All Articles