C# 在运行时更改语言的正确方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11711426/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 18:55:43  来源:igfitidea点击:

Proper way to change language at runtime

c#winformslocalizationglobalization

提问by formatc

What is the proper way to change Form language at runtime?

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

  1. Setting all controls manually using recursion like this
  2. Save language choice to file > Restart Application > Load languge choice before InitializeComponent();
  3. Using Form constructor to replace instance of active from (if this is even possible)
  4. Something else
  1. 这样使用递归手动设置所有控件
  2. 将语言选择保存到文件 > 重新启动应用程序 > 之前加载语言选择 InitializeComponent();
  3. 使用 Form 构造函数来替换 active 的实例(如果这可能的话)
  4. 别的东西

There is so much half written threads about this but none provides real answer on what is proper way to do this?

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

UPDATE:
To clarify my question:

更新:
澄清我的问题:

Doing something like this:

做这样的事情:

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

works fine and all my controls and everything else in resources get translated correctly. And doing something like:

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

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

does nothing, Form stays in language I set up before InitializeComponent();

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

采纳答案by mnn

I believe the solution shown in Hans Passant's comment might be the only (general) solution.

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

Personally, I use this base class for all forms that need to be localized:

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

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

Then instead of directly changing Thread.CurrentThread.CurrentUICulture, I use this property in static manager class to change UI culture:

然后,我没有直接更改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;
        }
    }
}

回答by Vaughanabe13

In reference to your ColumnHeader .NET framework bug, I also discovered this bug recently and posted a question about it (to which I haven't received any responses). I was able to fix the problem by hardcoding the changes for the ColumnHeaders. For example:

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

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

You basically just replace the call to .Name with a literal string of the name. I have tested this and it works. Unfortunately this means it won't fit as part of the code you use to change all of the controls. You will have to add a line for each ColumnHeader object you need to change. If you have a listview with a variable number of columns, that could get tricky. Another option is to create localized resource files. I assume you probably already have them for stuff like message box text and other strings. Then you can add an entry in your resource file like "columnHeader_myHeader" and set the appropriate language text for each language. Finally, you can manually change the text to your column headers by using:

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

_myHeader.Text = myResourceFileName.columnHeader_myHeader;

This will select the appropriate language based on the current thread culture. Hans was correct in that there doesn't seem to be a foolproof "proper" way to perform localization in .NET, though there are a variety of tools you can use. For something like a job application, even though it is probably already too late for this advice, my suggestion would be to learn as many different methods as you can for localization, learn the pros and cons, and then just pick a system and be able to argue why you believe it is the "proper" choice. They are probably more concerned with your logic and reasoning and demonstration of prior experience than they are with the actual method.

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

回答by Bronislav Dra?ka

I've discovered this kind of approach a few minutes ago. Just quick and simple restart of the main form. Meybe it will help to someone. Event is created inside the form on my own, called when user selects the language from menu but after the selected culture's name is saved into the settings. Culture names are then loaded from that settings. Works exactly as I need and looks like proper solution.

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

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

回答by Bosshog

I have found another way:

我找到了另一种方法:

Move initialization form code in private methode like below

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

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

In form constructor use it like this

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

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

And from Button, menuItem or other call methode like this

从 Button、menuItem 或其他像这样的调用方法

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

I hope this helps ;-)

我希望这有帮助 ;-)

回答by Avishay

Hope this would help anyone, I found it best for me cause i needed to change buttons location according the lang (browse on the right or left of the search box and labels next to text fields).

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

  1. save a public var on the main that will hold the lang.
  2. created a class which handles the visual part
  3. created xml files that will hold any language data and more(in my xml tag name=object name).
  4. sent that class's constructor the form (to save and work with)
  5. connect to that current xml file
  1. 在将保存 lang 的 main 上保存一个公共变量。
  2. 创建了一个处理视觉部分的类
  3. 创建的 xml 文件将包含任何语言数据和更多(在我的 xml 标记名称=对象名称中)。
  4. 向该类的构造函数发送表单(以保存和使用)
  5. 连接到当前的 xml 文件

From main form call whenever you want to initialView (part of the view class) and change lang (and more) anytime (just connect to the right xml file):

每当您想要初始化视图(视图类的一部分)并随时更改 lang(以及更多)(只需连接到正确的 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;
    }
}

And this is an example of how easy it is for my at runtime:

这是我在运行时是多么容易的一个例子:

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