Using `ResHelper` to get resource string uses thread culture not LocalizationContext - c#

Using Kentico CMS with a multi culture site, we have custom code to get a resource string, when we use ResHelper.GetString("someKey") it does not use LocalizationContext.CurrentCulture version of the string, it is always defaulting to the US culture.
When I pass in the culture code to use as the second parameter using Localization.CurrentCulture.CultureCode it returns the correct string?
Using a quick watch and breakpoint at the code, I can see that Thread.CurrentThread.CurrentCulture and CurrentUICulture are both set to US whilst LocalizationContext.CurrentCulture is set to which ever version of the website we are viewing.
Why wouldn't Kentico set the Thread culture if resource strings rely on it?
We have used many resource strings throughout the site, some are using built in cms controls <cms:LocalizedLabel ResourceString="someKey" /> - how do these controls work?
Thanks

Related

Whats the difference of NeutralResourcesLanguageAttribute and uiCulture in a asp.net mvc project

I have created an asp.net MVC4 web project which must be available in more than one language.
I want to set the default language for this web project.
After some research, it seems the default language can be set in two places.
NeutralResourcesLanguageAttribute in AssemblyInfo
[assembly: NeutralResourcesLanguageAttribute("en")]
From msdn: Informs the resource manager of an app's default culture. This class cannot be inherited.
globalization uiCultur in web.config
<system.web>
<globalization uiCulture="en"/>
From mdsn: Specifies the default culture for processing locale-dependent resource searches. For valid culture strings, see System.Globalization.CultureInfo Class.
Now my questions:
What is the correct way to set the default language in a web project(NeutralResourcesLanguageAttribute or globalization uiCultur) ?
Example resource files
Stuff.resx (contains english, is the default resources file)
Stuff.de.resx (contains german)
Stuff.fr.resx (contains french)
Now in web.config use
<globalization
enableClientBasedCulture="true"
uiCulture="auto:en"
culture="auto:en" />
This ("auto") will let the browser settings decide (german browsers gets the german resources, etc)
However (in the above example) if a Swedish browser comes it will default to en as there is no se (swedish) resource file and the fallback english ("auto: en ") uiCulture will be used.
So... then:
[assembly: NeutralResourcesLanguageAttribute("en")]
lets the application now that english requests go direct to the default.resx with no other lookup for en resx first.
Also if an english browser is requesting it should go directly to the default resource file without trying something else first. So the NeutralResourcesLanguageAttribute setting comes in play also here. The app knows to go to the default resx directly.
According to this article the important setting(s) for an Asp.Net web application are in the web.config in the "globalization" element. The article suggests setting both the culture and the uiCulture as a best practice. For example:
<globalization uiCulture="es" culture="es-MX" />
NeutralResourcesLanguageAttribute is ONE way to let the application's resource manager know the default culture and resource location of your app. This attribute can be used for any type of .NET app: desktop, web, win8, wcf, etc.
The globalization attribute is another way to let your web application know what the default culture for your app is.
Note then that they key difference is that the globalization attribute only works for web apps and does not allow you to specify a different location for resources.
Recommendation: For a web project, I'd use the config attribute.
The NeutralResourcesLanguage attribute informs the ResourceManager of the language that was used to display the resources of the neutral culture for an assembly. When it looks up resources in the same culture as the neutral resources language, the ResourceManager automatically uses the resources that are located in the main assembly.
It does this instead of searching for a satellite assembly that has the current user interface culture for the current thread. NeutralResourcesLanguage attribute improves lookup performance for the first resource that you load and can reduce your working set.
from http://technet.microsoft.com/en-us/subscriptions/bb385967.aspx

Localization using resources file not working

