【问题标题】:Proper way to change language at runtime在运行时更改语言的正确方法
【发布时间】:2023-03-22 08:54:01
【问题描述】:

在运行时更改表单语言的正确方法是什么?

  1. 使用递归手动设置所有控件,例如this
  2. 将语言选择保存到文件 > 重新启动应用程序 > 加载语言 InitializeComponent();之前的选择
  3. 使用 Form 构造函数替换 active from 的实例(如果可能的话)
  4. 别的东西

关于这个有很多半写的线程,但没有一个提供真正的答案来说明什么是正确的方法?

更新:
澄清我的问题:

做这样的事情:

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

工作正常,我的所有控件和资源中的所有其他内容都得到正确翻译。 并做类似的事情:

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

什么都不做,表单保持我在InitializeComponent();之前设置的语言

【问题讨论】:

  • @Hans 这不是重复的,我的问题是:正确的方法是什么,您链接到的问题只是提供快速修复而不是真正的答案。
  • “快速修复”到底有什么问题?大多数程序员更喜欢它们,而不是对框架进行彻底的重新设计。我猜你认为这应该是 Winforms 的内置功能。它不是。只有应用“快速修复”的能力才是设计功能。
  • @Hans 好吧,这是我的工作申请,所以我想知道“正确”的方式。答案中的代码是半功能性的,因为它没有覆盖 ListView 的 ColumnHeader.Text 属性,因为框架中的错误返回 ColumnHeader.Name 为空,所以它有点是框架的重新设计。它并没有回答我关于什么是正确方法的问题。
  • 再次,没有适当的方法,它不是内置功能。如果您确实对列标题有疑问,那么您在问题中描述该问题的工作做得很差。如果这是一份工作申请,那么您当然应该说“这不可能,它需要一个丑陋的快速解决方案”。

标签: c# winforms localization globalization


【解决方案1】:

我相信 Hans Passant 评论中显示的解决方案可能是唯一的(通用)解决方案。

就个人而言,我将这个基类用于所有需要本地化的表单:

public class LocalizedForm : Form
{
    /// <summary>
    /// Occurs when current UI culture is changed
    /// </summary>
    [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;

    /// <summary>
    /// Current culture of this form
    /// </summary>
    [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);
    }
}

然后我没有直接改变Thread.CurrentThread.CurrentUICulture,而是在静态管理器类中使用这个属性来改变UI文化:

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

            Thread.CurrentThread.CurrentUICulture = value;
        }
    }
}

【讨论】:

  • 请注意,如果您只循环“parent.Controls”,则不会完全遍历控制树。例如,一个 TabControl 将其 tabPages 保存在“parent.TabPages”中。 MenuStrip 在“parent.Items”中有其项目。以此类推。
  • 使用上面的代码,谁来本地化 MenuStrip 或 ToolStrip?
  • 在 TabControl 的情况下,每个选项卡都有自己的名称。所以如果是“tabPage4”,那么键就是“tabPage4.Text”
  • tabcontrols 有自己的控件树。如果您查看源代码,添加标签页实际上只是在控件集合中添加了一个控件。
【解决方案2】:

我找到了另一种方法:

在私有方法中移动初始化表单代码,如下所示:

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

在表单构造函数中这样使用它:

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

而从Button、menuItem或其他类似这样的调用方法:

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

我希望这会有所帮助;-)

【讨论】:

    【解决方案3】:

    几分钟前我发现了这种方法。只需快速简单地重新启动主窗体。 Meybe它会帮助某人。事件是在我自己的表单内创建的,当用户从菜单中选择语言但将所选文化的名称保存到设置中时调用。然后从该设置中加载文化名称。完全按照我的需要工作,看起来像正确的解决方案。

    static class Program
    {
        private static bool doNotExit = true;
        private static FormMain fm;
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [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();   
        }
    }
    

    【讨论】:

      【解决方案4】:

      关于您的 ColumnHeader .NET 框架错误,我最近也发现了这个错误并发布了一个关于它的问题(我没有收到任何回复)。我能够通过硬编码 ColumnHeaders 的更改来解决问题。例如:

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

      您基本上只是用名称的文字字符串替换对 .Name 的调用。我已经对此进行了测试,并且可以正常工作。不幸的是,这意味着它不适合作为您用于更改所有控件的代码的一部分。您必须为需要更改的每个 ColumnHeader 对象添加一行。如果您有一个列数可变的列表视图,那可能会变得很棘手。另一种选择是创建本地化的资源文件。我假设您可能已经将它们用于消息框文本和其他字符串等内容。然后,您可以在资源文件中添加一个条目,例如“columnHeader_myHeader”,并为每种语言设置适当的语言文本。最后,您可以使用以下方法手动将文本更改为列标题:

      _myHeader.Text = myResourceFileName.columnHeader_myHeader;
      

      这将根据当前线程文化选择适当的语言。 Hans 是正确的,在 .NET 中执行本地化似乎没有万无一失的“正确”方法,尽管您可以使用多种工具。对于工作申请之类的事情,即使这个建议可能已经太迟了,我的建议是尽可能多地学习不同的本地化方法,了解优缺点,然后选择一个系统并能够争论为什么你认为这是“正确”的选择。与实际方法相比,他们可能更关心您的逻辑、推理和先前经验的证明。

      【讨论】:

        【解决方案5】:

        希望这对任何人都有帮助,我发现它最适合我,因为我需要根据语言更改按钮位置(浏览搜索框的右侧或左侧以及文本字段旁边的标签)。

        1. 在保存该语言的 main 上保存一个公共 var。
        2. 创建了一个处理视觉部分的类
        3. 创建的 xml 文件将保存任何语言数据以及更多(在我的 xml 标记名称=对象名称中)。
        4. 向该类的构造函数发送表单(以保存和使用)
        5. 连接到当前的 xml 文件

        当您想要初始化视图(视图类的一部分)并随时更改语言(和更多)时,从主窗体调用(只需连接到正确的 xml 文件):

        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;
            }
        }
        

        这是一个例子,说明它在运行时对我来说是多么容易:

        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();
        }
        

        【讨论】:

          猜你喜欢
          • 2016-06-14
          • 1970-01-01
          • 2021-12-22
          • 1970-01-01
          • 2017-04-02
          • 2012-10-22
          • 1970-01-01
          • 1970-01-01
          • 2021-12-05
          相关资源
          最近更新 更多