sql >> Base de Datos >  >> RDS >> Database

Diseño de bases de datos para aplicaciones multilingües

Si bien algunos sistemas de software son utilizados por un número limitado de usuarios que hablan el mismo idioma, la mayoría de las organizaciones necesitan unificar y centralizar sus aplicaciones para que las utilicen personas que hablan diferentes idiomas en todo el mundo. Las bases de datos multilingües presentan un nivel adicional de dificultad en el diseño e implementación de modelos de datos. En este artículo, sugerimos algunos enfoques para enfrentar este desafío.

¿Qué información necesitamos almacenar en varios idiomas?

En la superficie, toda la información de cadenas puede parecer plausible para traducir a varios idiomas. Sin embargo, este no suele ser el caso. Información relacionada con el cliente como CompanyName o Address puede traducirse, pero puede que no sea una buena idea.

Tome un cliente comercial en el Reino Unido llamado "Riverside Trucks" con una oficina en "123 Upper Castle Road". No desea que un usuario de habla hispana imprima y envíe una carta a "Camiones Orilla" ubicado en "123 Calle Castillo Superior". ¡Royal Mail (el servicio postal del Reino Unido) no lo encontrará! Probablemente desee traducir solo las columnas que contienen información descriptiva, no los nombres propios.

Cuando se diseña un sistema para manejar traducciones, no siempre se sabe de antemano exactamente qué columnas son traducibles y cuáles no requieren traducción. Elegir un enfoque flexible ahorra mucho tiempo en diseño y desarrollo. Eche un vistazo al artículo "Cómo diseñar un sistema listo para la localización" para ver algunos ejemplos.

¿Qué enfoques consideramos?

En este artículo, describimos tres enfoques para el diseño de bases de datos multilenguaje. Comenzamos con la más simple que no es tan flexible y luego pasamos a considerar otras opciones, explicando los pros y los contras de cada una.

Tanto la sintaxis como los modelos de base de datos (disponibles en el modelador de datos basado en web de Vertabelo) utilizados en este artículo son para SQL Server. Sin embargo, se adaptan fácilmente a cualquier motor de base de datos.

Enfoque 1:Creación de columnas adicionales para contener contenido traducido

Este es el enfoque más simple de implementar, aunque no es muy flexible. Consiste en agregar una columna por cada columna e idioma que necesitemos usar en nuestro sistema, como se muestra en el siguiente diagrama de Vertabelo:

Si bien esto puede parecer una solución muy simple, tiene algunos inconvenientes. Te lo explicamos a continuación.

Desventajas:Complejidad del código

Este enfoque hace que el código sea más complejo. Requiere que escribamos una consulta diferente para cada idioma o usemos un CASE construir para recuperar la traducción del idioma adecuado en función de la configuración del usuario. Consulte el siguiente código, por ejemplo:

SELECT ProductID,
    CASE @Language WHEN ‘ES’ THEN ProductName_ES
                   WHEN ‘DE’ THEN ProductName_DE
                   WHEN ‘FR’ THEN ProductName_FR
                   ELSE ProductName
    END AS ProductName,
    CASE @Language WHEN ‘ES’ THEN ProductDescription_ES
                   WHEN ‘DE’ THEN ProductDescription_DE
                   WHEN ‘FR’ THEN ProductDescription_FR
                   ELSE ProductDescription
    END AS ProductDescription,
    Price,
    Weight,
    ProductCategoryID
FROM Product
WHERE …

Nota: En el ejemplo, usamos la variable @Language para mantener el idioma que queremos usar. Puede considerar usar SESSION_CONTEXT() (o Application Context en Oracle) para establecer y leer el idioma de cada usuario.

Con:falta de flexibilidad

Este enfoque carece de flexibilidad. Si necesitamos implementar un nuevo idioma, debemos modificar nuestro modelo de datos agregando una columna para el nuevo idioma para cada columna traducible en nuestro sistema. También necesitamos crear una nueva consulta de idioma para cada tabla (o editar la existente agregando un nuevo CASE WHEN cláusula que usa el nuevo idioma para cada columna traducible).