I added new Rosource file UserNotification.resx. Then I added two files for localization and named it UserNotification.hr-HR.resx and UserNotification.sl-SI.resx. (followed thread: How to use localization in C#)
Then I changed Windows language's to "si". If i print current culture using System.Globalization.CultureInfo.CurrentCulture.ToString() the output si sl-SI
But, I allways get a string from UserNotification.resx. Seams it doesn't recognizes the localization resource files.
The problem in .NET localization if you use multithreading is that you need to set culture manualy for every thread that you spawn (in case it is different than windows culture, in case when user can choose desired language). It is not inherited from the main thread.
After you create new thread and before you start it you should set it's Culture to the Culture of main thread (which normaly uses the default culture of the windows).
So, system shows good CurrentCulture. But what does current thread says? Maybe application is started with different culture for some reason so that's why it loads default resources file? Anyway, there is no reason why you shouldn't manualy set current culture. Mostly you need to do that when you want to give user the option to change application's language.

c# : In a dotnet class is there a property that states if the "Current" culture is actual the default culture?

Is there a property in some class that can tell me if the current culture is actually the default culture.
Similar to how localization works with winforms. It states in a form if the language is default.
lets say if i am in en-US - how can i tell via code if en.US is the actual default?
I need to implement some localization for some XML files which .net doesn't support hence i want to implement my own ...
And do it how winforms works i.e
nameofxml.default.xml (this is the default local)
nameofXml.de-de.xml (this is german)
etc
does a property exist?
System.Globalization.CultureInfo.CurrentCulture indicates you system's Control Panel Region settings, while System.Globalization.CultureInfo.CurrentUICulture corresponds to the system's Input UI Language setting (which you can't change unless you have Multilingual User Interface installed).
Therefore you can use the following code snippet to determine the 'default' culture:
using System.Globalization;
// N.B. execute the following line *before* you programmatically change the CurrentCulture
// (if you ever do this, of course)
// Gets the CultureInfo that represents the culture used by the current thread
CultureInfo culture = CultureInfo.CurrentCulture;
string code = culture.IetfLanguageTag; // e.g. "en-US", "de-DE", etc.
Then you can use the code to locate your .xml files.
You can state at the assembly level that the embedded resources are of a specific culture by using the NeutralResourcesLanguageAttribute.
IIRC, this way the resource manager can optimize the process of resource loading if the required culture is the one embedded in the assembly.
Since you are rolling your own implementation for localization I don't know how this can be helpful, but you can use that attribute to indicate that the culture of the XML localization information embedded directly in the assembly is of a specific culture and default to searching satellite assemblies or other custom back store if you find a mismatch between the declared culture in the assembly and the required culture you are looking for.
CultureInfo.CurrentCulture is a static member that "Gets the CultureInfo that represents the culture used by the current thread." It is accessible from any class if you just include the System.Globalization namespace.
Documentation: http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.aspx

How to load different RESX files based on some parameter

I have an ASP.NET3.5 (C#) ASPX page that is internationalized in 10 different languages.
The page is very complex in its structured with dozens of nested views driven by a state machine pattern.
EDIT: I am using the meta:resourcekey syntax in every asp control, which allows to use declarative syntax for Implicit Resource expressions.
I have been asked to "brand" the page based on some query string parameter. Branding will mean not just loading different CSS files, but also having different text blurbs (in all languages).
Is there an easy way to "swap" resx files without having to get resources manually for each of the hundreds of literals and images that I have on this page?
In other words, let's say I have the following RESX files:
brand1_myPage.aspx.en-US.resx
brand1_myPage.aspx.de-DE.resx
brand1_myPage.aspx.fr-FR.resx
brand2_myPage.aspx.en-US.resx
brand2_myPage.aspx.de-DE.resx
brand2_myPage.aspx.fr-FR.resx
myPage.aspx will be looking for resx files named myPage.xx-XX.resx.
Is there a way to load instead either the brand1xxx.resx files or the brand2xxx.resx based on some value?
Thanks in advance.
You can use custom cultures to achieve this effect.
First, create and register custom cultures in the system, for example:
CultureAndRegionInfoBuilder builder = new CultureAndRegionInfoBuilder("en-US-brand1", CultureAndRegionModifiers.None);
CultureInfo parentCI = new CultureInfo("en-US");
RegionInfo parentRI = new RegionInfo("en-US");
builder.LoadDataFromCultureInfo(parentCI);
builder.LoadDataFromRegionInfo(parentRI);
builder.Parent = parentCI;
// set other properties of the custom culture (CultureEnglishName, CultureNativeName, possibly other ones)
// ...
builder.Register();
Note that you might want to build a simple tool to automate this, since those cultures need to be installed on every system where your aplication will be compiled or executed. Administrative rights are needed to be able to register the cultures.
Once you have the cultures installed, create resx files like you would normally do, but use the custom culture names (myPage.aspx.en-US-brand1.resx etc).
Now all that's left to do is to set System.Threading.Thread.CurrentThread.CurrentUICulture based on some parameter (the sooner the better, BeginRequest is a good place; or Page_PreInit if you want this only for some pages):
CultureInfo ci = new CultureInfo(Request.QueryString["paramname"]);
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
(setting CurrentCulture is not really necessary, as resources work in terms of CurrentUICulture, but setting both allows you to further customize the page for each brand, e.g. use different date/time format settings for each custom culture/brand)
Some notes:
this solution gives you great flexibility, since normal culture fallback is in effect - if an entry is not found for en-US-brandX, the runtime will fall back to en-US and so on; this can greatly reduce duplicate resx entries if the brands are mostly similar, as you can put some entries only in the parent (en-US) resx file,
you can create more levels of inherited cultures, eg en-US-brandX-variantY,
all methods for accessing resources work as expected,
changing the culture of the worker thread means you will get localized exception messages if you set the culture to, say, de-DE-brandX and you have de-DE localization installed in the OS,
for the above reason, you might want to reset the current (UI) culture to CultureInfo.InvariantCulture in Application_Error, or even better also as soon as you catch an exception which you know will lead to Application_Error; this will prevent localization of the standard yellow page of death and at least part of the exception stack,
you might consider building a service tool which will register/unregister/update the cultures, especially if you anticipate frequent changes,
this solution might be problematic if you use client-based culture detection.
If you reference resources in your code like this: Resources.brand1_myPage.WelcomeMessage then it may be difficult.
But you can also retrieve resources programmatically: GetGlobalResourceObject ("brand1_myPage", "WelcomeMessage"). Here you have some room for manipulation with the resource file name.

How does a .NET process get the culture information?

I have a Windows service running (C#, .NET 2.0) on Windows Server 2003 R2. In one server the System.Threading.Thread.CurrentThread.CurrentCulture is {en-AU} and in the other {en-US}.
This has caused a difference when calling ToString() on a DateTime object. I want the culture to be {en-AU}.
I checked the "Regional and Language Setting". In both servers, the "Regional Options" tab shows "English (Asutralia)". But in the "Advanced" tab it shows "English (United States)" for one and "English (Australia)" for the other. So this must be causing the difference. Although I want to know why exactly the "Advanced" tab says "the language version of the non-unicode programs you want to use", I thought .NET processes were Unicode and should not be affected by this.
How does the .NET runtime determine the culture to use? Any detailed reference would be helpful.
If a culture has not been set on the thread, Thread.CurrentThread.CurrentCulture defaults to the "user default culture" - which it gets from the underlying OS. This is determined by the Formats section in the regional control panel applet.
For a service, it has no control panel settings by default like for a user (the case above) as it runs under the LocalSystem account which will not have a profile, so it uses the system locale from the OS. I'm not sure if this can be set for a service by adjusting the settings in Windows.
There are a few things you could do:
you can explicitly set the CurrentCulture of the main thread when the service starts. If you do this, you will need to bear in mind that any new threads that are created in your service will also need to have their CurrentCulture set as well, as threads do not inherit their culture from parent threads.
you can set the service to run as a specific user, and set that user's regional settings (the formats section) to be the culture you want to use. When the service starts as that use,it will use that user's regional settings.
since your problem seems to be related to calling DateTime.ToString(), make sure you pass the AU culture to the ToString() method:
DateTime.ToString(new CultureInfo("en-AU"))
You could add this as an extension method to save you having to do this everywhere:
public static string ToAUString(this DateTime dateTime)
{
return dateTime.ToString(new CultureInfo("en-AU"));
}
You can then call DateTime.ToAUString() to get the correct output.
In my case it took only one line of code to change the Culture:
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo( "en-US" )
It changes default Culture of Main thread and new ones as well.
This MSDN page on CultureInfo has some information that might be relevant:
The user might choose to override some of the values associated with the current culture of Windows through the regional and language options portion of Control Panel. For example, the user might choose to display the date in a different format or to use a currency other than the default for the culture. In general, your applications should honor these user overrides.
If UseUserOverride is true and the specified culture matches the current culture of Windows, the CultureInfo uses those overrides, including user settings for the properties of the DateTimeFormatInfo instance returned by the DateTimeFormat property, and the properties of the NumberFormatInfo instance returned by the NumberFormat property. If the user settings are incompatible with the culture associated with the CultureInfo, for example, if the selected calendar is not one of the OptionalCalendars, the results of the methods and the values of the properties are undefined.
I think this might be a good starting point for your investigations.

Categories

Resources