Thread.CurrentThread.CurrentUICulture not working consistently - c#

I've been working on a pet project on the weekends to learn more about C# and have encountered an odd problem when working with localization. To be more specific, the problem I have is with System.Threading.Thread.CurrentThread.CurrentUICulture.
I've set up my app so that the user can quickly change the language of the app by clicking a menu item. The menu item in turn, saves the two-letter code for the language (e.g. "en", "fr", etc.) in a user setting called 'Language' and then restarts the application.
Properties.Settings.Default.Language = "en";
Properties.Settings.Default.Save();
Application.Restart();
When the application is started up, the first line of code in the Form's constructor (even before InitializeComponent()) fetches the Language string from the settings and sets the CurrentUICulture like so:
public Form1()
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(Properties.Settings.Default.Language);
InitializeComponent();
}
The thing is, this doesn't work consistently. Sometimes, all works well and the application loads the correct language based on the string saved in the settings file. Other times, it doesn't, and the language remains the same after the application is restarted.
At first I thought that I didn't save the language before restarting the application but that is definitely not the case. When the correct language fails to load, if I were to close the application and run it again, the correct language would come up correctly. So this implies that the Language string has been saved but the CurrentUICulture assignment in my form constructor is having no effect sometimes.
Any help? Is there something I'm missing of how threading works in C#? This could be machine-specific, so if it makes any difference I'm using Pentium Dual-Core CPU.
UPDATE
Vlad asked me to check what the CurrentThread's CurrentUICulture is. So I added a MessageBox on my constructor to tell me what the CurrentUICulture two-letter code is as well as the value of my Language user string.
MessageBox.Show(string.Format("Current Language: {0}\nCurrent UI Culture: {1}", Properties.Settings.Default.Language, Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName));
When the wrong language is loaded, both the Language string and CurrentUICulture have the wrong language. So I guess the CurrentUICulture has been cleared and my problem is actually with the Language Setting.
So I guess the problem is that my application sometimes loads the previously saved language string rather than the last saved language string. If the app is restarted, it will then load the actual saved language string.

I got the same problem. I figured out that Application.Restart() do not really make an absolutely restart, see MSDN.
So Application.Restart() do not call the initializing things within the forms constructor
like InitializeComponent(), more the "Applications are restarted in the context in which they were initially run."
So your code is correct
Properties.Settings.Default.Language = "en";
Properties.Settings.Default.Save();
public Form1()
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(Properties.Settings.Default.Language);
//...
InitializeComponent();
}
but it doesn't work this way with Application.Restart(). If You close the app and open it again your (new) settings are taken.
So we have to find a way to initialize the form again to make the new language settings happen.

Could you check what is your thread's CurrentUICulture?
I remember having a problem like yours; it was solved by reloading the resource dictionary containing the strings to be localized:
Thread.CurrentThread.CurrentUICulture = <new culture>;
ResourceDictionary newDict = new ResourceDictionary();
newDict.Source = localizedStrings.Source;
localizedStrings = newDict;
(and this approach worked dynamically as well; here is some more information).

You could manually change the language of the current form similar to this:
CultureInfo cInfo = new CultureInfo("en-US");
ResourceManager rm = new ResourceManager(GetType());
// For each control on the form, perform the translation manually (probably better in a loop)
control1.Text = rm.GetString(control1.Name + ".Text", cInfo);
// Now set the culture for all other dialogs
Thread.CurrentThread.CurrentUICulture = cInfo;
Hope that helps!

You could simply "reset" your application by closing the open forms and re-creating them. Then you could directly set the culture when the user changes the setting.
Also, try giving some debug output so you see what values are being set and if the culture is actually what you expect.
EDIT: My guess: Since the data has to be written to a file, and then loaded from that file, you may be restarting too quickly for the write to have been completed.

By using CurrentThread.CurrentUICulture, and then changing the form, you don't need to restart the application. Ref my old post here

Related

Set CurrentUICulture so it uses Resources.resx

