1C de derecha a izquierda: cómo admitimos RTL en la plataforma 1C: Enterprise

Plataforma 1C: Enterprise está traducida a 22 idiomas , incluidos inglés, alemán, francés, chino y vietnamita. Recientemente, en la versión 8.3.17, admitimos el idioma árabe.



Una de las características del idioma árabe es que el texto se escribe y se lee de derecha a izquierda. La interfaz de usuario para el idioma árabe debe reflejarse horizontalmente (pero no todo y no siempre, hay sutilezas aquí), abrir el menú contextual a la izquierda del cursor, etc.



Debajo del corte: sobre cómo admitimos RTL (de derecha a izquierda) en el cliente web de la plataforma 1C: Enterprise, y también una de las hipótesis que explican por qué el mundo árabe escribe de derecha a izquierda.



imagen



Un poco de historia



Estamos acostumbrados a escribir de izquierda a derecha. Esta dirección de escritura se genera en gran medida por el hecho de que al escribir texto en papel, los diestros (y según las estadísticas, alrededor del 85% de ellos) ven lo que ya se ha escrito: la mano que escribe (derecha) no cubre el texto escrito. Los zurdos tienen que sufrir.



Una de las hipótesis de "por qué el idioma árabe se escribe de derecha a izquierda" suena así. Las lenguas de las que se origina el árabe se originaron en aquellos días en que no existía el papel y sus análogos (papiro, pergamino, etc.). Solo había una forma de registrar información: grabar letras en piedra. ¿Y cómo será más conveniente para los diestros empuñar un martillo y un cincel? Por supuesto, con un cincel en la mano izquierda y golpeándolo con un martillo en la derecha. Y en este caso es más conveniente escribir de derecha a izquierda.



Y ahora, sobre cómo lidiamos con este legado de siglos.



¿Cómo comenzamos con la tarea?



Ninguno de los desarrolladores de la plataforma hablaba árabe y no tenía experiencia en el desarrollo de interfaces RTL. Hemos recopilado muchos artículos sobre el tema de RTL (quiero agradecer especialmente a la compañía 2GIS por el trabajo realizado y los artículos cuidadosamente elaborados: artículo 1 , artículo 2 ). Mientras estudiaba el material, me di cuenta de que no podíamos prescindir de un hablante nativo. Por lo tanto, simultáneamente con la búsqueda de traductores al árabe, comenzamos a buscar un empleado: un hablante nativo del idioma árabe, que tuviera la experiencia que necesitábamos, que pudiera asesorarnos sobre los aspectos específicos de las interfaces en árabe. Después de revisar varios candidatos, encontramos a una persona así y nos pusimos manos a la obra.



Juguemos con las fuentes



De forma predeterminada, utilizamos la fuente de plataforma Arial, 10pt. El desarrollador de una configuración específica puede cambiar la fuente para la mayoría de los elementos de la interfaz, pero, como muestra la práctica, esto rara vez se hace. Aquellos. en la mayoría de los casos, los usuarios de programas 1C ven inscripciones escritas por Arial en sus pantallas.



Arial muestra bien 21 idiomas (incluidos el chino y el vietnamita). Pero, como resultó gracias a nuestro colega árabe, el texto árabe representado en esta fuente es muy pequeño y difícil de leer:



100%:



imagen



los usuarios árabes tienden a trabajar con un DPI aumentado: 125%, 150%. La situación mejora en este DPI, pero Arial sigue siendo difícil de leer debido a la naturaleza de la fuente.



125%:



imagen



150%:



imagen



Analizamos varias opciones para resolver este problema:



  1. Arial , , ( ).
  2. Arial 11pt RTL-.
  3. Arial , LTR- Arial.


Al elegir una solución, tuvimos que tener en cuenta que la fuente Arial 10pt se ha utilizado en la plataforma 1C: Enterprise durante mucho tiempo, en la plataforma nosotros y nuestros socios hemos creado más de 1300 soluciones de edición, y en todas ellas la fuente Arial 10pt se mostró bien en todos los sistemas operativos compatibles (Windows, Linux y macOS de varias versiones), así como en navegadores. Cambiar la fuente y / o su tamaño significaría pruebas masivas de la interfaz de usuario, y muchas de estas pruebas no se pueden automatizar. Cambiar la fuente también significaría cambiar la interfaz familiar de programas para los usuarios actuales.



Además, no pudimos encontrar una fuente universal que representara bien todos los idiomas, incluido el árabe. Por ejemplo, la fuente Segoe UI se reproduce bien en árabe incluso a 10 puntos, pero no es compatible con el chino y tampoco es compatible con algunos sistemas operativos. Tahoma es bueno para representar texto árabe a 10 puntos, pero tiene problemas con el soporte de Linux y el latín / cirílico "demasiado atrevido" para el negrita (el negrita árabe se ve bien). Etc.



