La forma correcta de cambiar el idioma en tiempo de ejecución

¿Cuál es la forma correcta de cambiar el lenguaje de formulario en tiempo de ejecución?

  1. Configurando todos los controles manualmente usando recursión como esta
  2. Guarde la elección de idioma en el archivo> Reiniciar aplicación> Cargar opción de idioma antes de InitializeComponent();
  3. Usar el constructor de formulario para reemplazar la instancia de activo de (si esto es posible)
  4. Algo más

Hay tanta mitad de hilos escritos sobre esto, pero ninguno proporciona una respuesta real sobre la forma correcta de hacerlo.

ACTUALIZAR:
Para aclarar mi pregunta:

Haciendo algo como esto:

 public Form1() { Thread.CurrentThread.CurrentUICulture = new CultureInfo("de"); this.InitializeComponent(); } 

funciona bien y todos mis controles y todo lo demás en recursos se traducen correctamente. Y haciendo algo como:

 private void button1_Click(object sender, EventArgs e) { Thread.CurrentThread.CurrentUICulture = new CultureInfo("en"); } 

no hace nada, la Forma se mantiene en el idioma que configuré antes de InitializeComponent();

Creo que la solución que se muestra en el comentario de Hans Passant podría ser la única solución (general).

Personalmente, utilizo esta clase base para todas las formas que necesitan ser localizadas:

 public class LocalizedForm : Form { ///  /// Occurs when current UI culture is changed ///  [Browsable(true)] [Description("Occurs when current UI culture is changed")] [EditorBrowsable(EditorBrowsableState.Advanced)] [Category("Property Changed")] public event EventHandler CultureChanged; protected CultureInfo culture; protected ComponentResourceManager resManager; ///  /// Current culture of this form ///  [Browsable(false)] [Description("Current culture of this form")] [EditorBrowsable(EditorBrowsableState.Never)] public CultureInfo Culture { get { return this.culture; } set { if (this.culture != value) { this.ApplyResources(this, value); this.culture = value; this.OnCultureChanged(); } } } public LocalizedForm() { this.resManager = new ComponentResourceManager(this.GetType()); this.culture = CultureInfo.CurrentUICulture; } private void ApplyResources(Control parent, CultureInfo culture) { this.resManager.ApplyResources(parent, parent.Name, culture); foreach (Control ctl in parent.Controls) { this.ApplyResources(ctl, culture); } } protected void OnCultureChanged() { var temp = this.CultureChanged; if (temp != null) temp(this, EventArgs.Empty); } } 

Entonces, en lugar de cambiar directamente Thread.CurrentThread.CurrentUICulture , utilizo esta propiedad en la clase de administrador estático para cambiar la cultura de UI:

 public static CultureInfo GlobalUICulture { get { return Thread.CurrentThread.CurrentUICulture; } set { if (GlobalUICulture.Equals(value) == false) { foreach (var form in Application.OpenForms.OfType()) { form.Culture = value; } Thread.CurrentThread.CurrentUICulture = value; } } } 

He encontrado otra forma:

Mueva el código del formulario de inicialización en un método privado como a continuación

 private void FormInitialize() {/*Your code here*/} 

En el constructor de formularios utilízalo así

 public Form1() { InitializeComponent(); FormInitialize(); } 

Y desde Button, menuItem u otro método de llamada como este

 private void ChangeCultureToFrench_Click(object sender, EventArgs e) { Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr"); this.Controls.Clear(); this.InitializeComponent(); FormInitialize(); } 

Espero que esto ayude 😉

Descubrí este tipo de enfoque hace unos minutos. Solo reinicio rápido y simple del formulario principal. Meybe ayudará a alguien. El evento se crea dentro del formulario por mi cuenta, se llama cuando el usuario selecciona el idioma del menú, pero después de que el nombre de la cultura seleccionada se guarda en la configuración. Los nombres de cultura se cargan desde esa configuración. Funciona exactamente como lo necesito y se ve como una solución adecuada.

 static class Program { private static bool doNotExit = true; private static FormMain fm; ///  /// The main entry point for the application. ///  [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); while(doNotExit) { System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo(Properties.Settings.Default.language);// System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo(Properties.Settings.Default.language);// doNotExit = false; fm = new FormMain(); fm.lanugageChangedEvent += new EventHandler(main_LanugageChangedEvent); Application.Run(fm); } } static void main_LanugageChangedEvent(object sender, EventArgs e) { doNotExit = true; fm.Close(); } } 

En referencia a su error ColumnHeader .NET framework, también descubrí este error recientemente y publiqué una pregunta al respecto (a la que no he recibido ninguna respuesta). Pude solucionar el problema codificando los cambios para ColumnHeaders. Por ejemplo:

 resources.ApplyResources(_myHeader, "_myHeader", culture); 

Básicamente, simplemente reemplaza la llamada a .Name con una cadena literal del nombre. Lo he probado y funciona. Desafortunadamente, esto significa que no se ajustará como parte del código que usa para cambiar todos los controles. Deberá agregar una línea para cada objeto ColumnHeader que necesite cambiar. Si tiene una vista de lista con una cantidad variable de columnas, podría ser complicado. Otra opción es crear archivos de recursos localizados. Supongo que probablemente ya los tengas para cosas como el texto del cuadro de mensaje y otras cadenas. Luego puede agregar una entrada en su archivo de recursos como “columnHeader_myHeader” y establecer el texto de idioma apropiado para cada idioma. Finalmente, puede cambiar manualmente el texto a los encabezados de sus columnas usando:

 _myHeader.Text = myResourceFileName.columnHeader_myHeader; 

Esto seleccionará el idioma apropiado basado en la cultura actual del hilo. Hans tenía razón en que no parece haber una forma “apropiada” de realizar la localización en .NET, aunque hay una variedad de herramientas que puede usar. Para algo así como una solicitud de trabajo, aunque probablemente ya sea demasiado tarde para este consejo, mi sugerencia sería aprender tantos métodos diferentes como sea posible para la localización, conocer los pros y los contras, y luego simplemente elegir un sistema y poder para argumentar por qué cree que es la opción “correcta”. Probablemente estén más preocupados con su lógica, razonamiento y demostración de experiencia previa que con el método real.

Espero que esto ayude a cualquiera, lo encontré mejor para mí porque necesitaba cambiar la ubicación de los botones de acuerdo con lang (busque a la derecha o izquierda del cuadro de búsqueda y las tags junto a los campos de texto).

  1. guarde una var pública en la principal que sostendrá la lang.
  2. creó una clase que maneja la parte visual
  3. archivos xml creados que contendrán cualquier información de idioma y más (en mi nombre de etiqueta xml = nombre de objeto).
  4. envió el constructor de esa clase el formulario (para guardar y trabajar con)
  5. conectarse a ese archivo xml actual

Desde la forma principal, llame siempre que quiera a initialView (parte de la clase de visualización) y cambie lang ( y más ) en cualquier momento (solo conéctese al archivo xml correcto):

 public void initialView() { //Set rightToLeft values initialIndent(mainForm); //set visual text values initialTxt(); } private void initialTxt() { // Are there any more controls under mainObj (Form1)? Boolean endOfElemsUnderPnl = false; // The current Control im working on Object curObj = mainForm; do { // MenuStrip needs to be handled separately if (typeof(MenuStrip).ToString().Equals(curObj.GetType().ToString())) { foreach (ToolStripMenuItem miBase in ((MenuStrip)(curObj)).Items) { miBase.Text = mainForm.dbCon.getData(miBase.Name.ToString()); foreach (ToolStripMenuItem miInnerNode in miBase.DropDownItems) { miInnerNode.Text = mainForm.dbCon.getData(miInnerNode.Name.ToString()); } } } // Any other Control i have on the form else { ((Control)(curObj)).Text = mainForm.dbCon.getData(((Control)(curObj)).Name.ToString()); } curObj = mainForm.GetNextControl(((Control)(curObj)), true); // Are there any more controls under mainObj? if (null == curObj) { endOfElemsUnderPnl = true; } } while (!endOfElemsUnderPnl); } private void initialIndent(frmMyFileManager parent) { if (parent.Language.Equals("Hebrew")) { parent.RightToLeft = RightToLeft.Yes; } else if (parent.Language.Equals("English")) { parent.RightToLeft = RightToLeft.No; } else { parent.RightToLeft = RightToLeft.No; } } 

Y este es un ejemplo de lo fácil que es para mí en tiempo de ejecución:

 private void selectLanguageToolStripMenuItem_Click(object sender, EventArgs e) { DialogResult res = MessageBox.Show(this, "click yes for english and no for hebrew", "Select language", MessageBoxButtons.YesNoCancel); if (DialogResult.Yes == res) { Language = "English"; } else if (DialogResult.No == res) { Language = "Hebrew"; } dbCon = new CDBConnector("****\\lang" + Language + ".xml"); view.initialView(); }