I'm working on a WPF / C# app that needs to be culturally aware for globalization. I already have resource files and a bindable translation manager that is all working as expected.
At the moment I'm doing this:
Thread.CurrentThread.CurrentUICulture = _currentlyConfiguredUiCulture;
Thread.CurrentThread.CurrentCulture = _currentlyConfiguredUiCulture;
This is all wired up in the UI like this:
TranslationManager.Instance.LanguageChanged += TranslationManager_LanguageChanged;
private void TranslationManager_LanguageChanged(object sender, EventArgs e)
{
Thread.CurrentThread.CurrentUICulture = TranslationManager.Instance.CurrentLanguage;
Thread.CurrentThread.CurrentCulture = TranslationManager.Instance.CurrentLanguage;
}
This all works sweet!
The problem is when the app is started my machine locale is "en-GB" and this is correctly set using the code shown above. However, when I hit some code I have in an IValueConverter class dealing with dates:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
return DateTime.Parse(value.ToString(), culture);
}
return null;
}
The culture property here is always "en-US" ... how on earth is this occurring? How do I fix this so that the app is actually using the correct system culture?
This link states that you might have to add the following:
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(
System.Globalization.CultureInfo.CurrentCulture.IetfLanguageTag)));
If you created a thread, then the culture info is reset to the default one(for that thread). I think in .net 4.5 there might be a way to set the default thread cultureinfo.
http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.defaultthreadcurrentculture.aspx
Related
I'm sitting in front of the following unit test without getting it to work properly
[TestMethod]
public void EvenIndexesZeroShouldHaveWhiteBackground()
{
var converterBinding = new Binding("BackgroundConverter");
converterBinding.Converter = new BackgroundConverter();
var lvi0 = new ListViewItem() { Background = Brushes.Gray };
var lv = new ListView();
lvi0.SetBinding(ListViewItem.BackgroundProperty, converterBinding);
lv.Items.Add(lvi0);
Assert.AreEqual(Brushes.White, converterBinding.Converter.Convert(lvi0, null, null, CultureInfo.InvariantCulture));
}
I was able to get another converter tested by directly calling the Convert(...) method, but it received a simple data type.
I have the feeling that I somehow need to trigger the converter when adding lvi0to the ListView(or manually afterwards) but I don't know how to do it.
Can anyone point me in the right direction?
I'm new to WPF and haven't fully gotten my head around the Bindings and Dependency Properties yet :(
[UPDATE]
The current problem is that the Convertmethod isn't called. It's not the content of the converter or the result it is giving back.
[UPDATE 2]
#Tatranskymedved comment pointed me into the right direction and calling the converter directly (as proposed by #PeterDuniho) now works. I have updated the code snippet above accordingly.
[UPDATE 3]
Here is the Converter. I HAVE to pass in a ListViewItem since this is what the it is working on. Changing it is currently not an option.
public class BackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ListViewItem item = value as ListViewItem;
if (item == null) return Brushes.White;
ListView listView = ItemsControl.ItemsControlFromItemContainer(item) as ListView;
// Get the index of a ListViewItem
if (listView == null)
return Brushes.White;
int index = listView.ItemContainerGenerator.IndexFromContainer(item);
if (index % 2 == 0)
{
return Brushes.WhiteSmoke;
}
return Brushes.White;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
The basic idea is: In WPF You are using some Window/UserControl, where are layouts and controls. If any of the controls should have binded its property to the ViewModel, property of the Control must be defined as DependencyProperty. If You are just using some already defined controls, You do not need to known them.
When You are creating own UserControl, it has to have a DependencyProperty, so You can bind one end to it.
Now You have to realize, what do You want to test. Is it the binding? Or the converter itself?
For the binding test, You can refer to this: Unit test WPF Bindings
Or: http://www.wpf-tutorial.com/data-binding/debugging/
However, talking about unit tests, You should test the Converter directly instead of putting them to the complicated chain objects like Binding. That is the basic motivation if the test won't work, You can say "the problem is with the Converter", not with the binding or the object You will bind to.
Only thing You need to check if the type of value You setting is the correct one. For WPF Control's BackgroundProperty it should be System.Windows.Media.Brush as on MSDN.
I have a DropDown (using MahApps if that is important) that I'd like to use to switch the language "on the fly" in my program.
Language Class
namespace SAM
{
public class Language
{
public string Title { get; set; }
public string Culture { get; set; }
}
}
Change Language
private void DropLanguage_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
Language lang = DropLanguage.SelectedItem as Language;
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang.Culture);
}
I have all my strings as resources in Resources.resx (default) and Resources.en.resx (english)
3 issues I don't understand
When selecting "englisch" from the dropdown, the language does not change immediately but when I click sth. else, e.g. "close" (it asks "sure?"), the language has changed.
Strings that are directly in the .xaml file like <TextBlock Text="{x:Static p:Resources.Config_HeaderBar_Find_Speaker}" /> do not get updated at all.
Bonus: How would I switch back to the default language, as new CultureInfo(lang.Culture); expects one parameter and for the default I have Culture = null (as the Resources.resx has nothing in it's name). Changing the file to Resources.default.resx messes with my code a lot...
I tried to solve similar problem. The simplest solution for me was to move all Window content to UserControl and creating interface for window with method refreshLanguage(). Then I call from model:
private void SetLanguage(string cultureName)
{
var cul = new System.Globalization.CultureInfo(cultureName);
Properties.Resources.Culture = cul;
System.Globalization.CultureInfo.DefaultThreadCurrentCulture = cul;
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = cul;
System.Threading.Thread.CurrentThread.CurrentUICulture = cul;
System.Threading.Thread.CurrentThread.CurrentCulture = cul;
InitializeHamburgerMenu();
MainWindowView.RegreshLanguage();
RaisePropertyChanged("Title");
}
And RefreshLanguage in Window looks like:
public void RegreshLanguage()
{
GlobalUserControl content = new GlobalUserControl("Views/SettingsPage.xaml");
ContentPresenter_GlobalUserControl.Content = content;
}
In my case, UserControl provides navigation, so I passed last navigated URI as parameter. So, if you need to preserve state you can pass it as parameter to new UserControl. Recreating usercontrol cause all strings to reload without window recreation. Maybe good idea would be to call GC.Collect(); here, but depends on your scenario.
About default neutral culture. For me works to call SetLanguage("").
There are 2 things
Thread.CurrentThread.CurrentCulture
and
Thread.CurrentThread.CurrentUICulture
In order to set these valuse use the static method
CultureInfo.GetCultureInfo(String langCode)
where some examples of the parameter langCode are the following strings
"de-DE"
"en-US"
etc.
more info at
https://msdn.microsoft.com/en-us/library/yck8b540(v=vs.110).aspx
So, overall these lines of code sould work for change in German Language:
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("de-DE");
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("de-DE");
Is it possible to implement different language into UWP application from code behind using multilingual toolkit & not by setting the desired language from the settings but instead from the dropdown list of language within the application
You don't actually need the Multilingual App Toolkit to modify the language of your app. By default, the app will configure itself based on the settings on the machine, but you can override that by setting the CurrentCulture and/or CurrentUICulture.
Say for instance you had a Combobox that contained "English", "Spanish" and "French"... and your desired behavior is to switch your language to whichever value the user selects. All you'd need to do is hook up the SelectionChanged event. Here's what the code might look like:
private void ChangeLanguage(object sender, SelectionChangedEventArgs e)
{
var newlySelected = e.AddedItems[0] as ComboBoxItem;
string newLanguage = newlySelected.Content.ToString();
switch (newLanguage)
{
case "English":
{
CultureInfo.CurrentCulture = new CultureInfo("en");
CultureInfo.CurrentUICulture = new CultureInfo("en");
break;
}
case "Spanish":
{
CultureInfo.CurrentCulture = new CultureInfo("es");
CultureInfo.CurrentUICulture = new CultureInfo("es");
break;
}
case "French":
{
CultureInfo.CurrentCulture = new CultureInfo("fr");
CultureInfo.CurrentUICulture = new CultureInfo("fr");
break;
}
default:
{
throw new NotImplementedException("Unidentified Language");
}
}
}
Naturally, I'd encourage you to do all the appropriate error checking (Make sure that the cast to ComboBoxItem works, etc...).
Also, remember that you need to set CurrentCulture if you want things like Dates and Times to show in the appropriate locale and use CurrentUICulture if you want to modify which resources your ResourceLoader uses to populate the UI.
Hopefully this should get you up and running!
--Dante
I have an application, which is based for India, and I'm setting Culture as:
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-IN");
The above code is called before the Window.InitializeComponent() method is called.
Still this is showing $ as CurrencySymbol in all TextBoxes.
If I bind a TextBox as following, it shows Rs. as CurrencySymbol:
Text="{Binding Salary,Mode=TwoWay,StringFormat=C,ConvertCulture=en-IN}".
I think you will need to add the following.
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-IN");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-IN");
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
Read more here:
http://www.west-wind.com/weblog/posts/2009/Jun/14/WPF-Bindings-and-CurrentCulture-Formatting
Just to give you an example, this is how I initialize the Culture in my program, based on the user setting, but you can simply replace UserSettings.DefaultCulture and UserSettings.Default.UICultrue with your wanted Culture.
private static void InitializeCultures()
{
if (!String.IsNullOrEmpty(UserSettings.Default.Culture))
{
Thread.CurrentThread.CurrentCulture = new CultureInfo(UserSettings.Default.Culture);
}
if (!String.IsNullOrEmpty(UserSettings.Default.UICulture))
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(UserSettings.Default.UICulture);
}
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
}
For me just works, if i put this code to the OnStartup overrided method:
public partial class App : Application
{
public App()
{
}
protected override void OnStartup(StartupEventArgs e)
{
var vCulture = new CultureInfo("de-DE");
Thread.CurrentThread.CurrentCulture = vCulture;
Thread.CurrentThread.CurrentUICulture = vCulture;
CultureInfo.DefaultThreadCurrentCulture = vCulture;
CultureInfo.DefaultThreadCurrentUICulture = vCulture;
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
base.OnStartup(e);
}
}
Thread.CurrentThread.CurrentCulture =
System.Globalization.CultureInfo.GetCultureInfo("en-IN");
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement),
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
This will switch the default language for the entire application. You’ll want to use this only in startup code as this setting can be applied only once per application. You can still override individual forms when necessary as below
this.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag);
All WPF elements include a Language property that can be assigned and
determines the Culture that is used for formatting.
Reference
To me just this worked, but in order to solve ToString and to make it work over the entire app, it's important to add it in constructor, not OnStartup etc, before you set up service container etc. otherwise it doesn't work in subsequent threads and CultureInfo.CurrentUICulture still resolves to the default system CultureInfo.
public class App : Application
{
public App()
{
var culture = new CultureInfo("en-IN");
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(culture.IetfLanguageTag)));
// should be before all this
var host = Host
.CreateDefaultBuilder()
.ConfigureServices(ConfigureServices)
...;
}
}
For my datagrid data i used below lines of code in App.xaml.cs & it worked ..
for de it displayed dot and En it displayed comma for 4 digit numbers.
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
Also, there's another way of doing this, assuming that you're using a TextBlock:
<TextBlock Text="{Binding Salary, Mode=TwoWay, StringFormat='{}{0:C}', ConverterCulture='en-IN'}" />
Please bear in mind the single quotes are needed.
I'd like to programmatically set the culture of my User Control which defines several Labels, Buttons and Textboxes ...
Usually, for aspx-pages you override the InitializeCulture-Method and set Culture and UICulture to achieve this, alas ASCX-Controls do not have this Method, so how exactly would I do this?
I've set up the local resources mycontrol.ascx.de-DE.resx, mycontrol.ascx.resx and mycontrol.ascx.en-GB.resx but only the values of the default file (mycontrol.ascx.resx) are used.
Thanks in advance.
Dennis
The current culture is thread-wide: Page.Culture and Page.UICulture actually set Thread.CurrentThread.CurrentCulture and Thread.CurrentThread.CurrentUICulture under the hood.
If the user control is defined in your page markup, I don't think there's anything you can do. If you load it with LoadControl(), you can temporarily override the current thread's culture before the call and restore it afterward, but it would be quite awkward:
protected void Page_Load(object sender, EventArgs e)
{
// Some code...
CultureInfo oldCulture = Thread.CurrentThread.CurrentCulture;
CultureInfo oldUICulture = Thread.CurrentThread.CurrentUICulture;
Thread.CurrentThread.CurrentCulture = yourNewCulture;
Thread.CurrentThread.CurrentUICulture = yourNewUICulture;
try {
Controls.Add(LoadControl("yourUserControl.ascx"));
} finally {
Thread.CurrentThread.CurrentCulture = oldCulture;
Thread.CurrentThread.CurrentUICulture = oldUICulture;
}
// Some other code...
}
I also spent hours on this problem and finally got This solution.
You only have to override the FrameworkInitialize() instead of initilizeculture(),
eg:
protected override void FrameworkInitialize()
{
String selectedLanguage = LanguageID;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(selectedLanguage);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(selectedLanguage);
base.FrameworkInitialize();
}
Jus Put this code inside your ascx.cs file, This override function does not need to be called.
I used the following code to change the language for asp.net mvc in my project page
public ActionResult ChangeCulture (Culture lang, string returnUrl)
{
if (returnUrl.Length> = 3)
{
returnUrl = returnUrl.Substring (3);
}
return redirect ("/" + lang.ToString () + returnUrl);
}
also I used usercontrol (ascx) on the same page.when I changed language with actionlink is changed viewpage language but user control pageload event isn't capture changing so viewpage's language changes but usercontrol's language doesn't change