Internacionalización de aplicaciones WinForms/WPF: la práctica de resx, ensamblados satélite y cambio de cultura

· · CSharp, .NET, WinForms, WPF, Internacionalización, Localización, Recursos, UI, Desarrollo de Windows, Consultoría técnica

«Queremos usar la misma aplicación también en nuestras sedes en el extranjero», «en la planta ha aumentado el personal que habla otros idiomas y queremos que la pantalla también esté en inglés»: la internacionalización de aplicaciones de escritorio Windows es una petición sencilla en apariencia, pero en cuanto se empieza a implementar surgen dudas continuas: «¿cómo se usa resx?», «WPF tiene varios métodos y no está claro cuál es el correcto», «¿se puede cambiar el idioma durante la ejecución?».

En este artículo, pensando en aplicaciones de negocio WinForms/WPF, organizamos en el orden en que suelen surgir las dudas en la práctica: la base de la internacionalización en .NET (culturas y fallback de recursos), el mecanismo de resx y los ensamblados satélite, los métodos realistas según el framework, el cambio en tiempo de ejecución, y los aspectos más allá de las cadenas de texto (formato, diseño, RTL).

1. La conclusión primero (tabla de decisión)

El método de internacionalización se determina por la combinación de framework y requisitos.

Situación Método recomendado Motivo
WinForms, centrado en etiquetas/botones de pantalla Localizable = true del formulario + resx por idioma (método del diseñador) Permite ajustar por idioma tanto las cadenas como el tamaño y la posición de los controles, todo completado solo con Visual Studio
WinForms/WPF comunes, muchos mensajes y textos compartidos resx compartido (Resources.resx + Resources.en.resx, etc.) + la clase tipada generada Permite referenciar las cadenas desde el código de forma segura en cuanto a tipos. La pantalla y la lógica pueden compartir el mismo texto
WPF resx compartido + enlace x:Static El método LocBaml no puede usarse en WPF de .NET (Core)1. Con el método resx se puede compartir el conocimiento con WinForms
El cambio de idioma en tiempo de ejecución es obligatorio resx + un mecanismo de notificación de cambios (o la regeneración de la pantalla) Antes de elegir el método, confirmar como requisito si realmente hace falta el cambio sin reiniciar (capítulo 6)
La traducción se subcontrata externamente Diseñar sobre la base de resx y entregar mediante una tabla de correspondencia entre claves y traducciones Al ser XML, resx puede entregarse tal cual, pero acompañar la información de contexto (en qué pantalla y en qué botón) es lo que determina la calidad

Escribimos primero las conclusiones.

  • CurrentCulture (formato) y CurrentUICulture (idioma de los recursos) son cosas distintas. Esta distinción es la base de todo el diseño de internacionalización (capítulo 2).
  • La sustancia de los recursos es «recursos del idioma neutro + ensamblados satélite por idioma», y el fallback es automático. La búsqueda se hace en el orden ja-JPja → neutro, por lo que aunque falte una traducción la aplicación no se cae, sino que se muestra en el idioma predeterminado2.
  • En WinForms, la solución práctica es combinar el método del diseñador (Localizable = true) con el método resx compartido. Las pantallas se resuelven con el método del diseñador, y los cuadros de mensaje o textos comunes se centralizan en el resx compartido (capítulo 4).
  • El método LocBaml que aparece en el tutorial oficial de WPF no puede usarse en .NET (Core)1. La primera opción actual en la práctica es el método resx + x:Static (o enlace de datos) (capítulo 5).
  • El cambio de idioma en tiempo de ejecución debe tener como primera opción «aplicarse en el próximo inicio». Esto se debe a que los recursos de la pantalla que ya está mostrada no se sustituyen automáticamente (capítulo 6).
  • La traducción de cadenas es solo la mitad de la internacionalización. La otra mitad es que el diseño se adapte a la longitud del texto traducido, el formato de fechas y números, las fuentes, y el soporte de idiomas que se escriben de derecha a izquierda (RTL) (capítulo 7).