Con:Desafíos en el manejo de información desconocida

Imagina esto:un usuario agrega un producto pero no sabe cómo traducirlo y deja las columnas traducidas vacías. Los usuarios que hablan esos idiomas ven NULL o información en blanco en las columnas que puedan ser requeridas.

Enfoque 2:aislar columnas traducibles en una tabla separada

Este enfoque agrupa las columnas de una tabla en columnas traducibles y no traducibles. Las columnas no traducibles permanecen en la tabla original. En cambio, los traducibles están en una tabla aparte, con una clave ajena a la tabla original y un indicador de idioma. Ver a continuación:

El diagrama muestra las tablas originales (sin datos traducibles) en blanco. Las tablas que contienen las traducciones están en azul claro y la tabla maestra que contiene la información del idioma está en amarillo.

Esto tiene enormes ventajas de flexibilidad en comparación con el uso de múltiples columnas como se discutió anteriormente. Este método no requiere cambiar el modelo de datos cuando se necesita un nuevo idioma. Además, la sintaxis para consultar la información es más sencilla:

SELECT p.ProductID,
    pt.ProductName,
    pt.ProductDescription,
    p.Price,
    p.Weight,
    p.ProductCategoryID
FROM Product p
LEFT JOIN ProductTranslation pt ON pt.ProductID = p.ProductID
                               AND pt.LanguageID = @Language
WHERE …

Sin embargo, todavía hay algunas desventajas, como discutimos a continuación.

Con:Desafíos cuando es necesario traducir columnas adicionales

Si necesitamos convertir una columna no traducible en una traducible (o viceversa), debemos modificar nuestro modelo de datos, moviendo la columna de una tabla a la otra. Esto suele implicar mayores costos una vez que el sistema está implementado y en uso.

Con:Desafíos en el manejo de información desconocida

Al igual que el primer enfoque, este enfoque tiene desafíos cuando se trata de información desconocida. Nuevamente, si un usuario agrega un producto pero no sabe cómo traducirlo y deja las columnas traducidas vacías, los usuarios que hablan esos idiomas verán NULL o información en blanco en las columnas que puedan ser requeridas. Además, la consulta requiere un LEFT JOIN en caso de que aún no se haya creado la traducción para el idioma del usuario actual, de modo que aún se muestren los datos no traducibles.

Enfoque 3:agregar un subsistema de traducción

La traducción se puede considerar como una característica que es completamente independiente del modelo de datos que requiere traducción. En un sistema ideal, podemos habilitar o deshabilitar la traducción de cualquier columna sin necesidad de modificar el modelo de datos. Desafortunadamente, es más fácil decirlo que hacerlo.

Presentamos un método que no tiene impacto en el modelo de datos existente y es completamente flexible. Aunque añade complejidad a la hora de consultar los datos, no requiere ningún cambio adicional en el modelo de datos salvo algunas tablas. Esta puede ser una excelente opción si necesita agregar la capacidad de realizar traducciones a un modelo de datos existente.

Echemos un vistazo al modelo y veamos cómo funciona:

Lo primero que debe notar es que el modelo de datos original no tiene ningún cambio. Además, no existe una relación directa entre ese modelo y el subsistema de traducción.

El subsistema de traducción incluye un pequeño diccionario de datos con las tablas y columnas que requieren traducción. Este diccionario de datos se puede modificar simplemente agregando/eliminando filas sin alterar el modelo de datos. Las traducciones se almacenan en una tabla separada, con cada valor identificado por las siguientes 3 columnas:

  • ColumnID :identifica de forma única la columna (y la tabla) que estamos traduciendo.
  • KeyID :Almacena el ID (clave principal) de la fila específica que estamos traduciendo.
  • LanguageID :identifica el idioma de la traducción.

Este diseño permite que los datos se ingresen y almacenen en las tablas originales, agregando traducciones solo cuando sea necesario. La información traducida se usa al recuperar datos, manteniendo intactos los datos originales (en el idioma original).

Los datos se pueden consultar utilizando una sintaxis más compleja que los ejemplos anteriores. Requiere un JOIN adicional para cada columna traducible como se muestra a continuación:

