How to localize search for strings from all streams in an application?

The recommended recommendation is to set your current application flow culture to enable resource discovery so that you can use the correct language.

Unfortunately, this doesn't set the culture for any other threads. This is especially problematic for streams of threads.

The question arises: how can you configure the correct localization of queries to search for strings from streams of streams of streams with the least amount of additional plumbing code?


Edit:

The problem is that this code is generated from a string table.

internal static string IDS_MYSTRING {
    get {
        return ResourceManager.GetString("IDS_MYSTRING", resourceCulture);
    }
}

      

The "ResourceCulture" in this scenario is incorrectly configured for the thread's thread. I could just call "ResourceManager.GetString (" IDS_MYSTRING ", correctCulture); but that would lose the benefit of compile-time checking that the string exists.

Now I am wondering if the fix should change the visibility of the string table to the public and set the Culture property for all assemblies listed using reflection.

+2


source to share


4 answers


For those trying to do this in the future, I got the following code:



/// <summary>
/// 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.
/// </summary>
public class LocalisationCulture
{
    private CultureInfo                 cultureInfo         = Thread.CurrentThread.CurrentUICulture;
    private static LocalisationCulture  instance            = new LocalisationCulture();
    private List<Assembly>              loadedAssemblies    = new List<Assembly>();
    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);
                }
            }
        }
    }

    /// <summary>
    /// The singleton instance of the LocalisationCulture class.
    /// </summary>
    public static LocalisationCulture Instance
    {
        get
        {
            return LocalisationCulture.instance;
        }
    }

    /// <summary>
    /// 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.
    /// </summary>
    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;
            }
        }
    }
}

      

+2


source


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

Resource1.resx:

<!-- snip-->
<data name="foo" xml:space="preserve">
    <value>bar</value>
  </data>

      

Resource1.FR-fr.resx

<--! le snip -->
  <data name="foo" xml:space="preserve">
    <value>le bar</value>
  </data>

      

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 culture for lookup
                        Thread.CurrentThread.CurrentUICulture = uiCult;

                        // call the user-supplied callback
                        user(state);
                    }
                    finally
                    {
                        // let 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 . . .

      

This is because the Resource1.Culture property is always null, so it falls back to the default (IE Thread.CurrentThread.UICulture).

To prove this, edit the Resource1.Designer.cs file and remove the following attribute from the class:

//[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]

      

Then set a breakpoint on the Resource.Culture accessor and foo property accessors and start the debugger.

Cheers, Florian

+1


source


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

0


source


Then, if it's a socket handler, just override the callback type and register an async callback with a localized factory handler 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 culture for lookup
                    Thread.CurrentThread.CurrentUICulture = uiCult;

                    // call the user-supplied callback
                    user(state);
                }
                finally
                {
                    // let restore the TP thread state
                    tp.CurrentUICulture = oldUICult;
                }
            };

        }

        public static implicit operator AsyncCallback(LocalizedAsyncCallback me)
        {
            return me.localized;
        }
    }

      

And here is your async socket handler registration template:

Socket sock;
AsyncCallback socketCallback = result => { };
sock.BeginReceive(buffer, offset,size, flags, new LocalizedAsyncCallback(socketCallback), state);

      

0


source







All Articles