2. La base: la diferencia entre CurrentCulture y CurrentUICulture

En la configuración de cultura (configuración regional) de .NET existen dos propiedades con roles distintos3.

Propiedad Qué determina Ejemplo
CultureInfo.CurrentCulture El formato de fechas, números y moneda Mostrar 2026/07/07 o 7/7/2026
CultureInfo.CurrentUICulture En qué idioma se cargan los recursos Que el botón muestre «Guardar» o «Save»

Estas dos propiedades pueden configurarse de forma independiente. Por ejemplo, la combinación «pantalla en inglés, pero fechas y moneda con el formato japonés» (un requisito habitual en aplicaciones de negocio para hablantes de otros idiomas dentro de Japón) se expresa de forma natural con CurrentUICulture = en y CurrentCulture = ja-JP.

Como ambos valores predeterminados provienen de la configuración del sistema operativo, el punto de partida es «si no se hace nada, se sigue al sistema operativo». Cuando se quiere cambiar el idioma explícitamente en toda la aplicación, en lugar de configurarlo hilo por hilo, se sustituye el valor predeterminado.

using System.Globalization;

// Sustituye la cultura predeterminada de todos los hilos que se creen a partir de ahora
// (ejecutar antes de que se cree el hilo de UI, es decir, antes de Application.Run)
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en"); // Idioma de los recursos
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("ja-JP"); // Formato

Asignar un valor a Thread.CurrentThread.CurrentUICulture solo afecta a ese hilo. En aplicaciones de negocio que cruzan hilos con async/await o procesamiento en segundo plano, es más seguro cambiar el propio valor predeterminado con DefaultThreadCurrentUICulture / DefaultThreadCurrentCulture3.

Además, el formato de los logs, la salida a archivos y la interconexión entre sistemas no debe seguir la cultura del usuario, sino fijarse en CultureInfo.InvariantCulture. Para el diseño general del formato de fechas, consulte «Diseño de fecha, hora y zona horaria en aplicaciones de negocio».

3. El mecanismo de los recursos: resx, ensamblados satélite y fallback

La internacionalización de .NET sigue un modelo de «concentrador y radios» (hub and spoke). Los recursos del idioma predeterminado (neutro) se incrustan en el ensamblado principal, y los recursos por idioma se separan en otras DLL llamadas ensamblados satélite4.

Al compilar, los resx por idioma se organizan en una salida como esta:

MyApp.exe                     ← Cuerpo principal (incluye los recursos neutros)
MyApp.dll
ja/MyApp.resources.dll        ← Ensamblado satélite en japonés
en/MyApp.resources.dll        ← Ensamblado satélite en inglés

En tiempo de ejecución, el administrador de recursos parte de CurrentUICulture y busca los recursos en el orden cultura específica (ja-JP) → cultura neutra (ja) → predeterminado (cuerpo principal)2. Gracias a este fallback automático, aunque falte una traducción la aplicación no se cae, sino que muestra la cadena del idioma predeterminado. Dicho de otro modo, si «se supone que la pantalla está en inglés, pero aparecen mezcladas algunas palabras en japonés», es señal de una traducción faltante, algo que puede usarse como criterio de prueba (capítulo 8).

En la práctica conviene tener presentes estos dos puntos:

  • Declarar mediante el atributo NeutralResourcesLanguage cuál es la cultura del idioma predeterminado elimina la búsqueda innecesaria de un satélite cuando ya se está en la cultura predeterminada, y deja claro el punto final del fallback4.
  • Los ensamblados satélite solo funcionan si se distribuyen junto con el cuerpo principal, por lo que hay que incluir las carpetas de idioma en el diseño del instalador o de la distribución por copia (capítulo 8).

4. La práctica en WinForms: el método del diseñador y el método resx compartido

WinForms tiene dos métodos, y en la práctica se combinan.