SELECT p.ProductID,
    ISNULL(t1.TranslationValue, p.ProductName) AS ProductName,
    ISNULL(t2.TranslationValue, p.ProductDescription) AS ProductDescription,
    p.Price,
    p.Weight,
    p.ProductCategoryID
FROM Product p
LEFT JOIN Translation t1 ON t1.ColumnID = <>
                       AND t1.Key = p.ProductID
                       AND t1.LanguageID = @Language
LEFT JOIN Translation t2 ON t2.ColumnID = <>
                       AND t2.Key = p.ProductID
                       AND t2.LanguageID = @Language
WHERE …;

Nota:<<ProductName_ColumnID>> ” y “<<ProductDescription_ColumnID>> ” debe reemplazarse con los ID de las columnas que se traducirán tal como están almacenadas en ColumnInformation mesa. Considere generar vistas de traducción para cada tabla que requiera traducción para ocultar la complejidad de los JOIN para los usuarios finales. Incluso puede automatizar este paso con un script que genera cada vista. Este script puede consultar el diccionario de datos de la base de datos para seleccionar las tablas y columnas y agregar la lógica de traducción para las columnas que existen en ColumnInformation mesa.

Consejo adicional n.º 1

También puede simplificar la sintaxis. Reemplace cada JOIN con una llamada a una función que maneje (y oculte) el aspecto de la traducción, como se muestra a continuación:

SELECT p.ProductID,
    ISNULL(fn_translate(‘Product’,‘ProductName’,ProductID), p.ProductName)
         AS ProductName,
    ISNULL(fn_translate(‘Product’,‘ProductDescription’,ProductID),
         p.ProductDescription) AS ProductName,
    p.Price,
    p.Weight,
    p.ProductCategoryID
FROM Product p
WHERE …;

La función puede leer el idioma deseado del contexto, o puede agregarlo como un parámetro adicional. En este ejemplo, la función utiliza los nombres de la tabla y la columna proporcionados como parámetros más la clave de fila (también proporcionada como parámetro) para buscar y devolver la traducción deseada.

Llamar a una función implica un impacto adicional en el rendimiento debido al cambio de contexto entre SQL y el lenguaje de procedimiento. Sin embargo, puede ser una solución más sencilla para bases de datos o tablas donde la cantidad de datos que se traducen lo permita.

Ambos ejemplos, el que tiene JOIN y el que tiene una función, usan la función ISNULL() de SQL Server. Por lo tanto, cuando la traducción al idioma deseado no existe, aún muestra el valor original almacenado en las columnas ProductName y ProductDescription en lugar de espacios en blanco o NULL.

Consideraciones generales

El tercer enfoque suele ser el mejor para implementar diseños más grandes. Permite flexibilidad tanto en el diseño como una vez que el sistema está en uso. Sin embargo, hay consideraciones específicas que pueden hacer útiles los otros enfoques. Independientemente de su elección, considere lo siguiente para ahorrar tiempo tanto en el diseño como en el desarrollo/implementación.

Añadir una capa de abstracción

Como se mencionó anteriormente, considere crear vistas que se ocupen de la lógica de traducción, por ejemplo, seleccionar una columna entre varias columnas de traducción o unirlas a filas específicas. Esto mantiene los detalles de implementación específicos ocultos para los programadores. Simplemente usan estas vistas en lugar de tener que construir sentencias SQL complejas cada vez que necesitan acceder a una tabla con información traducible.

Usar contexto para filtrar datos

Como se mencionó en el primer ejemplo, considere usar funciones de contexto disponibles en la mayoría de los motores de bases de datos. Úselos para almacenar la información del idioma del usuario una vez que haya iniciado sesión en el sistema y luego filtre los resultados automáticamente en las vistas que se encargan de la traducción.

Automatizar

Los sistemas modernos pueden tener cientos e incluso miles de tablas. Tómese el tiempo para automatizar la generación de vistas de traducción en lugar de escribir las consultas una por una. Puede que le lleve algo de tiempo llegar a un script que funcione, pero luego siempre puede crear nuevas vistas o recrear las existentes en menos de un segundo.