Aumentar el tamaño de fuente predeterminado a 11 puntos en la interfaz RTL significaría una gran cantidad de pruebas de interfaz de usuario: tenemos que asegurarnos de que todo se procese correctamente, que todas las etiquetas se coloquen en el espacio que se les proporciona, etc. E incluso a 11 puntos, Arial no muestra los caracteres árabes a la perfección.



Como resultado, la tercera vía resultó ser óptima en términos de costos laborales y el efecto logrado: seguimos usando Arial para todos los caracteres excepto el árabe. Y para los caracteres árabes, utilizamos una fuente adecuada para esto: Almarai . Para hacer esto, agregue al 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;
}


y luego, siempre que necesite usar la fuente predeterminada, configure la fuente de esta manera:



font-family: 'Almarai', Arial, sans-serif;


La belleza de este enfoque es que si no hay un solo carácter en la interfaz que se encuentre dentro del rango Unicode, esa fuente ni siquiera se cargará. Pero tan pronto como aparezca dicho símbolo, el navegador descargará la fuente en sí (o usará su versión local) y mostrará el símbolo en la fuente deseada.



Interfaz "Flip"



Como era de esperar, el diseño HTML del cliente web no estaba listo para un cambio. Después de dar el primer paso, estableciendo el atributo dir = ”rtl” en el elemento raíz y agregando el estilo html [dir = rtl] {text-align: right;} , comenzamos el trabajo minucioso. En el transcurso de este trabajo, hemos desarrollado una serie de prácticas que queremos compartir aquí.



Simetría



Veamos el ejemplo de botones. Los botones de la plataforma pueden contener una imagen, texto y un marcador de lista desplegable. Y todo ello en cualquier composición a criterio de los desarrolladores de soluciones de aplicaciones basadas en la plataforma.



La columna "antes de RTL" representa gráficamente el relleno inicial de los elementos del botón. Es obvia la dependencia de la cantidad de sangrías de la presencia de elementos en el botón, así como de la secuencia de su disposición. Si hay una imagen, entonces el texto no necesita una sangría izquierda, si la imagen está a la derecha, entonces la imagen tiene un desplazamiento negativo, si hay un marcador en la lista desplegable, el contenedor con el texto tiene más sangría a la derecha, si el marcador está inmediatamente después de la imagen, todavía tiene una sangría a la derecha. Demasiados if, excepto por un botón de solo texto con relleno simétrico. ¡Simétrico! Si distribuye las sangrías simétricamente, entonces no hay nada que invertir. Esta se convirtió en la idea principal.



La columna "después de RTL" muestra las nuevas sangrías simétricas en los mismos botones. Queda por resolver el matiz con la sangría entre la imagen y el marcador de lista. Quería una solución universal para cualquier orientación. El triángulo en sí se dibuja con el borde superior en el pseudoelemento, y solo necesita una sangría si está después de la imagen. Bajo esta condición, se agrega otro pseudo-elemento con el ancho de la sangría requerida. El triángulo y el relleno se intercambiarán cuando se cambie la orientación.



imagen



Nota. Todos los ejemplos a continuación son predeterminados para la interfaz LTR. Para ver cómo se ve el ejemplo en la interfaz RTL, cambie dir = "ltr" por 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>


Intentamos evitar elementos, pseudoelementos y envoltorios innecesarios. Pero, eligiendo en este caso entre aumentar las condiciones en CSS y agregar un pseudo-elemento, la solución con un pseudo-elemento ganó debido a su versatilidad. No hay muchos botones de este tipo en el formulario, por lo que el rendimiento al agregar elementos no se verá afectado incluso en Internet Explorer.



El principio de simetría también ha resultado útil al desplazarse por nuestros paneles. Para mover el contenido horizontalmente, aplicamos previamente una propiedad única margin-left: -Npx; ...



imagen



El valor ahora se establece en margen simétrico : 0 -Npx; , es decir para izquierda y derecha a la vez, y dónde moverse: el navegador mismo lo sabe, según la dirección especificada.



Clases atómicas



Una de las capacidades de nuestra plataforma es la posibilidad de cambiar dinámicamente el contenido y su ubicación en el formulario "sobre la marcha" según el gusto de cada usuario. Un caso común de cambios es la alineación del texto horizontalmente: izquierda, derecha o centro. Esto se logra simplemente alineando text-align con el valor apropiado. Una reversión para RTL significaría expandir las condiciones en scripts y estilos para cada control y para cada caso de su posicionamiento. La solución mínima cuesta 4 líneas:



.taStart {
    text-align: left;
} 
html[dir=rtl] .taStart {
    text-align: right;
}
.taEnd {
    text-align: right;
}
html[dir=rtl] .taEnd {
    text-align: left;
}


