How to localize string resource lookups from all threads in an application?

I am using string resources from the insert... resx file and satellite assemblies. Are you sure you are naming your files correctly?

I am using string resources from the insert... resx file and satellite assemblies. Are you sure you are naming your files correctly? Resource1.

Resx: bar Resource1. FR-fr. Resx le bar Class1.Cs : using System; using System.Collections.

Generic; using System. Diagnostics; using System. Globalization; using System.

IO; using System. Threading; namespace Frankenstein { public class Class1 { struct LocalizedCallback { private WaitCallback localized; public LocalizedCallback(WaitCallback user) { var uiCult = Thread.CurrentThread. CurrentUICulture; // wrap localized = (state) => { var tp = Thread.

CurrentThread; var oldUICult = tp. CurrentUICulture; try { // set the caller thread's culture for lookup Thread.CurrentThread. CurrentUICulture = uiCult; // call the user-supplied callback user(state); } finally { // let's restore the TP thread state tp.

CurrentUICulture = oldUICult; } }; } public static implicit operator WaitCallback(LocalizedCallback me) { return me. Localized; } } public static void Main(string args) { AutoResetEvent evt = new AutoResetEvent(false); WaitCallback worker = state => { Console.Out. WriteLine(Resource1.

Foo); evt.Set(); }; // use default resource Console.Out. WriteLine(">>>>>>>>>>{0}", Thread.CurrentThread. CurrentUICulture); Console.Out.

WriteLine("without wrapper"); ThreadPool. QueueUserWorkItem(worker); evt.WaitOne(); Console.Out. WriteLine("with wrapper"); ThreadPool.

QueueUserWorkItem(new LocalizedCallback(worker)); evt.WaitOne(); // go froggie Thread.CurrentThread. CurrentUICulture = CultureInfo. GetCultureInfo("FR-fr"); Console.Out.

WriteLine(">>>>>>>>>>{0}", Thread.CurrentThread. CurrentUICulture); Console.Out. WriteLine("without wrapper"); ThreadPool.

QueueUserWorkItem(worker); evt.WaitOne(); Console.Out. WriteLine("with wrapper"); ThreadPool. QueueUserWorkItem(new LocalizedCallback(worker)); evt.WaitOne(); } } } Output: >>>>>>>>>>en-US without wrapper bar with wrapper bar >>>>>>>>>>fr-FR without wrapper bar with wrapper le bar Press any key to continue .

. . The reason why this works is that the Resource1.

Culture property is always set to null, so it falls back to the default (IE Thread.CurrentThread. UICulture). To prove it, edit the Resource1.Designer.

Cs file and remove the following attribute from the class: //global::System.Diagnostics. DebuggerNonUserCodeAttribute() Then set a breakpoint in the Resource. Culture property accessors and the foo property accessors, and startup the debugger.

Cheers, Florian.

The real problem is with the string table lookups. I've edited the question to make this clear. – Thomas Bratt Oct 7 '09 at 13:31 I have modified the sample to work with table lookups and it is working on my machine (2.0 and 3.5)... – Florian Doyon Oct 7 '09 at 14:10 Thanks, I'll give it a go :) – Thomas Bratt Oct 7 '09 at 14:30 1 Actually no, this is just what this is doing.

But if you need to have several threads supporting different cultures, the Resource1. Culture is static, so shared by all threads. Cheers!

– Florian Doyon Oct 7 '09 at 14:58 1 Thanks :) I misunderstood the question and the answer is over-engineered. If you only need to support 1 locale for all the threads -> Set your ResourceManager. Culture property and you're good to go!

Cheers! – Florian Doyon Oct 7 '09 at 15:21.

Have you tried accessing Application. CurrentCulture instead of Thread.CurrentThread. CurrentCulture?

The real problem is with the string table lookups. I've edited the question to make this clear. – Thomas Bratt Oct 7 '09 at 13:28 Application.

CurrentCulture just delegates to Thread.CurrentThread.CurrentCulture. – adrianbanks Oct 7 '09 at 14:07.

Then if it's a socket handler, just redefine the callback type and register your async callback with the localized handler factory like this: struct LocalizedAsyncCallback { private AsyncCallback localized; public LocalizedAsyncCallback(AsyncCallback user) { var uiCult = Thread.CurrentThread. CurrentUICulture; // wrap localized = (state) => { var tp = Thread. CurrentThread; var oldUICult = tp.

CurrentUICulture; try { // set the caller thread's culture for lookup Thread.CurrentThread. CurrentUICulture = uiCult; // call the user-supplied callback user(state); } finally { // let's restore the TP thread state tp. CurrentUICulture = oldUICult; } }; } public static implicit operator AsyncCallback(LocalizedAsyncCallback me) { return me.

Localized; } } And here's your async socket handler registration boilerplate: Socket sock; AsyncCallback socketCallback = result => { }; sock. BeginReceive(buffer, offset,size, flags, new LocalizedAsyncCallback(socketCallback), state).

For anyone attempting this in the future, I've ended up with the following code: /// /// Encapsulates the culture to use for localisation. /// This class exists so that the culture to use for /// localisation is defined in one place. /// Setting the Culture property will change the culture and language /// used by all assemblies, whether they are loaded before or after /// the property is changed.

/// public class LocalisationCulture { private CultureInfo cultureInfo = Thread.CurrentThread. CurrentUICulture; private static LocalisationCulture instance = new LocalisationCulture(); private List loadedAssemblies = new List(); private static ILog logger = LogManager. GetLogger(typeof(LocalisationCulture)); private object syncRoot = new object(); private LocalisationCulture() { AppDomain.CurrentDomain.

AssemblyLoad += new AssemblyLoadEventHandler(this. OnAssemblyLoadEvent); lock(this. SyncRoot) { foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { if(LocalisationCulture.

IsAssemblyResourceContaining(assembly)) { this. LoadedAssemblies. Add(assembly); } } } } /// /// The singleton instance of the LocalisationCulture class.

/// public static LocalisationCulture Instance { get { return LocalisationCulture. Instance; } } /// /// The culture that all loaded assemblies will use for localisation. /// Setting the Culture property will change the culture and language /// used by all assemblies, whether they are loaded before or after /// the property is changed.

/// public CultureInfo Culture { get { return this. CultureInfo; } set { // Set the current culture to enable resource look ups to // use the correct language. Thread.CurrentThread.

CurrentUICulture = value; // Store the culture info so that it can be retrieved // elsewhere throughout the applications. This. CultureInfo = value; // Set the culture to use for string look ups for all loaded assemblies.This.

SetResourceCultureForAllLoadedAssemblies(); } } private static bool IsAssemblyResourceContaining(Assembly assembly) { Type types = assembly.GetTypes(); foreach(Type t in types) { if( t. IsClass && t. Name == "Resources") { return true; } } return false; } private void OnAssemblyLoadEvent(object sender, AssemblyLoadEventArgs args) { if(!LocalisationCulture.

IsAssemblyResourceContaining(args. LoadedAssembly)) { return; } lock(this. SyncRoot) { this.

LoadedAssemblies. Add(args. LoadedAssembly); this.

SetResourceCultureForAssembly(args. LoadedAssembly); } } private void SetResourceCultureForAllLoadedAssemblies() { lock(this. SyncRoot) { foreach(Assembly assembly in this.

LoadedAssemblies) { this. SetResourceCultureForAssembly(assembly); } } } private void SetResourceCultureForAssembly(Assembly assembly) { Type types = assembly.GetTypes(); foreach(Type t in types) { if( t. IsClass && t.Name == "Resources") { LocalisationCulture.logger.

Debug(String. Format( CultureInfo. InvariantCulture, "Using culture '{0}' for assembly '{1}'", this.cultureInfo.

EnglishName, assembly. FullName)); PropertyInfo propertyInfo = t. GetProperty( "Culture", BindingFlags.

GetProperty | BindingFlags. Static | BindingFlags. NonPublic); MethodInfo methodInfo = propertyInfo.

GetSetMethod(true); methodInfo. Invoke( null, new object{this. CultureInfo} ); break; } } } }.

I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.

Related Questions