4.1 Las pantallas, con el método del diseñador (Localizable = true)

Si en las propiedades del formulario se pone Localizable en true y se cambia la propiedad Language al idioma de destino (por ejemplo, inglés) antes de editar el Text de las etiquetas y botones, Visual Studio genera automáticamente un resx por idioma como Form1.en.resx5.

La fortaleza de este método es que no solo se guardan las cadenas, sino también el tamaño y la posición de los controles, por idioma. El problema de que «Registrar» en japonés se convierta en «Register» en inglés y sobresalga del botón puede ajustarse por idioma en el propio diseñador. Por otro lado, también aparece un coste de mantenimiento: los resx aumentan multiplicando el número de idiomas por el número de formularios, y cada cambio de diseño de un formulario obliga a revisar todos los idiomas. Si se prevé que el número de idiomas vaya a aumentar, dé prioridad a construir pantallas que «no se rompan aunque no se ajusten», mediante las medidas de diseño que se explican más adelante (capítulo 7).

4.2 Mensajes y textos comunes, con el método resx compartido

Las cadenas usadas desde el código, como los textos de cuadros de mensaje, diálogos de confirmación o la barra de estado, se centralizan en un Resources.resx común del proyecto, añadiendo por idioma archivos como Resources.en.resx. Como resx genera automáticamente una clase tipada, desde el código se puede hacer referencia sin errores de escritura en las claves.

// Se referencia a través de la clase tipada generada.
// La cadena devuelta cambia automáticamente según CurrentUICulture
MessageBox.Show(
    Properties.Resources.ConfirmDeleteMessage,
    Properties.Resources.ConfirmTitle,
    MessageBoxButtons.YesNo);

Aquí la regla más importante es no escribir directamente en el código las cadenas que se muestran al usuario. El esfuerzo de la internacionalización no se dedica a introducir el mecanismo, sino a «recuperar las cadenas dispersas escritas directamente». Incluso en la etapa en que se opera con un solo idioma, canalizar las cadenas a través de resx tiene valor por esta razón.

5. La práctica en WPF: resx como primera opción, no LocBaml

La internacionalización de WPF es un terreno donde la información tiende a confundirse. El tutorial de la documentación oficial presenta el método de configurar UICulture en el archivo de proyecto para generar recursos BAML e insertar las traducciones con la herramienta LocBaml, pero LocBaml es una herramienta de ejemplo exclusiva de WPF en .NET Framework, y no funciona en WPF de .NET (Core)1. Considere que no es un método a elegir en un proyecto nuevo hoy en día.

La primera opción en la práctica es referenciar desde XAML el mismo método resx compartido que en WinForms.

<!-- Se pone el modificador de acceso de resx en Public y se referencia con x:Static -->
<Window xmlns:res="clr-namespace:MyApp.Properties"
        Title="{x:Static res:Resources.MainWindowTitle}">
    <Button Content="{x:Static res:Resources.SaveButton}" />
</Window>

La ventaja de este método es que el mecanismo de recursos (ensamblados satélite, fallback) es totalmente común con WinForms, lo que permite gestionar de forma centralizada los textos incluso en soluciones mixtas. Como x:Static es una referencia estática que se resuelve al iniciar, si se necesita cambio en tiempo de ejecución hay que evolucionar hacia un enlace de datos con notificación de cambios (capítulo 6).

En el lado XAML, configurar además el xml:lang del elemento raíz hace que funcionen correctamente las funciones de WPF que dependen del idioma, como la separación silábica, el fallback de fuentes y la presentación de números1.

Para la decisión de si construir con WinForms o con WPF, consulte «Tabla de decisión para elegir entre WinForms, WPF y WinUI».

6. Cambio de idioma en tiempo de ejecución: «aplicar en el próximo inicio» como primera opción

