i have two Resources files in the Properties folder of a WPF-project (VS 2008):
Resources.resx
Resources.de-DE.resx
Selecting the culture "de-DE" does not work (no error, but always the strings from "Resources.resx" are used):
public App()
{
UntitledProject2.Properties.Resources.Culture = new CultureInfo("de-DE");
}
BUT: if I rename "Resources.de-DE.resx" to "Resources.fr-CA.resx" or "Resources.en-US.resx"
and then set it via
UntitledProject2.Properties.Resources.Culture = new CultureInfo("fr-CA");
it works!! But why!? Mysterious...
By default, WPF will always use "en-US"; at least it did the last time I checked (which was .net 3.5). If you want WPF to instead use the culture currently set by the system, you would execute this code block:
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(
CultureInfo.CurrentCulture.IetfLanguageTag)));
This will override the default value used for the FrameworkElement's Language dependency property, which, again, is "en-US" by default.
Execute this code once and early on in the lifetime of your application. AFter that, you shouldn't have to worry about it again, unless you expect your user to be switching the culture in the middle of program execution...
Related
I know it's easy to localize Windows Forms App: set Localizable=True, change Language and set text in Controls for every Language. This information saves in resx-files and application will automatically select the required file. Great!
I know about disadvantages of this solution (you need to rebuild the app if there a typo, it's impossible to change language in runtime, etc), but it's not a problem for me and "resources" is the simpliest, built-in solution.
But this mechanism uses the property Culture of app's thread.
My app is the part ("plugin") of the bigger application and works in the same Thread.
The main application is multilingual too, but it doesn't use Culture to change interface's language. I can change the thread's culture globally, but it crushes the main app's interface.
So my question:
is it possible to manually set the resx-localizable resurce file that will be used? Not based on Culture, but, for example, on some variable in my app:
if (this.Language == "fr")
this.Resources.Add("Form1.fr.resx");
else
this.Resources.Add("Form1.en.resx");
Or something else.
Thank you!
My sandbox:
https://github.com/Tereami/WindowsFormsTestLanguage
The built resources file has a property ResourceManager that's used to return the desired content. This has an overload with a CultureInfo parameter. You can use it to request resources in individual languages:
var desiredCulture = new CultureInfo("en-us");
var text = MyStrings.ResourceManager.GetString(nameof(Resources.ExitMessage), desiredCulture);
If you want to set the culture globally for your resource file, you could also set it through the corresponding property:
var desiredCulture = new CultureInfo("en-us");
MyStrings.Culture = desiredCulture;
var text = MyStrings.ExitMessage;
Consider the following simple WPF application:
XAML (MainWindow):
<StackPanel>
<TextBlock x:Name="Old"/>
<TextBlock x:Name="New"/>
</StackPanel>
Codebehind:
public MainWindow()
{
InitializeComponent();
SystemEvents.UserPreferenceChanged += (sender, args) =>
{
Old.Text = $"Old culture name {CultureInfo.CurrentCulture.Name}";
CultureInfo.CurrentCulture.ClearCachedData();
New.Text = $"New culture name {CultureInfo.CurrentCulture.Name}";
};
}
Each time the user changes their regional settings it will print to Old TextBlock the old culture's name and, after calling ClearCachedData, the new culture's name will be set on New TextBlock. This is the window where I change these settings in Clock and Region section within Control Panel:
Now, if you build targeting 4.5.2 or less, it will work as expected and you will receive, after actually changing the setting, an output similar to this:
Now, build targeting 4.6 or later versions, you get something like this:
If you are targeting 4.6 or later, no matter how many times you change the number format region, no matter how many times you call ClearCachedData, CurrentCulture will not change...
But wait! Things get weirder... If you put above code into a Console App (using Console.WriteLine instead of course), it works not depending in which framework you chose (4.5.2, 4.6, anything..), strange isn't it? That is why I wrote "WPF only" in the title.. but I do not know for sure if this happens in any other platform other than WPF
There is still another crazy thing in this tale, since I have been playing around with this issue since yesterday, I found a trikcy way to make WPF or Console apps, targeting 4.5.2 or below, working wrong, just as the one targeting 4.6 and older. Simply setting CultureInfo.DefaultThreadCurrentCulture to anything different from null will break things apart.
public MainWindow()
{
InitializeComponent();
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CurrentCulture;
SystemEvents.UserPreferenceChanged += (sender, args) =>
{
Old.Text = $"Old culture name {CultureInfo.CurrentCulture.Name}";
CultureInfo.CurrentCulture.ClearCachedData();
New.Text = $"New culture name {CultureInfo.CurrentCulture.Name}";
};
}
The above code will behave as if it were targeting 4.6. So, what I thought was that WPF targeting 4.6 was setting CultureInfo.DefaultThreadCurrentCulture in some way, but after testing this theory I am really stuck, because it is null by default, event in 4.6 and older...
So, is this a bug in WPF and/or .NET 4.6? I have not been able to find this breaking change reported.
Please, anything that shed some light on this will be appreciated. Also, if this is a bug and anyone know where could I report it, please tell me.
Note: I tag this question with C# as it is the language I am using, although I consider this issue is language independent.
For those visitors from the future and beyond who may will be having the same issue. I found a sort of workaround:
public MainWindow()
{
InitializeComponent();
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CurrentCulture;
SystemEvents.UserPreferenceChanged += (sender, args) =>
{
Old.Text = $"Old culture name {CultureInfo.CurrentCulture.Name}";
CultureInfo.CurrentCulture.ClearCachedData();
CultureInfo.CurrentCulture = new Thread(() => { }).CurrentCulture;
New.Text = $"New culture name {CultureInfo.CurrentCulture.Name}";
};
}
The above code will work in both building options: 4.5.2 and earlier, or 4.6 and later. Notice that the call to ClearCachedData is still a must. We create a new thread instance and we manually update our CurrentCulture to that of the new thread.
Happy coding!
I've localized my App in two languages (English and German) with the MulitlingualAppToolkit 4.0. English is the base language, while german is a translation based on the english one.
The translations are stored as resw-file inside folder "strings.en" and "strings.de".
In App.xaml.cs App() I set the culture like this:
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = GetCurrentCulture();
CultureInfo.CurrentUICulture = GetCurrentCulture();
private CultureInfo GetCurrentCulture()
{
var cultureName = new DateTimeFormatter("longdate", new[] {"US"}).ResolvedLanguage;
return new CultureInfo(cultureName);
}
(I got this quiet weird way to the regional-culture in Windows 10 from this article https://www.pedrolamas.com/2015/11/02/cultureinfo-changes-in-uwp/ since I recognized that CultureInfo.CurrentCulture and CultureInfo.CurrentUICulture are always "en-EN" no matter what i configurate in my machines regional- and language-settings)
To check if PrimaryLanguageOverride works as expected, I added a TextBox by the name of tbTest on my first Page and a button linkt to this event:
private void Button_Click(object sender, RoutedEventArgs e)
{
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = tbTest.Text;
Frame.Navigate(this.GetType(), 0);
System.IO.File.AppendAllText(System.IO.Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "PrimaryLanguageOverride.txt"),
DateTime.Now + " - Actual PrimaryLanguageOverride:\n " + WIndows.Globalization.ApplicationLanguages.PrimaryLanguageOverride);
}
catch(Exception ex) { Helper.Log.LogUnhandledError(ex); }
}
Now comes the weird stuff:
When Debugging, or executing the App from my Development-Machine, everything works as expected, but when I make an appx-bundle and install it on another (Windows10-Desktop) device, the App does not recognize the its Language.
In my situation, the device is set to german, regional as well as its language. Also when using the test-procedure, it defenetively gets the string I set , as long as it's a valid culture-name, e.g.: "en-EN", "en-US", "de-DE", "de", "en" (all of these are working fine on my development machine) if it's an invalid string, I get an exception, with a log-entry in my unhandled-error-log. It refresh the Page, and even writes the new-set language in my PrimaryLanguageOverride-Log, but it doesn't change any text I did translate.
So my question is, is this a common Issue (since I recognized in UWP/Win10 the culture-system is a little messed up) or do I use the wrong procedure to override the App-Culture?
This is an issue with AppXBundling. When Installing bundles, it checks with the current OS for the installed Language packs and copies the relevant language resources from the bundle and omits the other language files. The objective of a single bundle is to copy necessary resources and build the application and therefore the languages which are not in the system are considered irrelevant. As a fix you could stop generating single bundles and create package for each CPU architecture. Check this for more info
I have a localization file that I created that works fine, I called it:
Labels.resx
Now in my global.asax.cs, if a particular querystring value is present, I change the language to french:
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-CA");
I created a new resource file:
Labels.fr-CA.resx
I just put a single entry in the new french local file to test it, now when the controller action executes, while debugging I can see in the immediate window that the culture has changed correctly, but my text label is not in french.
What could the issue be?
The properties for my Labels.fr-CA.resx file are exactly like my Labels.resx file:
embedded resource
PublicResXFileCodeGenerator
(same namespace)
Also, if the given key isn't found, does it automatically lookup the value in the Strings.resx key as a fallback or does it cause an exception?
Try add the folowing line to global.ajax.cs:
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-CA");
I want to localize my Windows Store app. I have string\cs-CZ\Resources.resw and I load the strings in C# using ResourceLoader. When I have my system set to cs-CZ locale, the string are returned ok. When I set to to another locale, the GetString method returns an empty string. I have my projects neutral solution set to cs-CZ so what is the problem? How do I make the app always take resources from string\cs-CZ\Resources.resw?
The default locale can be programatically set using
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "cs-CZ";
in OnLaunched