Migración de archivos de internacionalización

Compartir por correo electrónico
Bandera mundial internacional

Durante mi doctorado proyecto de migración, Considero la migración de varios aspectos de la GUI: Visual, de comportamiento y de negocio. Estos elementos son los principales. Cuando perfectamente considerado, se puede migrar el front-end de cualquier aplicación. Pero, nos faltan algunas otras cosas 😄 Por ejemplo, ¿cómo se migran los archivos i18N? En este post, presentaré cómo construir una sencilla herramienta de migración para migrar archivos I18N de .propiedades (utilizado por Java) para .json (utilizado por Angular).

Archivos I18N

Primero, veamos nuestra fuente y nuestro objetivo. Como fuente, tengo varios .propiedades archivos, incluyendo I18N para un proyecto Java. Cada archivo tiene un conjunto de clave/valor y comentarios. Por ejemplo, el EditerMessages_fr.properties es la siguiente:

##########
# Página : Editar
##########

pageTitle=Editor
classerDemande=Demanda
classerDiffusion=Difusión
classerPar=Clásico Par

Y su versión en árabe EditerMessages_ar.properties

#########
# Página : Editor
#########

pageTitle=تحرير
classerDemande=طلب
classerDiffusion=بث
classerPar=تصنيف حسب

Como objetivo, necesito sólo un archivo JSON por idioma. Así, el archivo de la traducción al francés tiene el siguiente aspecto:

{
    "EditerMessages" : {
        "classerDemande" : "Demande",
        "classerDiffusion" : "Diffusion",
        "classerPar" : "Classer Par",
        "pageTitle" : "Editer"
    }
}

Y la versión árabe:

{
    "EditerMessages" : {
        "classerDemande" : "طلب",
        "classerDiffusion" : "بث",
        "classerPar" : "تصنيف حسب",
        "pageTitle" : "تحرير"
    },
}

Para realizar la transformación desde el .propiedades archivo a jsonutilizaremos el MDE. El enfoque se divide en tres pasos principales:

  1. Diseñar un metamodelo que represente la internacionalización
  2. Creación de un importador de archivos de propiedades
  3. Creación de un exportador JSON

Metamodelo de internacionalización

Los archivos I18N son sencillos. Consisten en un conjunto de clave/valores. Cada valor está asociado a un idioma. Y cada archivo puede asociarse a un espacio de nombres. Por ejemplo, en el ejemplo de la introducción, el espacio de nombres de todas las entradas es "EditerMessages". Diseñé un metamodelo para representar todos esos conceptos:

Modelo I18N Méta
Metamodelo I18N

Una vez diseñado el metamodelo, debemos crear un importador que tome .propiedades como entrada y produce un modelo.

Importador de propiedades

Para producir un modelo, primero busco un .propiedades parser sin mucho éxito. Por ello, decidí crear mi propio analizador sintáctico. Dado un archivo correctamente formateado, el analizador sintáctico me proporciona las entradas I18N. Entonces, iterando sobre esta colección, construyo un modelo I18N.

Parser I18N

Para implementar el analizador sintáctico, utilicé el programa Proyecto PetitParser2. Este proyecto pretende facilitar la creación de nuevos analizadores sintácticos. En primer lugar, he descargado la última versión de Moosey he instalado PetitParser utilizando el comando proporcionado en el Readme del repositorio:

Metacello nuevo
    línea de base: 'PetitParser2';
    repositorio: 'github://kursjan/petitparser2';
    cargar.

En mi imagen de Moose, he creado un nuevo analizador sintáctico. Para ello, extendí el PP2CompositeNode clase.

PP2CompositeNode << #CS18NPropertiesParser
    ranuras: { };
    paquete: 'Casino-18N-Model-PropertyImporter'

Luego, definí las reglas de análisis sintáctico. Usando PetitParser2, cada regla corresponde a un método.

Primero, iniciar es el punto de entrada.

inicio
    ^ pares fin

pares analiza las entradas del .propiedades archivos.

pares
    ^ comment optional starLazy, pair , ((newline / comment) star , pair ==> [ :token | token second ]) star , (newline/comment) star ==> [ :token |
      ((OrderedCollection con: token segundo)
           addAll: token tercero;
           yourself) asArray ]

La primera parte de este método (antes de ==>) corresponde a la regla analizada. La segunda parte (después de ==>), a la producción.

La primera parte trata de analizar uno o varios comentario. Luego, analiza una par seguido de una lista de comentarionueva líneapar.