En los requisitos donde la pantalla de configuración permite elegir el idioma, hay que elegir entre dos opciones de momento de aplicación.

  • Opción A: aplicar en el próximo inicio (recomendada). Se guarda el idioma seleccionado en el archivo de configuración y se establece DefaultThreadCurrentUICulture antes de Application.Run (o antes de iniciar Application en WPF). La implementación es sencilla y, estructuralmente, no se producen los problemas asociados a la reconstrucción de la pantalla (pérdida de datos que se estaban introduciendo, doble registro de controladores de eventos, etc.).
  • Opción B: aplicación inmediata. Aunque se cambie CurrentUICulture, las cadenas de las pantallas ya mostradas no se sustituyen automáticamente. En el método del diseñador de WinForms habría que volver a aplicar los recursos a los formularios abiertos con ComponentResourceManager.ApplyResources, y en WPF habría que suministrar las cadenas mediante enlace de datos con notificación de cambios; en cualquier caso, se parte de la premisa de que «todas las pantallas admiten volver a aplicar los recursos», lo cual implica un trabajo de construcción considerable.

En las aplicaciones de negocio, dada la frecuencia con la que se cambia de idioma (normalmente una vez, en el momento de la implantación), el coste de implementación y pruebas de la opción B suele no compensar, por lo que se recomienda tomar la opción A como predeterminada y reservar la opción B solo para cuando el requisito realmente lo exija. Como la configuración de idioma es una preferencia por usuario, el lugar adecuado para guardarla es dentro de %LOCALAPPDATA%. Para decidir dónde ubicar la configuración, consulte la tabla de decisión de «La práctica de la gestión de configuración en aplicaciones de negocio Windows».

7. La internacionalización más allá de las cadenas: diseño, formato, fuentes y RTL

Aunque la traducción esté terminada, todavía queda la mitad de la internacionalización.

  • Expansión y contracción del diseño. Incluso con el mismo significado, la longitud del texto varía mucho según el idioma (japonés «保存» → alemán «Speichern»). Los diseños con coordenadas y tamaños fijos se rompen cada vez que se traduce, por lo que en WinForms el método recomendado oficialmente es combinar TableLayoutPanel con AutoSize para lograr un diseño que se adapte a la longitud del texto6. En WPF, como el sistema de diseño ya es flexible por naturaleza, la mayor parte se resuelve simplemente evitando fijar anchos de forma rígida. Conviene diseñar esto al mismo tiempo que las medidas contra la ruptura en entornos de alto DPI («Guía de alto DPI en WinForms», «Guía de alto DPI en WPF»), para evitar retrabajos.
  • Formato de fechas, números y moneda. Se deja la visualización en pantalla a cargo de CurrentCulture, reduciendo desde el código de pantalla los ToString("yyyy/MM/dd") fijados a mano. En cambio, los archivos, logs y datos de interconexión se fijan en InvariantCulture (capítulo 2).
  • Fuentes. Hay que verificar si los caracteres del idioma de destino están incluidos en la fuente. Al añadir chino (simplificado/tradicional) o coreano, si se mantiene una fuente pensada para japonés, las formas de los caracteres pueden verse antinaturales (el conocido problema de las formas CJK). Centralizar en un solo lugar la especificación de la fuente de la interfaz facilita los cambios al añadir idiomas.
  • Idiomas que se escriben de derecha a izquierda (RTL). El soporte de árabe o hebreo requiere, además de traducir las cadenas, invertir la disposición izquierda-derecha de la pantalla. En WinForms se controla con las propiedades RightToLeft / RightToLeftLayout, y en WPF con la propiedad FlowDirection. Añadir el soporte RTL «más adelante» implica revisar todas las pantallas, así que si existe la posibilidad de incluir alguno de estos idiomas, inclúyalo en los requisitos desde el principio.
  • Texto dentro de imágenes o iconos. Las cadenas incrustadas en imágenes no cambian con el mecanismo de recursos. Es más seguro evitar imágenes con texto incrustado y usar una combinación de imagen más etiqueta de texto.

Para el diseño general de UI, consulte también «Tabla de decisión de diseño UX para aplicaciones Windows».