I'm working on localization in a small test WPF app and want to have English and French. I created Resources.en-CA.resx and Resources.fr-CA.resx.
When my test app starts, I have a startup window with a dropdown to select English or French. When the user clicks OK, a method is called to switch the culture based on the selected language, and then it loads another window which will show some resource strings in the selected language.
switch (selectedLanguage)
{
case "English":
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-CA");
break;
case "French":
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-CA");
break;
}
The user can close then close this window and select a different language from the startup window and click OK to show the window again but with the new language they selected.
This is working but I also have a Resources.resx which is basically just a duplicate of Resources.en-CA.resx. I want to know if I can get rid of Resources.en-CA.resx, and if so, what do I set CurrentUICulture to so that it will use the values from Resources.resx when English is selected.
switch (selectedLanguage)
{
case "English":
//What would I do here?
break;
case "French":
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-CA");
break;
}
I'm accessing the resources strings in code using
Properties.Resources.SomeLocalizedString
Yes, you can get rid of the Resources.en-CA.resx file if you like. You don't need to change anything as your current code will continue to work in the same way.
The localization in .Net is implemented with a fallback feature from the more specific to the more generic. If It cannot find the resource file for the specified language and local (e.g. Resources.fr-CA.resx ), it looks for the resource file of the language alone (e.g. Resources.fr.resx) and if it cannot find it, it uses the default resource file (e.g. Resources.resx).
For more details, see Hierarchical Organization of Resources for Localization.

Windows Phone 8.1 app Multi language

I am creating windows phone app 8.1 in SilverLight with Visual Studio 2015. I am creating multi language app in English and Arabic. For that I have created Strings folder in the project with two folder of en-US and ar-KW with Resources.resw file in each folder.
I am x:Uid setting properties. For example Key:- Actual.Text Value:- Actual
<TextBlock x:Uid="Actual" TextWrapping="Wrap" MaxWidth="65" HorizontalAlignment="Center" />
Above is working very good. I have combobox with Item EN and AR. I am triggering SelectionChanged event to change the Language But the issue is when I stop the app and run it again than only it change the text and layout.
How can I do it at runtime without restart.
private void LanguageComboBoxName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string selectedItem = (e.AddedItems[0] as ComboBoxItem).Content as string;
var RootFrame = Window.Current.Content as SlideApplicationFrame;
if (selectedItem == "EN")
ApplicationLanguages.PrimaryLanguageOverride = "en-US";
else if (selectedItem == "AR")
ApplicationLanguages.PrimaryLanguageOverride = "ar-KW";
RootFrame.Navigate(this.GetType());
}
As far as i remember it is not recommended by Microsoft to change language in a runtime (by overriding it) but you can do it by reloading the page. The easiest way is to put your combobox with the code on another page and there you will override language, so when you navigate back (and page will be reloaded) you will have what you want. Keep in mind that overriding language will not localize controls in the runtime (but it shouldn't be a great issue for you). You must restart the app to localize controls.
When you override language with this code:
ApplicationLanguages.PrimaryLanguageOverride = "xx-XX";
then it is saved and you don't have to override it again on the start. App will load with the overrided language.
Additionally when you localize the app it is good to override culture info because if you have some dates - they are shown properly for the culture (e.g. 12/01/2015 or 01-12-2015)
string lang = "en-US"; //default
var culture = new CultureInfo(lang);
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = lang;
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
There's one more important thing.
In your app manifest or what you have there, you need to set Generate app bundle to "Never". The reason is that when you uploaded packaged version to the store then your runtime localization won't work. You may be able to localize your app in the runtime with emulator or debug versions or even sometimes with deployed release versions. But you need to do that so when your app is in the store then there is no problem with localization and all of the string for languages can be accessed (because for packaged version some of them might not be if you haven't installed the language on your phone).
If you want the app to depend on a language through user selection, you'll need to store that selection and read it out on app start.
That way you can change the ApplicationLanguage when the app starts.
So in App.Xaml.cs look for the method InitializePhoneApplication() and add following lines ( or you could also try your approach here with ApplicationLanguages.PrimaryLanguageOverride )
string savedLanguage = string.Empty;
var hasSavedLanguage = AppSettings.TryGetSetting(Constants.LanguageSettingKey, out savedLanguage);
if (hasSavedLanguage)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(savedLanguage);
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture;
}
Of course you'll also need to save the language when the user selected one from the combobox

Create Custom Cultures and then modify them using "Region and Language" dialog in Windows

