I want to change Language but when I compile this an exception pop up. it says
"Could not find any resources appropriate for the specified culture or
the neutral culture. Make sure "System.Type.resources" was correctly
embedded or linked into assembly "mscorlib" at compile time, or that
all the satellite assemblies required are loadable and fully signed."
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedItem.ToString() == "English")
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("En");
ChangeLanguage("En");
}
else if (comboBox1.SelectedItem.ToString() == "German")
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("De");
ChangeLanguage("De");
}
}
private void ChangeLanguage(string lang)
{
foreach (Control c in this.Controls)
{
ComponentResourceManager resources = new ComponentResourceManager(typeof(Type));
resources.ApplyResources(c, c.Name, new CultureInfo(lang));
}
}
Any suggestions?
ComponentResourceManager resources = new ComponentResourceManager(typeof(Type));
The argument to the constructor is wrong, you are telling it to find the resources for System.Type. Which is why it is complaining that it can't find "System.Type.resources". It will never find those.
You need to pass the type of the form that you actually want to localize. Use this.GetType() instead. Albeit that this probably will just localize your Options form and not the rest of the windows in your app. You could iterate Application.OpenForms() instead. It is also necessary to apply the localization to all the controls. Not just the ones on the form, also the ones that are located inside containers like panels. Thus:
private static void ChangeLanguage(string lang) {
Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);
foreach (Form frm in Application.OpenForms) {
localizeForm(frm);
}
}
private static void localizeForm(Form frm) {
var manager = new ComponentResourceManager(frm.GetType());
manager.ApplyResources(frm, "$this");
applyResources(manager, frm.Controls);
}
private static void applyResources(ComponentResourceManager manager, Control.ControlCollection ctls) {
foreach (Control ctl in ctls) {
manager.ApplyResources(ctl, ctl.Name);
applyResources(manager, ctl.Controls);
}
}
Be careful with wiz-bang features like this. Nobody actually changes their native language while they are using your program.
private void ChangeLanguage(CultureInfo culture)
{
Application.CurrentCulture = culture;
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(culture.Name);
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(culture.Name);
}
You'll need to have appropriate resources for all languages though.
Related
I have a MenuStrip control with all translations entered for several languages. When I try to change the culture dynamically all controls are changing the language as it must be except the MenuStrip. Here is the code which I used specifically for MenuStrip:
protected ComponentResourceManager res2;
private void signOutToolStripMenuItem_Click(object sender, EventArgs e)
{
LoginForm login = new LoginForm();
if (login.ShowDialog() == DialogResult.OK)
{
//this is for other controls
resManager = new ComponentResourceManager(this.GetType());
this.ApplyResources(this, Thread.CurrentThread.CurrentCulture);
//this is for MenuStrip
res2 = new ComponentResourceManager(typeof(MenuStrip));
this.ApplyResourcesToolStripMenuItem(Thread.CurrentThread.CurrentCulture);
}
}
private void ApplyResourcesToolStripMenuItem(CultureInfo lang)
{
foreach (ToolStripItem m in this.menuStrip1.Items)
{
//resources.ApplyResources(m, m.Name, lang);
if (m is ToolStripMenuItem)
{
string text = res2.GetString(m.Name + ".Text", lang); //Exception is thrown here
if (text != null)
m.Text = text;
}
}
}
I used another developer's code sample from this forum and made minor changes.
Now when I change culture at runtime, I get the following exception:
An unhandled exception of type 'System.Resources.MissingManifestResourceException' occurred in
mscorlib.dll
Additional information: Could not find any resources appropriate for
the specified culture or the neutral culture. Make sure
"System.Windows.Forms.MenuStrip.resources" was correctly embedded or
linked into assembly "System.Windows.Forms" at compile time, or that
all the satellite assemblies required are loadable and fully signed.
I have checked the resource file and all MenuStripItems have all translations/values there...
I failed to find solution in the internet, so would highly appreciate your help.
Thanks in advance!
So since I'm a beginner in localizing, the solution is much more easier than one that I have posted. Maybe this can help for other beginners too.
So I removed all my code which actually was a bulk of headache and replaced it with the following code:
Thread.CurrentThread.CurrentUICulture = new CultureInfo(StaticValues.currentCulture);
this.Controls.Clear();
this.InitializeComponent();
And it worked easily. And yes, thanks to Adriano Repetti!
In a WinForms (C#) application, I have created a language menu strip and a sound button but I don't know how to make them switchable.
I mean I want the sound button to stop sound when i hit it & play sound when i hit it again and change its icon as well ?
Similarly, the language menu, how to make it change language with "Localizable" and its text to the other language at first hit, then turn back at second hit ?
Here is my code:
using System.Globalization;
using System.Threading;
namespace Project
{
public partial class Form2 : Form
{}
private void Menu_LanguageSwitch_Click (object sender, EventArgs e)
{
//Switch to EN - what's here?
{
CultureInfo ci = new CultureInfo("en-US");
System.Threading.Thread.CurrentThread.CurrentCulture = ci;
System.Threading.Thread.CurrentThread.CurrentUICulture = ci;
System.ComponentModel.ComponentResourceManager res = new ComponentResourceManager(typeof(Form2));
res.ApplyResources(lbl_Status, "lbl_Status");
Menu_LanguageSwitch.Text = "Francais";
}
//Switch to French
{
CultureInfo ci = new CultureInfo("fr");
System.Threading.Thread.CurrentThread.CurrentCulture = ci;
System.Threading.Thread.CurrentThread.CurrentUICulture = ci;
System.ComponentModel.ComponentResourceManager res = new ComponentResourceManager(typeof(Form2));
res.ApplyResources(Menu_LanguageSwitch, "Menu_LanguageSwitch");
res.ApplyResources(label2, "label2");
res.ApplyResources(label3, "label3");
res.ApplyResources(label4, "label4");
res.ApplyResources(label5, "label5");
res.ApplyResources(label6, "label6");
res.ApplyResources(label7, "label7");
res.ApplyResources(label8, "label8");
res.ApplyResources(lbl_Status, "lbl_Status");
Menu_LanguageSwitch.Text = "Francais";
}
}
}
Thank you, please make it clear to a beginner. I'ma "rookie".
You'll need to keep track of the current state of the program, so you can check against it when the button is pushed. For simple flags (like "Mute") this can just be a bool:
private bool isMuted = false;
private void onSoundClick(...)
{
if (isMuted)
{
//Do unmute kind of things
isMuted = false;
}
else
{
//Do mute kind of things
isMuted = true;
}
}
The localization logic would be similar, but if you want it to be more than 2 languages, you'll need to cycle through a list/queue.
the simple way is the use a form scope variable
Externalize to the form
CultureInfo ci = new CultureInfo("fr");
when you click the button check the active CutureInfo and then switch to the correct
I have a context menu application where I have a base project SampleContextMenu.Base and a child project SampleContextMenu.Menu.
In SampleContextMenu.Menu, I have created a context menu for which I set a text like this.
sampleItem.Text = "Open In";
Now I want to globalize this string. So I created 3 resource files called UiStrings.resx, UiStrings.de-DE.resx, and UiStrings.fr-FR.resx.
In the class SampleContextMenu.cs, I have the following code now,
// Resource path
private string strResourcesPath = Application.StartupPath + "/Resources";
// string to store current culture which is comon in all the forms
private string strCulture = "en-US";
//resourcemanager which retrivesthe strings
//from the resource files
private static ResourceManager rm;
private void GlobalizeApp()
{
SetCulture();
SetResource();
}
private void SetCulture()
{
CultureInfo objCI = new CultureInfo(strCulture);
Thread.CurrentThread.CurrentCulture = objCI;
}
private void SetResource()
{
rm = new ResourceManager("SampleContextMenu.Menu.UiStrings", typeof(SampleContextMenu).Assembly);
}
protected override ContextMenuStrip CreateMenu()
{
// some code to create menu
strCulture = Read("language");
GlobalizeApp();
sampleItem.Text = rm.getString("0000");
}
EDIT
For me now even if the language is de or fr, it still shows stirng for the default( i.e. En)
I have my Resource files with 2 languages and my app already reads the values of one of them. I would like to be able to change the language of my app (use the other resource file) in C# instead of changing the language of the whole phone in Settings.
Is this possible? If so, how?
In App.xaml.cs, in the InitializePhoneApplication method:
private void InitializePhoneApplication()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
.......
}
The limitation is that it needs to be in the app initialization, so if the user changes the language, a restart will be required for it to take effect.
you can do this without need to restart by reloading the page where the user change a language and maintaining the RTL/LTR of your page layout
I added this function in App.xaml.cs
public static void ChangeAppLanguage(string CultureName)
{
App.RootFrame.Language = XmlLanguage.GetLanguage(CultureName);
FlowDirection flow = (FlowDirection)Enum.Parse(typeof(FlowDirection), AppResources.ResourceFlowDirection);
App.RootFrame.FlowDirection = flow;
App.Current.RootVisual.UpdateLayout();
App.RootFrame.UpdateLayout();
var ReloadUri =( App.RootFrame.Content as PhoneApplicationPage).NavigationService.CurrentSource;
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri(ReloadUri + "?no-cache=" + Guid.NewGuid(), UriKind.Relative));
}
where the CultureName like this : "ar-SA", "en-US"
and I called like this
private void EnglishMenuItem_Click(object sender, EventArgs e)
{
try
{
if(Thread.CurrentThread.CurrentCulture.Name == "en-US")
Thread.CurrentThread.CurrentCulture = new CultureInfo("ar-SA");
else
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
AppResources.Culture = Thread.CurrentThread.CurrentCulture;
App.ChangeAppLanguage(Thread.CurrentThread.CurrentCulture.Name);
//this._contentLoaded = false; //this way does not refresh appBar
//this.InitializeComponent();
//this way work for one time only => if user change language more thane once the api does NOT call constructor
//NavigationService.Navigate(new System.Uri("/PhoneApp2;component/MainPage.xaml", System.UriKind.Relative));
}
catch (Exception ex)
{
MessageBox.Show("error:\n\n" + ex.Message);
}
}
What is the proper way to change Form language at runtime?
Setting all controls manually using recursion like this
Save language choice to file > Restart Application > Load languge
choice before InitializeComponent();
Using Form constructor to replace instance of active from (if this is even possible)
Something else
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();
I believe the solution shown in Hans Passant's comment might be the only (general) solution.
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:
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;
}
}
}
I have found another way:
Move initialization form code in a private method like below:
private void FormInitialize() {/*Your code here*/}
In the form constructor use it like this:
public Form1()
{
InitializeComponent();
FormInitialize();
}
And from Button, menuItem or other call method like this:
private void ChangeCultureToFrench_Click(object sender, EventArgs e)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr");
this.Controls.Clear();
this.InitializeComponent();
FormInitialize();
}
I hope this helps ;-)
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();
}
}
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:
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:
_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.
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).
save a public var on the main that will hold the lang.
created a class which handles the visual part
created xml files that will hold any language data and more (in my xml tag name=object name).
sent that class's constructor the form (to save and work with)
connect to that current xml file
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):
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();
}