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

时间:2012-07-29 17:36:34

标签: c# winforms localization globalization

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

  1. 使用this
  2. 之类的递归手动设置所有控件
  3. 将语言选择保存到文件>重启应用程序>加载语言 InitializeComponent();
  4. 之前的选择
  5. 使用Form构造函数替换active的实例(如果可能的话)
  6. 其他东西
  7. 关于这个问题有很多半成文,但是没有一个能提供真正的答案来解决这个问题的正确方法吗?

    更新
    澄清我的问题:

    做这样的事情:

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

    工作正常,我的所有控件和资源中的其他所有内容都可以正确转换。 并做了类似的事情:

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

    什么都不做,Form保持我在InitializeComponent();

    之前设置的语言

5 个答案:

答案 0 :(得分:21)

我相信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;
        }
    }
}

答案 1 :(得分:3)

我找到了另一种方式:

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

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

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

答案 2 :(得分:2)

几分钟前我发现了这种方法。只需快速简单地重新启动主窗体即可。 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();   
    }
}

答案 3 :(得分:0)

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

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

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

_myHeader.Text = myResourceFileName.columnHeader_myHeader;

这将根据当前的线程文化选择适当的语言。 汉斯是正确的,因为在.NET中执行本地化似乎并不是一种万无一失的“正确”方式,尽管您可以使用各种工具。对于像工作申请这样的东西,即使这个建议可能已经太晚了,我的建议是学习尽可能多的不同方法来进行本地化,学习利弊,然后选择一个系统并且能够争论为什么你认为这是“适当的”选择。他们可能更关心你的逻辑和推理以及先前经验的证明,而不是实际的方法。

答案 4 :(得分:0)

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

  1. 将一个公共var保存在将保存lang的main上。
  2. 创建了一个处理可视部分的类
  3. 创建的xml文件将包含任何语言数据和更多(在我的xml标记名称=对象名称中)。
  4. 将该类的构造函数发送给表单(保存并使用)
  5. 连接到当前的xml文件
  6. 从主窗体调用,无论何时想要initialView(视图类的一部分)并随时更改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;
        }
    }
    

    这是一个在运行时对我来说有多容易的例子:

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