I've just created and registered a new culture following the steps described in MSDN article: How to: Create Custom Cultures.
var cib = new CultureAndRegionInfoBuilder(CultureName, CultureAndRegionModifiers.None);
cib.LoadDataFromCultureInfo(new CultureInfo("cs-CZ"));
cib.LoadDataFromRegionInfo(new RegionInfo("CZ"));
cib.CultureNativeName = CultureNativeName;
cib.CultureEnglishName = CultureNativeName;
cib.NumberFormat.NumberDecimalSeparator = separator
cib.Register();
Everything looks just fine. I can see my new culture in "Region and Language" dialog in Windows. I can even load the culture back to my application.
When I change something directly in "Region and Language" dialog in Control Panel (let say NumberDecimalSeparator should be dot "." instead of comma ",") and then try to retrieve the culture:
var ci = CultureInfo.GetCultureInfo(CultureName);
ci.ClearCachedData();
ci = CultureInfo.GetCultureInfo(CultureName);
I've got still the same culture info I registered. Manual change is not projected in retrieved CultureInfo object.
Is that a proper behaviour or Am I doing something wrong?
EDIT:
If I think about it again, it is obviously the problem that I am running application in IIS under different user account than the user who is changing the culture info in Control Panel, because it is a UserCustomCulture as I've noticed.
Correct question is, how can I register a culture to be common for all users (or at least for IIS user and some Admin user)?
The reasons why I want to do this:
I want to be able to customize formatting used in the application.
I don't want to create GUI equivalent to the one in Control Panel inside my Web Application.
An administrator can simply set up formatting for the application in Control Panel.

What does this code for SetUICulture() do?

Say I want my programs to work correctly on anyone's computer in the world.
I have no clue how much work this takes but I figure it's easier to get right with a console app so I started by decompiling MSTest.exe entry point and found:
private static void SetUICulture()
{
Thread.CurrentThread.CurrentUICulture =
CultureInfo.CurrentUICulture.GetConsoleFallbackUICulture();
if (
(
(Console.OutputEncoding.CodePage != 0xfde9) &&
(Console.OutputEncoding.CodePage !=
Thread.CurrentThread.CurrentUICulture.TextInfo.OEMCodePage)
) &&
(Console.OutputEncoding.CodePage !=
Thread.CurrentThread.CurrentUICulture.TextInfo.ANSICodePage)
)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
}
}
It looks like the intention here to look at the "fallback" culture, and then look at some conditions that would prevent it from being "good enough" and if so set it to us english.
What is is the 0xfde9 doing?
Does this effectively internationalize a console app?
The 0xfde9 code page is UTF-8. So the code says, if the code page is not UTF8, OEM, or ANSI, then set the culture to US English.
Now, your application should work correctly assuming the presence of the version of the .Net Framework your application requires, but the issue is whether or not the end user will be able to understand what your application is trying to tell them.

C# Windows Forms Localization Testing

I have setup a windows forms project to use localization so that it will support Chinese and English languages. I have built in a way of forcing the language to one or the other when the form loads. Before InitializeComponent() is called I have a bit of code that does this...
switch (Properties.Settings.Default.SelectedLanguage)
{
case "":
break;
case "English":
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
break;
case "Chinese":
try
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("zh-CHT");
Thread.CurrentThread.CurrentCulture = new CultureInfo("zh-CHT");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
break;
}
So when the SelectedLanguage property is set to "Chinese" the program should use the Chinese localization right? I tested this while debugging and it worked exactly as expected. That is, when SelectedLanguage == "Chinese" at the program start all the buttons and labels display in the Chinese text that I entered. When the SelectedLangugage == "English" everything is displayed in the English Text that I have entered.
The problem is that when I install this program and run it (not debugging) it doesn't work. Even on my development machine. No matter what SelectedLanguage is set to the program always displays the English Localization. I even put in a message box to pop up at the beginning of the program which displays `Application.CurrentCulture.Name' and it shows the Chinese culture name (zh-CHT) but it still displays everything in English. So what is the difference between what happens during debugging and during actual run time? And how can i fix it?!?
Make sure that your localized resources Dll are correctly installed.
Your installer should put resources files into sub-folders such as
zh-CHT\AssemblyName.resources.dll
It sounds like the english default is being used at all times, which would suggest that the resources you have set up for Chinese are not being included in the build. Double check your resources and make sure it is included in your deployment.

Categories