8. Operación: entrega de traducciones, detección de omisiones y distribución

Por último, los aspectos operativos una vez que el mecanismo ya está construido.

  • Acompañar la entrega de la traducción con contexto. Si solo se entregan la clave y el texto original de resx, la calidad de traducción de palabras cortas como «OK» o «Abrir» no es estable. Es más realista intercambiar una tabla de correspondencia (por ejemplo, una hoja de cálculo) con clave, texto original y traducción, acompañada de «en qué pantalla se muestra y para qué se usa», y reflejarla en resx tras recibirla.
  • Detectar las traducciones faltantes mediante el fallback. Debido al fallback de recursos, una traducción faltante se manifiesta como «se mezcla el idioma predeterminado en la pantalla de ese idioma»2. Incluir en la integración continua (CI) una verificación sencilla (mediante script o código de prueba) que compare la lista de claves del resx por idioma con la del resx neutro permite detectarlo de forma mecánica antes del lanzamiento.
  • Incluir los ensamblados satélite en la distribución. Si no se distribuyen las carpetas de idioma como ja/ o en/, ese idioma caerá en el fallback y se producirá la situación de «se tradujo, pero no se refleja». Que falten carpetas de idioma por una copia manual es un incidente clásico, así que hay que incluir toda la estructura de directorios en el instalador (MSI/MSIX, etc.). En la distribución mediante ClickOnce, por defecto se incluyen en el despliegue los ensamblados satélite de todos los idiomas7. Para la decisión general sobre el método de distribución, consulte «Cómo elegir el método de distribución de aplicaciones Windows».

Resumen

La internacionalización de aplicaciones de escritorio se apoya en el mecanismo ya consolidado de resx y ensamblados satélite, y el mecanismo en sí no es difícil. Lo que determina el éxito en la práctica es diseñar distinguiendo CurrentCulture de CurrentUICulture, la disciplina de no escribir directamente en el código las cadenas destinadas al usuario, un diseño que resista la longitud del texto traducido, y discernir si realmente es necesario el cambio inmediato de idioma como requisito. En WPF, en lugar de dejarse arrastrar por la información sobre el antiguo método LocBaml, la solución práctica actual es construir sobre resx.

Elegir el método de internacionalización de una aplicación existente y estimar el alcance del impacto, o adaptar a múltiples idiomas las pantallas, informes y datos de interconexión para expandirse a sedes en el extranjero, suele requerir decisiones que dependen del estado actual del código base, así que si tiene dudas, no dude en consultarnos.

Artículos relacionados

Áreas de consultoría relacionadas

En Komura Soft LLC (合同会社小村ソフト) atendemos consultoría técnica sobre el diseño e implementación del soporte multilingüe en aplicaciones WinForms/WPF, así como sobre la elección del método y la delimitación del alcance del impacto al internacionalizar más adelante una aplicación existente.

