La localización (traducción) de contenidos es un tema muy importante, en ocasiones es el punto clave de los desarrollos, pero en muchas ocasiones no se tiene en cuenta. En mi caso he tenido que pasar por esta situación más de una vez.
Cuando se nos platea la situación siempre pensamos en una configuración inteligente de los contenidos basada en XML, crear un gestor, objetos, eventos, listeners, switch on the fly para cambiar de idioma y muchas ideas súper guays, pero como siempre tenemos al señor tiempo que viene a presionar, por otro lado el cliente ya tiene el curso completo y funcional, lo único que quiere ahora es que su producto esté en X o Y idioma, vamos que eso tiene que estar chupado.
Pero que pasa cuando quien ha desarrollado esos contenidos no pensó en que algún día se localizarían y utilizó mogollón de campos de texto estáticos, por no hablar de imágenes o diseños, pero esa es otra historia.
Bueno, ahora que más o menos he contado un poco el panorama, jejeje vamos a ver para que sirve todo eso, resumiendo, tenemos, por que si, un fla de su padre y de su madre con un montón de campos de texto estáticos distribuidos por símbolos, gráficos, botones, clips de película y en distintos fotogramas de sus líneas de tiempo respectivas, es decir, un verdadero caos del bueno.
Claro, también tenemos un cliente que nos pide para mañana la traducción de ese curso a Chino, Alemán e Italiano.
¿Qué hacemos?
Le decimos al cliente que estamos hasta arriba de trabajo y que no podremos dar prioridad a su trabajo, vamos que no lo vamos a hacer.
Le decimos al cliente que no podemos hacerlo en ese tiempo y que necesitaremos dos semanas por lo menos y que el trabajo será muy costoso.
Le decimos que vale.
Pero que!!!!, ¿cómo que vale? pero este tío está loco, pero si esa tarea es imposible.
Bueno, ahora es donde entra en juego nuestro querido y siempre infravalorado JSFL.
IDEA E HISTORIETA
Tenemos una línea de tiempo y muchos elementos en una biblioteca, pues simplemente tenemos que recorrer todas las líneas de tiempo y encontrar todos los campos de texto estáticos, convertirlos en campos de texto dinámicos, asignarles una variable, leer su contenido y guardarlo en un fichero XML.
Como trabajamos en una súper empresa enorme con múltiples departamentos, contamos con un departamento de traducción y muchos más, nos ponemos en contacto con nuestro amigo Axel del departamento de traducción y le pedimos que nos traduzca el fichero XML a los idiomas que ha pedido el cliente, en unas horas nos lo devuelve. ¡Qué gusto trabajar en esta empresa! todos son tan eficientes que dan ganas de dar el 200% de cada uno.
Bueno, ahora ya tenemos los ficheros XML en Chino, Alemán e Italiano. Pero recordemos, como hemos dicho que trabajamos en una súper empresa en la que todos son la hostia de eficientes, nosotros no vamos a ser menos, así que mientras Axel trabajaba en la traducción del XML, nosotros hemos estado creado el súper lector de XML que simplemente creará variables y asignará valores, para que cuando los textos dinámicos las necesiten ya estén ahí. Así que mira que coincidencia, hemos terminado al mismo tiempo que Axel, con todo el desarrollo que hemos hecho, más esos XML’s ya tenemos el contenido en 3 idiomas y nos ha tomado menos de un día.
QUE NECESITAMOS
INTRODUCCIÓN
La localización (traducción) de contenidos es un tema muy importante, en ocasiones es el punto clave de los desarrollos, pero en muchas ocasiones no se tiene en cuenta. En mi caso he tenido que pasar por esta situación más de una vez.
Cuando se nos platea la situación siempre pensamos en una configuración inteligente de los contenidos basada en XML, crear un gestor, objetos, eventos, listeners, switch on the fly para cambiar de idioma y muchas ideas súper guays, pero como siempre tenemos al señor tiempo que viene a presionar, por otro lado el cliente ya tiene el curso completo y funcional, lo único que quiere ahora es que su producto esté en X o Y idioma, vamos que eso tiene que estar chupado.
Pero que pasa cuando quien ha desarrollado esos contenidos no pensó en que algún día se localizarían y utilizó mogollón de campos de texto estáticos, por no hablar de imágenes o diseños, pero esa es otra historia.
Bueno, ahora que más o menos he contado un poco el panorama, jejeje vamos a ver para que sirve todo eso, resumiendo, tenemos, por que si, un fla de su padre y de su madre con un montón de campos de texto estáticos distribuidos por símbolos, gráficos, botones, clips de película y en distintos fotogramas de sus líneas de tiempo respectivas, es decir, un verdadero caos del bueno.
Claro, también tenemos un cliente que nos pide para mañana la traducción de ese curso a Chino, Alemán e Italiano.
¿Qué hacemos?
- Le decimos al cliente que estamos hasta arriba de trabajo y que no podremos dar prioridad a su trabajo, vamos que no lo vamos a hacer.
- Le decimos al cliente que no podemos hacerlo en ese tiempo y que necesitaremos dos semanas por lo menos y que el trabajo será muy costoso.
- Le decimos que vale.
Pero qué!!!!, ¿cómo que vale? pero este tío está loco, pero si esa tarea es imposible.
Bueno, ahora es donde entra en juego nuestro querido y siempre infravalorado JSFL.
IDEA E HISTORIETA
Tenemos una línea de tiempo y muchos elementos en una biblioteca, pues simplemente tenemos que recorrer todas las líneas de tiempo y encontrar todos los campos de texto estáticos, convertirlos en campos de texto dinámicos, asignarles una variable, leer su contenido y guardarlo en un fichero XML.
Como trabajamos en una súper empresa enorme con múltiples departamentos, contamos con un departamento de traducción y muchos más, nos ponemos en contacto con nuestro amigo Axel del departamento de traducción y le pedimos que nos traduzca el fichero XML a los idiomas que ha pedido el cliente, en unas horas nos lo devuelve. ¡Qué gusto trabajar en esta empresa! todos son tan eficientes que dan ganas de dar el 200% de cada uno.
Bueno, ahora ya tenemos los ficheros XML en Chino, Alemán e Italiano. Pero recordemos, como hemos dicho que trabajamos en una súper empresa en la que todos son la ostia de eficientes, nosotros no vamos a ser menos, así que mientras Axel trabajaba en la traducción del XML, nosotros hemos estado creado el súper lector de XML que simplemente creará variables y asignará valores, para que cuando los textos dinámicos las necesiten ya estén ahí. Así que mira que coincidencia, hemos terminado al mismo tiempo que Axel, con todo el desarrollo que hemos hecho, más esos XML’s ya tenemos el contenido en 3 idiomas y nos ha tomado menos de un día.
QUE NECESITAMOS
Primero un repaso rápido al entorno de Flash, en la imagen que se muestra a continuación podemos ver listados los elementos con los que trabajamos a diario y que para este Comando vamos a necesitar.
- Documento fl.getDocumentDOM()
- Línea de tiempo fl.getTimeline()
- Capas fl.getTimeline().layers
- Biblioteca fl.getDocumentDOM().library
- Panel de Propiedades fl.setProperty(“property”, “value”);
Y ahora si, manos a la obra maestro.
Primero que nada vamos a recorrer la línea de tiempo principal para buscar campos de texto, eso ya nos da pistas sobre lo que tenemos que hacer, veamos, hemos dicho que vamos a recorrer la línea de tiempo principal, entonces esto nos indica que necesitaremos una referencia al documento, esto lo resolvemos fácilmente utilizando el comando:
var oDoc = fl.getDocumentDOM();
Una vez que tenemos una referencia al documento ahora si ya podemos obtener una referencia a la línea de tiempo, esto lo hacemos con el siguiente dódigo:
var oTimeline = oDoc.getTimeline();
Una vez que tenemos una referencia a la línea de tiempo ahora tenemos que obtener una referencia a las capas (layers), esta nos la proporciona el objeto línea de tiempo, lo que nos devuelve es un Array (que guay!!!, de verdad que es súper cómodo trabajar con los Arrays), así que aquí empezaremos a ejecutar bucles for anidados. Utilizando un código similar a este:
var aLayers = oTimeline.layers;
var nTotalLayers = aLayers.length;
for(var i = 0; i < nTotalLayers; i++) { ... }
Ahora que recorremos las capas, …
¿Porqué recorremos las capas?
Muy buena pregunta, un punto para el que hizo esa pregunta. La respuesta es sencilla, recorremos las capas por que para buscar los campos de texto tenemos que buscar en los fotogramas clave y la única forma que tenemos de garantizar una búsqueda completa es recorriendo todos los fotogramas de la línea de tiempo.
Pero qué!!!!
A ver, a ver, lo vas a hacer tú? o tú? o yo, no, lo va a hacer automáticamente Flash por nosotros, así que hay de que preocuparse.
… vamos a utilizar la propiedad frames (fotogramas) del objeto Layer (capa) que también nos devuelve un Array (ahora si que ya no quepo de gozo), para buscar los elementos que tiene ese fotograma, tendríamos un código similar a este:
var oLayer = aLayers[i];
var aFrames = oLayer.frames;
var nTotalFrames = aFrames.length;
for(var j = 0; j < nTotalLayers; j++) { ... }
Con esto llegamos casi al punto que nos interesa, el campo de texto, pero nos falta un pequeño paso, el objeto frame tiene una propiedad muy útil, me refiero a la propiedad elements, esta propiedad curiosamente también nos devuelve un Array (he dicho que me gustan mucho los Arrays, es que son la mar de útiles), el código que utilizaríamos sería algo como lo siguiente:
var oFrame = aFrames[j];
var aElements = oFrame.elements;
var nTotalElements = aElements.length;
for(var k = 0; k < nTotalLayers; k++) { ... }
Ahora si, hemos llegado, después de tres bucles anidados, ya tenemos acceso a los elementos del fotograma, así que simplemente tenemos que preguntar el tipo de elemento y en caso de ser de tipo texto podemos modificar sus propiedades y leer el texto que tiene, el código sería más o menos parecido al siguiente:
var oElement = aElements[k];
if (oElement.elementType == "text") { ... }
Bueno, que en el párrafo de arriba escribí de más, así que ahora tengo que volver a escribir otra vez lo de modificar las propiedades del campo de texto y leer su texto, esto lo haremos con el siguiente código:
oElement.textType = "type";
oElement.variableName = "variable_name";
oElement.getTextString();
Y listo, ya lo tenemos.
Menos mal, ya me estaba cansando.
Hey!!! que puedo leer vuestras mentes, a ver tú y tú, los puntos que os habéis ganado hace rato ahora los habéis perdido.
No, no estoy de acuerdo. Si, me estoy cansando, ¿y qué? es normal, con estos tutoriales tan grandes como quieres conservar la atención tanto tiempo. Si quieres mi atención deberías hacer algo más interactivo, entretenido, divertido, cómico, algo en lo podamos participar y no quedarnos todo el rato leyendo. Pero …
Vale, vale, lo siento. Es que estoy aprendiendo. Pero tomo nota de tus comentario, es más, por ser tan honesto te has ganado 150 puntos. Veis chicos, si todos participaseis cuando menos un poco así …
Ahora veamos el código completo:
//Delcaración de variables
var oDoc;
var oLib;
var SEPARATOR = ";";
var PREFFIX_VAR_NAME = "_root.varCommandTextLocalizable_";
var nCounter;
var sXML;
//Llamada a los métodos de configuración e inicialización del comando
this.config();
this.init();
/**
* @method config
* @description Método encargado de configurar el comando
* @return Void
*/
function config()
{
//Indicamos que no queremos ver el mensaje sobre un script de ejecución demasiado larga
fl.showIdleMessage(false);
//Limpiamos la ventana de salida
fl.outputPanel.clear();
if(fl.getDocumentDOM())
{
//Obtenemos la referencia al documento y a la biblioteca
this.oDoc = fl.getDocumentDOM();
this.oLib = this.oDoc.library;
}
else
{
//En caso que no haya un documento abierto mostrarmo un mensaje de alerta.
alert("Debes tener un documento abierto.");
}
}
/**
* @method init
* @description Método encargado de inicializar el comando
* @return Void
*/
function init()
{
//Delcaración de variables
var aItems = this.oLib.items;
var nTotalItems = aItems.length;
var oItem;
this.nCounter = 1;
//Inicializamos el valor del XML abriendo la etiqueta principal
this.sXML = "<DATA>\n";
//Llamamos al método analizeTimeline para que analice le línea de tiempo principal
this.analizeTimeline();
for (var i = 0; i < nTotalItems; i++)
{
oItem = aItems[i];
if ((oItem.itemType == "movie clip") || ( oItem.itemType == "graphic") || (oItem.itemType == "button") )
{
this.oLib.editItem(oItem.name);
this.analizeTimeline();
}
}
//Finalizamos el valor del XML cerrando la etiqueta principal
this.sXML += "</DATA>";
//Llamada al método createXmlFile
this.createXmlFile();
//Limpiamos la memoria de Flash eliminando las variable que hemos creado
delete aItems, nTotalItems, oItem;
}
/**
* @method analizeTimeline
* @description Método encargado analizar una línea de tiempo para buscar campos de texto
* si encuentra campos de texto, los convierte en dinámicos y les añade
* una variable.
* Toda la información que va encontrando la almacena en la variable sXML
* @return Void
*/
function analizeTimeline()
{
//Delcaración de variables
var oTimeline = this.oDoc.getTimeline();
var aLayers = oTimeline.layers;
var nTotalLayers = aLayers.length;
var oLayer;
//Recorremos las capas
for (var j = 0; j < nTotalLayers; j++)
{
oLayer = aLayers[j];
oLayer.locked = false;
oLayer.visible = true;
if ((oLayer.layerType != "guide") || (oLayer.layerType != "folder"))
{
//Delcaración de variables
var aFrames = oLayer.frames;
var nTotalFrames = aFrames.length;
var oFrame;
var aElements;
//Recorremos los fotogramas de la capa
for (var k = 0; k < nTotalFrames; k++)
{
oFrame = aFrames[k];
if (oFrame.elements.length > 0)
{
aElements = oFrame.elements;
//Delcaración de variables
var nTotalElements = aElements.length;
var oElement;
//Recorremos los elementos del fotograma
for (var l = 0; l < nTotalElements; l++)
{
oElement = aElements[l];
if (oElement.elementType == "text" && oElement.textType != "input")
{
//Delcaración de variables
var sDataItem = "Nombre de la timeline: " + oTimeline.name;
var sDataLayer = "Nombre de la capa: " + oLayer.name;
var sDataFrame = "Número de fotograma: " + (k + 1);
var sDataElement = "Nombre del elemento: " + oElement.name;
var sElementText = "Texto del elemento: " + oElement.getTextString();
var sElementFormat = "Formato del elemento: " + "";
//fl.trace(sDataItem + this.SEPARATOR + sDataLayer + this.SEPARATOR + sDataFrame + this.SEPARATOR + sDataElement + this.SEPARATOR + sElementText);
oElement.textType = "dynamic";
oElement.variableName = this.PREFFIX_VAR_NAME + this.nCounter;
this.sXML += " <node varName=\"" + this.PREFFIX_VAR_NAME + this.nCounter + "\">\n";
this.sXML += " <![CDATA[" + oElement.getTextString() + "]]>\n";
this.sXML += " </node>\n";
this.nCounter++;
}
}
//Limpiamos la memoria de Flash eliminando las variable que hemos creado
delete nTotalElements, oElement;
}
//Incrementamos el valor de k con la duración del fotograma, esto es para que en el siguiente
//ciclo del bucle vayamos director al siguiente fotograma clave
k += oFrame.duration - 1;
}
//Limpiamos la memoria de Flash eliminando las variable que hemos creado
delete aFrames, nTotalFrames, oFrame, aElements;
}
this.oDoc.exitEditMode();
}
//Limpiamos la memoria de Flash eliminando las variable que hemos creado
delete oTimeline, aLayers, nTotalLayers, oLayer;
}
/**
* @method createXmlFile
* @description Método encargado de crear el fichero XML con la relación entre textos y variables
* @return Void
*/
function createXmlFile()
{
//Declaración de variables
var sPath = this.oDoc.pathURI.split(this.oDoc.name).shift();
var sFileURI = sPath + "textos_extraidos_automaticamente.xml";
//Comprobación, si existe el fichero lo eliminamos
if (FLfile.exists(sFileURI))
FLfile.remove(sFileURI);
//Creamos el fichero XML
FLfile.write(sFileURI, this.sXML);
//Limpiamos la memoria de Flash eliminando las variable que hemos creado
delete sPath, sFileURI;
}
Bueno, en este código encontramos además de lo explicado en este tutorial para recorrer las líneas de tiempo, modificar propiedades de campos de texto y extraer textos, código para crear ficheros de texto, en el caso del ejemplo crear un fichero XML con la relación entre la variable asignada al campo de texto y el texto.
El código está comentado, pero si hace falta alguna explicación, si tenéis alguna duda, comentario, mejora, sugerencia, pues aquí estaré gustoso de seguir compartiendo.
DESCARGAS
Como es habitual dejo descargas del fichero JSFL, también el fichero MXP instalable y un zip con el paquete del JSFL y un FLA de ejemplo para extraer los textos.
- Comando Extractor de Textos (versión JSFL).
- Instalador de Comando Extractor de Textos.
- Paquete con JSFL y FLA de ejemplo.
Saludos!!!
NOTAS
Se que algunas personas tendrán mucho que decir, así que por favor háganlo, el tema es complicado, la localización de contenidos es algo muy chungo en ocasiones tal como lo plateo en este ejemplo.
También es cierto que con este ejemplo no salvamos otros obstáculos tales como imágenes que tienen textos y que con la solución de modificar los campos de texto podríamos estropear el funcionamiento de algunos campos de texto que ya fuesen dinámicos y que utilizaran variables.
Ahora si, después de estas notas y una última aclaración, mi objetivo es explicar el funcionamiento de los objetos de JSFL, el ejemplo se presta para sacarle partido al lenguaje, así que por eso lo he desarrollado.
Entradas relacionadas