Está claro que este analizador sintáctico no es perfecto y requiere algunas mejoras. Sin embargo, funciona en nuestro contexto. La segunda parte produce una colección (es decir una lista) de los par.

Construcción del modelo I18N

Ahora que podemos analizar un archivo, podemos construir un modelo I18N. Para ello, primero analizaremos cada .propiedades archivo. Para cada archivo, extraemos el idioma y el espacio de nombres basado en el nombre del archivo. Así, EditerMessages_fr.properties es el archivo para el fr lenguaje y el EditerMessages espacio de nombres. Luego, para cada entrada de archivo, instanciamos una entrada en nuestro modelo dentro del espacio de nombres y con el lenguaje correcto adjunto.

importString: aString
    (parser parse: aString) do: [ :keyValue |
        (self model allWithType: CS18NEntry) asOrderedCollection
            detectar: [ :entrada |
                "buscar clave existente en el archivo"
                clave de entrada nombre = claveValor clave ]
            ifOne: [ :entrada |
                "Si ya existe una entrada (en otro idioma, por ejemplo)"
                entrada addValue: ((self createInModel: CS18NValue)
                    nombre: valor keyValue;
                    idioma: currentLanguage;
                    yourself) ]
            ifNone: [
                "Si no existe ninguna entrada"
                (self createInModel: CS18NEntry)
                    namespace: currentNamespace;
                    key: ((self createInModel: CS18NKey)
                        name: keyValue key;
                        tú mismo);
                    addValue: ((self createInModel: CS18NValue)
                        nombre: keyValue valor;
                        language: currentLanguage;
                        yourself);
                    yourself] ]

Tras realizar la importación, obtenemos un modelo con, para cada espacio de nombres, varias entradas. Cada entrada tiene una clave y varios valores. Cada valor está vinculado al idioma.

Exportador JSON

Para realizar la exportación de JSON, utilicé el programa Proyecto NeoJSON. NeoJSON permite crear un codificador personalizado. Para la exportación, primero seleccionamos un idioma. Luego, construimos un diccionario con todos los espacios de nombres:

rootDic := Diccionario nuevo.
    (modelo allWithType: CS18NNamespace)
        seleccionar: [ :namespace | namespace namespace isNil ]
        thenDo: [ :namespace | rootDic at: namespace name put: namespace ].

Para exportar un espacio de nombres (es decir, a CS18NNamespace), defino un codificador personalizado:

escribiendo para: CS18NNamespace customDo: [ :mapeador |
    mapper encoder: [ :namespace | (self constructNamespace: namespace) asDictionary
        ]
    ].
construirEspacioDeNombres: aEspacioDeNombres
    | dic |
    dic := Diccionario nuevo.
    aNamespace containables do: [ :containable |
        (contenible isKindOf: CS18NNamespace)
            ifTrue: [ dic at: containable name put: (self constructNamespace: containable) ]
            ifFalse: [ "debe ser un CS18NEntry"
                dic at: containable key name put: (containable values detect: [ :valor | valor idioma = idioma ] ifOne: [ :valor | valor nombre ] ifNone: [ '' ]) ] ].
    ^ dic

El codificador personalizado consiste en convertir un Espacio de nombres en un diccionario de entradas con las claves de las entradas y sus valores en el idioma seleccionado.

Realice la migración

Una vez diseñados mi importador y mi exportador, puedo realizar la migración. Para ello, utilizo un pequeño script. Crea un modelo de I18N, importa varios .propiedades en el modelo, y exporta las entradas árabes en un archivo JSON.

"Crear un modelo"
i18nModel := CS18NModel new.

"Crear un importador"
importador := CS18NPropertiesImporter new.
importador modelo: i18nModel.

"Importar todas las entradas de la carpeta ".
('D:devmyProject' asFileReference allChildrenMatching: '*.properties') do: [ :fileRef |
    self record: fileRef absolutePath basename.
    importador importFile: fileRef.
].

"exportar el archivo JSON I18N árabe"
'D:/miArchivo-ar.json' asFileReference writeStreamDo: [ :stream |
    CS18NPropertiesExporter nuevo
        modelo: importador modelo;
        stream: stream;
        language: ((importer model allWithType: CS18NLanguage) detect: [ :lang | lang shortName = 'ar' ]);
        exportar
]

Recurso

El metamodelo, el importador y el exportador están disponibles gratuitamente en GitHub.

Más ...

Scroll al inicio