Así, en los lugares necesarios, la clase se instala con la alineación necesaria y su fácil reemplazo si es necesario. Solo queda reemplazar la configuración de alineación con style = "text-align: ..." con la clase apropiada.



El mismo principio se utiliza para establecer otro tipo de alineación: flotación .



.floatStart {
    float: left;
} 
html[dir=rtl] .floatStart {
    float: right;
}
.floatEnd {
    float: right;
}
html[dir=rtl] .floatEnd {
    float: left;
}


Y, como sin ella, una clase para duplicar, por ejemplo, iconos, que también se instala en cualquier contenedor que necesite duplicación en la interfaz RTL.



html[dir=rtl] .rtlScale {
    transform: scaleX(-1);
}


Anti escala



Habiendo tratado con los elementos lineales "simples", es hora de pasar a los "complejos". También hay algunos en nuestra plataforma, por ejemplo, interruptores de palanca. Pueden tener diferentes formas geométricas. El navegador hizo frente a la disposición de los elementos, las sangrías en nuestros interruptores de palanca son inicialmente simétricas. ¿Entonces, cuál es el problema? El problema está en el redondeo de los marcos.

El redondeo de los marcos se calcula para cada elemento de interruptor de palanca, dependiendo de su posición. "Izquierda-arriba", "derecha-arriba", "derecha-arriba y derecha-abajo": las variaciones son diferentes.



Puede voltear todo el contenedor con el interruptor de palanca, pero ¿qué pasa con el texto que también se voltea? A esta técnica la llamamos "anti-escala" . Agregue la clase atómica rtlScale al contenedor que debe reflejarsey agregue la propiedad de transformación a su elemento hijo : heredar; ... En la interfaz LTR, este método se ignorará, pero para la interfaz RTL, el texto, volteado dos veces, se mostrará según sea necesario.



imagen



<!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>


Flexbox



Por supuesto, desafortunadamente, no se nos ocurrió esta increíble tecnología, pero con gran placer usamos sus capacidades para nuestros propósitos. Por ejemplo, en el panel de sección. Los botones de desplazamiento de este panel no ocupan espacio; aparecen en la parte superior del panel cuando es posible desplazarse en una dirección u otra. Una implementación bastante lógica de la posición: absoluta; derecha / izquierda: 0; resultó no ser universal, así que lo abandonamos. Como resultado, la solución universal comenzó a verse así: establezca el contenedor principal del botón de desplazamiento en ancho cero para que no ocupe espacio, y la orientación del botón de desplazamiento ubicado al final se cambió a través de flex-direction: row-reverse; ...



imagen



Por lo tanto, el botón al final de la línea se presiona contra el final de la línea del contenedor de ancho cero y se muestra "al revés" sobre el panel.



<!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>


Por cierto, la idea de ancho cero resultó ser útil para resolver otros problemas también. Los elementos desplegables (menús contextuales, listas desplegables, etc.) son ampliamente utilizados en la plataforma. El cálculo de posicionamiento es complejo y sutil, por lo que la duplicación requiere aún más complejidad y sutileza.



imagen



La solución es colocar el menú desplegable en un contenedor de tamaño cero (llamado ancla). El ancla se coloca absolutamente en el punto requerido de la interfaz, y su contenido con su borde inicial se presiona contra el borde inicial del ancla, posicionando el contenido en la dirección deseada.



<!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>


Elementos absolutamente posicionados



Donde no se puede evitar el posicionamiento absoluto de elementos ( estilo = ”posición: absoluta;” o estilo = ”posición: fija;” ), dir = ”rtl” es impotente. Un enfoque viene al rescate cuando la coordenada horizontal no se aplica al estilo izquierdo , sino al derecho .



imagen



Además, si en JS, al calcular las coordenadas, hay un atractivo para las propiedades scrollLeft y offsetLeft de los elementos, entonces, en la interfaz RTL, el uso de estas propiedades directamente puede tener consecuencias inesperadas. Debe calcular el valor de estas propiedades de una manera diferente. La implementación de esta funcionalidad en la biblioteca de cierre de Google, que usamos en el cliente web, ha demostrado su eficacia: ver.https://github.com/google/closure-library/blob/master/closure/goog/style/bidi.js .



Finalmente



¡Lo hicimos! Volcamos y guardamos nuestro código fuente en una sola versión para interfaces LTR y RTL. La necesidad aún no ha surgido, pero si lo desea, podemos mostrar dos formas de direcciones diferentes simultáneamente en una página. Y por cierto, usando nuestras técnicas, terminamos con el archivo CSS final un 25% más ligero.



También admitimos RTL en un cliente 1C delgado (nativo) que funciona en Windows, Linux y macOS, pero este es un tema para otro artículo.



All Articles