Referencias

  1. Microsoft Learn, WPF Globalization and Localization Overview. Sobre la generación de ensamblados satélite mediante la configuración de UICulture en el archivo de proyecto, el papel del xml:lang del elemento raíz, y el hecho de que LocBaml es una herramienta de ejemplo exclusiva de WPF en .NET Framework y no funciona en WPF de .NET.  2 3 4

  2. Microsoft Learn, Retrieve resources in .NET apps. Sobre el proceso de fallback de recursos (la búsqueda se realiza en el orden cultura específica → cultura neutra → recurso predeterminado del ensamblado principal).  2 3

  3. Microsoft Learn, CultureInfo.CurrentUICulture Property. Sobre cómo CurrentUICulture se usa en la búsqueda de recursos específicos de cultura por parte del administrador de recursos, que su valor predeterminado proviene del idioma de la interfaz de usuario del sistema operativo, y sobre la configuración del valor predeterminado de todos los hilos mediante DefaultThreadCurrentUICulture 2

  4. Microsoft Learn, Resources in .NET apps. Sobre la creación y el empaquetado de recursos resx, el modelo de concentrador y radios mediante el ensamblado principal y los ensamblados satélite, y el atributo NeutralResourcesLanguage 2

  5. Microsoft Learn, Walkthrough: Localizing a Hybrid Application. Sobre el procedimiento para poner en true la propiedad Localizable en el diseñador de Windows Forms y cambiar la propiedad Language para generar un resx por idioma (por ejemplo, Form1.es-ES.resx), y sobre el ejemplo de establecer CurrentUICulture antes de la llamada a InitializeComponent

  6. Microsoft Learn, How to: Design a Windows Forms Layout that Responds Well to Localization. Sobre cómo usar TableLayoutPanel para crear un diseño en el que el tamaño de los controles se adapte al cambio de longitud de la cadena de la propiedad Text provocado por la localización. 

  7. Microsoft Learn, Localize ClickOnce applications. Sobre el hecho de que incluir todos los ensamblados satélite en un mismo despliegue es el comportamiento predeterminado de Visual Studio, y que en tiempo de ejecución se usa el ensamblado satélite correspondiente a la cultura del sistema operativo. 

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

El artículo está directamente relacionado con los siguientes servicios.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

¿Se puede internacionalizar más adelante una aplicación existente creada solo en japonés?
Sí, es posible, pero la mayor parte del trabajo no consiste en «introducir el mecanismo de recursos», sino en «localizar y reemplazar las cadenas escritas directamente en el código». Es necesario hacer un inventario que incluya no solo los textos de pantalla, sino también los cuadros de mensaje, los registros (logs), los mensajes de excepción, los informes y las cadenas de visualización almacenadas en la base de datos; el tamaño del trabajo depende más de la dispersión de las cadenas que del número de pantallas. Si desde la etapa de desarrollo inicial se canalizan a través de resx incluso las cadenas destinadas al usuario, el costo futuro de internacionalización se reduce drásticamente.
¿Es aceptable resolver la traducción con traducción automática?
Para herramientas internas o como solución provisional, es realista empezar con traducción automática. Sin embargo, las aplicaciones de negocio suelen tener muchas palabras cortas en botones y menús (por ejemplo, «Registrar», «Cancelar»), un terreno donde la tasa de errores de la traducción automática aumenta sin contexto. Como mínimo, se recomienda incluir una ronda de revisión en la que una persona que realmente use ese idioma pruebe las pantallas. Además, más importante que la calidad de la traducción es garantizar de antemano, con TableLayoutPanel y AutoSize, que el diseño no se rompa con cadenas traducidas más largas.
¿También deben traducirse los registros (logs) y los mensajes de error?
La práctica habitual es traducir los mensajes que se muestran en la pantalla del usuario y no traducir (fijar el idioma de) el contenido que se escribe en archivos de registro o en el registro de eventos. Los logs los leen desarrolladores y personal de operaciones, no el usuario final, y unos logs en varios idiomas arruinan la capacidad de búsqueda durante la investigación de incidentes. El formato (fechas, números) de los logs también debe fijarse en CultureInfo.InvariantCulture. Diseñar la aplicación separando CurrentCulture de CurrentUICulture permite lograr esta distinción de forma natural.
¿Conviene permitir cambiar el idioma solo dentro de la aplicación, independientemente del idioma de visualización del sistema operativo?
En la expansión hacia sedes en el extranjero, o en aplicaciones de negocio usadas dentro de Japón por personal que habla otros idiomas, tener una configuración de idioma propia dentro de la aplicación aporta mucho valor. Un esquema seguro de dos niveles es: por defecto seguir el idioma de visualización del sistema operativo (valor predeterminado de CurrentUICulture), y sobrescribirlo solo cuando el usuario lo elige explícitamente en la pantalla de configuración. Aplicar el cambio «en el próximo inicio» es la opción más segura, y también simplifica tanto la implementación como la explicación operativa.

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog