Правильный способ изменения языка во время выполнения
Каков правильный способ изменить язык формы во время выполнения?
- Настройка всех элементов управления вручную с помощью рекурсии, например
- Сохранить выбор языка в файле > Перезапустить приложение > Загрузить язык
выбор до
InitializeComponent();
- Использование конструктора форм для замены экземпляра active из (если это возможно)
- Что-то еще
Существует так много наполовину написанных нитей об этом, но никто не дает реального ответа на то, что является правильным способом для этого?
UPDATE:
Чтобы уточнить мой вопрос:
Выполнение чего-то подобного:
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();
Ответы
Ответ 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 я использую это свойство в классе статического менеджера для изменения культуры пользовательского интерфейса:
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;
}
}
}
Ответ 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
Я нашел этот подход несколько минут назад. Просто быстрый и простой перезапуск основной формы. Мейбе это поможет кому-то. Событие создается внутри формы самостоятельно, вызывается, когда пользователь выбирает язык из меню, но после того, как выбранное имя культуры будет сохранено в настройках. Затем из этих настроек загружаются имена культур. Работает точно так, как мне нужно, и выглядит как правильное решение.
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
В связи с вашей ошибкой Framework ColumnHeader.NET я также обнаружил эту ошибку недавно и разместил вопрос об этом (на который я не получил никаких ответов). Я смог исправить проблему, скопировав изменения для ColumnHeaders. Например:
resources.ApplyResources(_myHeader, "_myHeader", culture);
В основном вы просто заменяете вызов на .Name буквенной строкой имени. Я тестировал это, и он работает. К сожалению, это означает, что он не будет входить в состав кода, который вы используете для изменения всех элементов управления. Вам нужно будет добавить строку для каждого объекта ColumnHeader, который необходимо изменить. Если у вас есть listview с переменным числом столбцов, это может стать сложным. Другой вариант - создать локализованные файлы ресурсов. Я предполагаю, что вы, вероятно, уже имеете их для таких вещей, как текст окна сообщения и другие строки. Затем вы можете добавить запись в свой файл ресурсов, например "columnHeader_myHeader", и установить соответствующий язык для каждого языка. Наконец, вы можете вручную изменить текст в заголовки столбцов, используя:
_myHeader.Text = myResourceFileName.columnHeader_myHeader;
Это позволит выбрать соответствующий язык, основанный на текущей культуре потоков.
Ганс был прав в том, что, похоже, нет надежного "правильного" способа выполнить локализацию в .NET, хотя есть множество инструментов, которые вы можете использовать. Для чего-то вроде приложения для работы, хотя, вероятно, уже слишком поздно для этого совета, мое предложение состояло бы в том, чтобы узнать как можно больше различных методов, например, для локализации, узнать плюсы и минусы, а затем просто выбрать систему и уметь спорить, почему вы считаете, что это "правильный" выбор. Вероятно, они больше связаны с вашей логикой, рассуждениями и демонстрацией предыдущего опыта, чем с фактическим методом.
Ответ 5
Надеюсь, это помогло бы кому-нибудь, я счел это лучшим для меня, потому что мне нужно было изменить расположение кнопок в соответствии с lang (просмотрите справа или слева от поля поиска и метки рядом с текстовыми полями).
- сохранить публичный var на главном, который будет удерживать lang.
- создал класс, который обрабатывает визуальную часть
- созданный xml файл, в котором будут храниться любые языковые данные и более (в моем имени тега xml = имя объекта).
- отправил этот конструктор класса в форму (для сохранения и работы)
- подключиться к текущему файлу xml
От вызова основной формы всякий раз, когда вы хотите инициализировать (часть класса представления) и меняете 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();
}