Introducción a las aplicaciones localizables
A pesar de ser un adjetivo válido y totalmente aceptado por la
R.A.E, como muchos de los lectores ya sabrán, el término
localizable en la acepción en que lo utilizaremos aquí es un
anglicismo, que por casualidades de la vida resulta tener un
aspecto muy castellano, pero que en realidad hace referencia a una
aplicación preparada para adaptar sus contenidos a diferentes
culturas.
Todo desarrollador que haya necesitado distribuir una aplicación en
diferentes países valorará enormemente la infraestructura de
localización proporcionada por Visual Studio y .NET
Framework.
Entonces, ¿dónde está el problema? Lo cierto es que hay que
buscarlo bastante, pero finalmente se encuentra.
Esos diez segundos infernales
Un amigo, ingeniero industrial, me dijo una vez que un ingeniero en
informática es alguien que pierde un mes entero desarrollando una
herramienta para automatizar algo que se hace en un minuto. Pues
sí, tiene toda la razón. Es más, casi diría que ése es precisamente
nuestro trabajo. Y es que ¿por qué hacer algo en diez segundos,
cuando puedes hacerlo en uno? Trataré de ilustrar a qué me
refiero.
Si se está trabajando en una aplicación localizable como la de la
figura 1, y se necesita añadir una nueva opción (por ejemplo, la
del menú principal: "Guardar como"), los pasos necesarios para
cambiar el texto del menú a cada uno de los idiomas disponibles
son:
1. Seleccionar el formulario principal (ya que
las opciones de localización están en el formulario).
2. Buscar la propiedad Language en la ventana de
propiedades.
3. Desplegar el combo con las opciones.
4. Buscar el idioma deseado en la lista (que
incluye los cientos de idiomas disponibles en el sistema).
5. Volver a seleccionar el MenuItem.
6. Adaptar su contenido al idioma
seleccionado.
7. Volver a seleccionar el formulario
principal.
8. Volver a buscar la propiedad Language.
9. Volver a establecer el idioma por defecto
(obviamente mediante la opción Reset de la propiedad, no buscando
de nuevo en la lista).
¡Nueve pasos por cada idioma! Algunos totalmente innecesarios y
otros francamente molestos. Por ejemplo, buscar un idioma en una
lista con cientos de opciones, cuando la inmensa mayoría de
aplicaciones soportan solo dos o tres. O tener que bailar entre el
formulario principal y el control una y otra vez. Está claro que
son aspectos susceptibles de una profunda mejora.
Caso de uso ideal
¿Cuál sería el caso de uso ideal para llevar a cabo la tarea
expuesta anteriormente?
En primer lugar, la selección del idioma debe ser mucho más rápida,
implicando menos pasos. En segundo lugar, sería necesario tener que
escoger únicamente entre los idiomas que la aplicación ya está
utilizando, y no entre todos los disponibles en el sistema. De esta
forma, los pasos podrían reducirse a:
1. Pulsar un botón para cambiar el idioma
2. En una lista que muestre únicamente los
idiomas utilizados en la aplicación, hacer doble clic en el que
buscamos.
3. Adaptar el contenido al idioma
seleccionado
4. Pulsar un botón para cambiar al idioma por
defecto
Si se consigue esto, el proceso quedará reducido a cuatro cómodos y
rápidos pasos.
Extensiones de Visual Studio
Existen dos formas de extender las funcionalidades de Visual
Studio: mediante macros o mediante add-ins. Ambas tienen sus
ventajas y sus inconvenientes, que he tratado de resumir en la
tabla 1.
¿Qué opción es mejor en este caso?
Para poder escoger una de las dos opciones, es necesario analizar
los requerimientos del desarrollo que se va a realizar:
1. Es necesario que la extensión muestre una
interfaz de usuario, con una lista de idiomas, algún botón, etc.
Esto se puede lograr tanto con macros como con add-ins.
2. También se necesita averiguar qué idiomas son
los que el componente editado incluye. Como se verá más adelante,
para esto es necesario explorar elementos de la solución, lo cual
también puede hacerse desde un macro o desde un add-in,
indistintamente.
3. Una vez el usuario haya escogido el idioma que
desea "activar", necesitaremos cambiar la propiedad Language del
componente diseñado. Y he aquí el problema. Dado que los macros se
ejecutan en un hilo diferente al de Visual Studio, desde ellos no
es posible acceder al componente que se está editando en el
diseñador; únicamente a elementos del propio Visual Studio o a
ficheros.
Si sumamos a estos puntos el hecho de que para crear un add-in se
puede utilizar C# como lenguaje de programación (algo que
personalmente prefiero), la elección está clara.
El proyecto
Se crea un nuevo proyecto (en este caso llamado LangSelector), de
tipo "Extensibility (Visual Studio Add-in)". Una vez completado el
asistente, obtendremos una solución como la que muestra la figura
2.
El asistente crea de forma automática los archivos XML de registro
de la extensión (LangSelector.AddIn), por lo que no es necesario
preocuparse de registrarla, al menos en la máquina que se esté
utilizando para el desarrollo. No obstante, es recomendable
consultar la referencia [1] para obtener más información acerca del
registro de add-ins y la referencia [3] para conocer más acerca del
gestor de add-ins de Visual Studio.
El lugar donde se debe introducir código es la clase Connect.cs,
más concretamente en el método Exec, llamado cada vez que se invoca
el add-in, y que inicialmente tiene la forma que se presenta en el
listado 1. Más exactamente, en el punto donde se indica "Do your
stuff here".
Para simplificar la explicación del código, hemos dividido la tarea
completa a desarrollar en varios pasos. Para construir la extensión
completa, copie sucesivamente cada uno de los listados que se
exponen a continuación a partir del punto indicado del método
Exec.
1. Comprobar que es un
documento válido
Es necesario asegurarse de que el editor de Visual Studio tiene un
documento activo. Para ello, se puede utilizar la variable miembro
_applicationObject generada por el asistente, que permite
interactuar con algunos componentes de Visual Studio. Una de las
propiedades de este objeto es ActiveDocument, que proporciona un
enlace al documento actualmente abierto en el editor. Si el valor
de esta propiedad es nulo, no es posible continuar.
La propiedad ActiveDocument.ProjectItem (que será muy utilizada a
partir de ahora) da acceso a una clase que contiene información
sobre el fichero de proyecto del proyecto relacionado con el
documento activo. Inicialmente, y como paso opcional, nos permite
comprobar si el documento tiene cambios y proceder a guardarlo
antes de cambiar el idioma. Todo esto queda reflejado en el listado
2.
2. Obtener la lista de idiomas
disponibles
Cuando un formulario o control es localizable, Visual Studio crea
un fichero de tipo .resx por cada idioma adicional que incluye,
agrupándolos en el Explorador de Soluciones como nodos hijos del
componente. De esta forma, analizando la colección
currentItem.ProjectItems es posible conocer la lista de idiomas
disponibles.
3. Acceder al componente editado
El listado 4 muestra cómo acceder al componente editado en el
diseñador de Visual Studio. Esto no podría hacerse en un macro, ya
que por los motivos explicados anteriomente, host.RootComponent no
sería un IComponent válido. Cabe señalar que una vez se dispone del
componente, no es posible acceder a sus propiedades directamente.
Es necesario hacerlo a través de un objeto
System.ComponentModel.PropertyDescriptor, creado por el método
TypeDescriptor.GetProperties.
4. Preguntar al usuario qué idioma desea utilizar
El listado 5 consulta al usuario qué idioma desea utilizar. Para
ello, se basa en un formulario muy simple que contiene un ListBox y
un par de botones, con el aspecto de la figura 3. Este formulario
ofrece una propiedad llamada Values, que es una colección de
cadenas asociada al ListBox, y devuelve el índice seleccionado a
través de otra propiedad SelectedIndex.
5. Asignar el idioma seleccionado
El último paso, que se presenta en el listado 6, consiste en crear
un objeto de tipo CultureInfo que refleje el idioma seleccionado en
la lista, y asignarlo a la propiedad Language del componente
editado.
Integración en Visual Studio
Una vez que el add-in está compilado y registrado, Visual Studio lo
muestra por defecto como una opción más en el menú "Tools". Es
bastante más cómodo incluirlo en una barra de herramientas, lo que
se puede realizar fácilmente accediendo al menú "Tools" |
"Customize" | "Rearrange Commands", y agregando a la barra de
herramientas deseada un nuevo botón que enlace con el add-in.
Trabajo pendiente
Para completar las aportaciones de este add-in, lo ideal sería
desarrollar otro que automáticamente asigne al componente el idioma
por defecto, sin necesidad de escoger en ninguna lista. Así, volver
al modo de trabajo normal después de retocar los contenidos sería
cuestión de un único clic. Partiendo del ejemplo de este artículo,
desarrollar este segundo add-in es sumamente sencillo.
Conclusión
Pocas cosas hay en Visual Studio que no me gusten, y las que
finalmente encuentro, casi siempre pueden solucionarse mediante
add-ins o macros que, además, ya suelen estar desarrollados y
publicados de forma gratuita. Siguiendo los patrones de trabajo
mostrados en este artículo, podrían desarrollarse muchos otros
add-ins que faciliten sensiblemente nuestra vida de
desarrolladores.