阿拉伯语言的特征之一是其中的文本是从右到左书写和读取的。阿拉伯语的用户界面应水平镜像(但不是全部,而且并非总是-这里有一些细微之处),打开光标左侧的上下文菜单,等等。
切入点-关于我们如何在1C:企业平台的Web客户端中支持RTL(从右到左),也是解释阿拉伯世界为何从右向左书写的假说之一。
一点历史
我们习惯于从左到右书写。书写的方向主要是由于以下事实:当在纸上书写文本时,惯用右手(据统计,其中约85%)会看到已经写过的东西-书写(右)手不会覆盖所写的文本。左撇子必须受苦。
假设之一“为什么阿拉伯语是从右到左书写的”听起来像这样。阿拉伯语起源的语言起源于当时没有纸及其类似物(纸莎草纸,羊皮纸等)的时代。记录信息的方法只有一种-用石头雕刻字母。对于惯用右手的人来说,挥锤和凿子会更方便吗?当然,左手拿凿子,右手用锤子敲它。在这种情况下,从右到左书写更加方便。
现在-关于我们如何处理数百年来的遗产。
我们是如何开始这项任务的?
没有平台开发人员讲阿拉伯语,也没有开发RTL接口的经验。我们已经整理了很多关于RTL的文章(我特别要感谢2GIS公司所做的工作,并认真撰写了文章:第1条,第2条)。在学习材料时,我们意识到没有母语的人就做不到。因此,在寻找翻译成阿拉伯语的翻译人员的同时,我们开始寻找一名员工-以阿拉伯语为母语的人,他会拥有我们所需要的经验,可以就界面的阿拉伯语细节为我们提供建议。在审查了几位候选人之后,我们找到了这样的人,开始工作。
让我们玩字体
默认情况下,我们使用平台字体Arial 10pt。特定配置的开发人员可以更改大多数界面元素的字体,但是,如实践所示,很少这样做。那些。在大多数情况下,使用1C程序的用户会在屏幕上看到Arial编写的题词。
Arial会显示21种语言(包括中文和越南文)。但是,事实证明,这要归功于我们的阿拉伯同事,以这种字体呈现的阿拉伯文本很小且难以阅读:
100%:
阿拉伯用户倾向于以更高的DPI工作-125%,150%。这种DPI的情况有所改善,但是Arial仍然由于字体的性质而难以阅读。
125%:
150%:
我们研究了解决此问题的几种方法:
- Arial , , ( ).
- Arial 11pt RTL-.
- Arial , LTR- Arial.
在选择解决方案时,我们必须考虑到Arial 10pt字体在1C:Enterprise平台上使用了很长时间,在我们和合作伙伴创建的平台上,已经有1300多个版本的解决方案,并且在所有支持的OS(Windows,Linux)上,Arial 10pt字体都表现得很好。和各种版本的macOS),以及在浏览器中。更改字体和/或其大小将意味着对用户界面进行大规模测试,并且其中许多测试无法自动化。更改字体还意味着更改当前用户熟悉的程序界面。
而且,我们找不到能很好地代表所有语言的通用字体,包括阿拉伯语。例如,Segoe UI字体即使在10pt时也能很好地呈现阿拉伯语,但是它不支持中文,并且在某些操作系统中也不支持。 Tahoma擅长在10pt处呈现阿拉伯文本,但是在Linux支持和“太粗”的拉丁/西里尔字母对粗体显示方面存在问题(阿拉伯粗体看起来不错)。等等。
在RTL界面中将默认字体大小增加到11pt意味着大量的UI测试-我们必须确保正确呈现所有内容,所有标签都放置在为其提供的空间中,依此类推。即使在11pt,Arial也无法完美显示阿拉伯字符。
结果,第三种方法在人工成本和所达到的效果方面被证明是最佳的:我们继续将Arial用于除阿拉伯语之外的所有字符。对于阿拉伯字符,我们为此使用一种合适的字体-Almarai。为此,添加到CSS:
@font-face {
font-family: 'Almarai';
font-style: normal;
font-weight: 400;
font-display: swap;
src: local('Almarai'),
local('Almarai-Regular'),
url(https://fonts.gstatic.com/s/almarai/v2/tsstApxBaigK_hnnQ1iFo0C3.woff2)
format('woff2');
unicode-range:
U+0600-06FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE80-FEFC;
}
然后在需要使用默认字体的任何地方,以这种方式设置字体:
font-family: 'Almarai', Arial, sans-serif;
这种方法的优点在于,如果接口中没有单个字符落在unicode范围内,则该字体甚至不会加载。但是,一旦出现这样的符号,浏览器就会下载字体本身(或使用其本地版本)并以所需的字体显示该符号。
“翻转”界面
如您所料,Web客户端的HTML布局尚未准备好进行翻转。在执行了第一步之后,在根元素上设置dir =“ rtl”属性,并添加html [dir = rtl] {text-align:right;}样式,我们开始了艰苦的工作。在这项工作的过程中,我们已经开发了一些实践,我们希望在这里分享。
对称
让我们看一下按钮的例子。平台中的按钮可以包含图片,文本和下拉列表标记。基于平台的应用程序解决方案的开发人员可以随意决定所有这些内容。
“ RTL之前”列以图形方式表示按钮元素的初始填充。缩进量对按钮中元素的存在及其排列顺序的依赖性是显而易见的。如果有图片,则文本不需要左缩进,如果图像在右侧,则图像有负移,如果下拉列表中有标记,则带有文本的容器在右边具有更多的缩进,如果标记紧接在图像之后,则在右边还有一个空白。 if太多,除了带有对称填充的纯文本按钮外。对称!如果您对称地分布凹痕,则没有任何可翻转的地方。这成为主要思想。
“在RTL之后”列在相同的按钮上显示了新的对称缩进。仍然需要解决图片与列表标记之间的缩进的细微差别。我想要任何方向的通用解决方案。三角形本身是用伪元素的顶部边框绘制的,并且只有在图片之后才需要缩进。在这种情况下,将使用所需缩进的宽度添加另一个伪元素。更改方向时,三角形和填充自身将互换。
注意。 默认情况下,以下所有示例都是LTR接口的。要查看该示例在RTL界面中的外观,请将dir =“ ltr”更改为dir =“ rtl”。
<!DOCTYPE html>
<html dir="ltr">
<head>
<style>
.button {
display: inline-flex;
align-items: center;
border: 1px solid #A0A0A0;
border-radius: 3px;
height: 26px;
padding: 0 8px;
}
.buttonImg {
background: #A0A0A0;
width: 16px;
height: 16px;
}
.buttonBox {
margin: 0 8px;
}
.buttonDrop {
display: flex;
}
.buttonDrop:after {
content: '';
display: block;
border-width: 3px 3px 0;
border-style: solid;
border-left-color: transparent;
border-right-color: transparent;
}
.buttonImg + .buttonDrop::before {
content: '';
display: block;
width: 8px;
overflow: hidden;
}
</style>
</head>
<body>
<a class="button">
<span class="buttonImg"></span>
<span class="buttonBox"></span>
<span class="buttonDrop"></span>
</a>
<a class="button">
<span class="buttonImg"></span>
<span class="buttonDrop"></span>
</a>
</body>
</html>
我们尝试避免不必要的元素,伪元素和包装器。但是,在这种情况下,在增加CSS的条件和添加伪元素之间做出选择,使用伪元素的解决方案由于其多功能性而获胜。表单上的按钮并不多,因此即使在Internet Explorer中,添加元素时的性能也不会受到影响。
对称原理已被证明对滚动面板也很有用。要水平移动内容,我们先前应用了一个margin-left属性:-Npx; ...
现在将值设置为对称边距:0 -Npx; ,即 一次左右移动以及移动的位置-浏览器本身知道,具体取决于指定的方向。
原子类
我们平台的功能之一就是能够根据每个用户的喜好动态地更改内容及其在表单上的位置。更改的一种常见情况是文本水平对齐:左,右或居中。这可以通过简单地将text-align与适当的值对齐来实现。逆转RTL意味着针对每个控件及其定位的每种情况扩展脚本和样式中的条件。最低解决方案成本为4行:
.taStart {
text-align: left;
}
html[dir=rtl] .taStart {
text-align: right;
}
.taEnd {
text-align: right;
}
html[dir=rtl] .taEnd {
text-align: left;
}
这样,在必要的地方,该类进行了必要的对准,并在必要时易于更换。剩下的只是用适当的类替换style =“ text-align:...”的对齐设置。
使用相同的原理来设置另一种类型的alignment- float。
.floatStart {
float: left;
}
html[dir=rtl] .floatStart {
float: right;
}
.floatEnd {
float: right;
}
html[dir=rtl] .floatEnd {
float: left;
}
并且,没有它,则是用于镜像的类(例如,图标),该类也安装在需要在RTL接口中进行镜像的任何容器中。
html[dir=rtl] .rtlScale {
transform: scaleX(-1);
}
防垢
处理了“简单的”线性元素之后,是时候进行“复杂的”线性元素了。我们的平台中还有一些功能,例如拨动开关。它们可以具有不同的几何形状。浏览器处理了元素的排列,切换开关中的凹痕最初是对称的。所以有什么问题?问题在于框架的舍入。
根据每个拨动开关元件的位置计算框架的舍入。 “左上”,“右上”,“右上和右下”-变体不同。
您可以使用切换开关翻转整个容器,但是文本也会翻转吗?我们称这种技术为“反规模”。将原子类rtlScale添加到需要镜像的容器中,并将转换继承属性添加到其子元素:... 在LTR界面中,将忽略此方法,但对于RTL界面,将根据需要显示翻转两次的文本。
<!DOCTYPE html>
<html dir="ltr">
<head>
<style>
html[dir=rtl] .rtlScale {
transform: scaleX(-1);
}
.tumbler {
display: inline-flex;
border-radius: 4px 0 0 4px;
border: 1px solid #A0A0A0;
padding: 4px 8px;
}
.tumblerBox {
transform: inherit;
}
</style>
</head>
<body>
<div class="tumbler rtlScale">
<div class="tumblerBox"> </div>
</div>
</body>
</html>
弹性盒
当然,不幸的是,我们并没有提出这项惊人的技术,但是我们非常高兴地将其功能用于我们的目的。例如,在部分面板中。该面板的滚动按钮不会占用空间;当可以沿一个方向或另一个方向滚动时,它们会出现在面板顶部。位置的合理逻辑实现:绝对;右/左:0; 原来不是通用的,所以我们放弃了它。结果,通用解决方案开始如下所示:将滚动按钮的父容器设置为零宽度,以使其不占用空间,并通过flex-direction更改位于末端的滚动按钮的方向:row-reverse; ...
因此,将行尾的按钮按在零宽度容器的行尾,并在面板上“向后”显示。
<!DOCTYPE html>
<html dir="ltr">
<head>
<style>
.panel {
display: inline-flex;
background: #fbed9e;
height: 64px;
width: 250px;
}
.content {
width: 100%;
}
.scroll {
display: flex;
position: relative;
width: 0;
}
.scrollBack {
order: -1;
}
.scrollNext {
flex-direction: row-reverse;
}
.scroll div {
display: flex;
flex: 0 0 auto;
justify-content: center;
align-items: center;
background: rgba(255,255,255,0.5);
width: 75px;
}
</style>
</head>
<body>
<div class="panel">
<div class="content"> </div>
<div class="scroll scrollBack">
<div></div>
</div>
<div class="scroll scrollNext">
<div></div>
</div>
</div>
</body>
</html>
顺便说一下,零宽度的想法对于解决其他问题也很有用。平台中广泛使用下拉元素(上下文菜单,下拉列表等)。定位计算是复杂而微妙的,因此镜像需要更高的复杂性和微妙性。
解决方案是将下拉列表放入大小为零的容器(称为锚)中。锚绝对位于接口的所需点,并且其内容及其起始边缘被压向锚的起始边缘,将内容沿所需方向定位。
<!DOCTYPE html>
<html dir="ltr">
<head>
<style>
.anchor {
border: 1px solid red;
position: absolute;
width: 100px;
height: 50px;
max-width: 0;
max-height: 0;
top: 25%;
left: 50%;
}
.anchorContent {
background: #FFF;
border: 1px solid #A0A0A0;
width: inherit;
height: inherit;
padding: 4px 8px;
}
</style>
</head>
<body>
<div class="anchor">
<div class="anchorContent"> </div>
</div>
</body>
</html>
绝对定位的元素
在无法避免元素的绝对定位的情况下(style =“ position:absolute;”或style =“ position:fixed;”),dir =“ rtl”是无能为力的。当水平坐标不应用于左侧样式而应用于右侧样式时,便采取了一种补救措施。
而且,如果在JS中计算坐标时,对元素的scrollLeft和offsetLeft属性具有吸引力,那么在RTL接口中直接使用这些属性会导致意外的后果。您需要以其他方式计算这些属性的值。我们在Web客户端中使用的Google Closure库中的此功能的实现已很好地证明了自己:参见。https://github.com/google/closure-library/blob/master/closure/goog/style/bidi.js。
最终
我们做到了!我们上交了源代码并将其源代码保存在用于LTR和RTL接口的单个版本中。需求尚未出现,但是如果需要,我们可以在一页上同时显示两种形式的不同方向。顺便说一下,使用我们的技巧,我们最终得到了比CSS文件轻25%的结果。
我们还在可在Windows,Linux和macOS上运行的瘦(本机) 1C客户端中支持RTL ,但这是另一篇文章的主题。