.net - 如何本地化应用程序中所有线程的字符串资源查找?

标签 .net localization multithreading threadpool

建议的最佳实践是设置应用程序线程的当前区域性,以使资源查找能够使用正确的语言。

不幸的是,这不会为任何其他线程设置区域性。这对于线程池线程来说尤其是一个问题。

问题是:如何使用最少的额外管道代码从线程池线程中正确本地化启用字符串资源查找?


编辑:

问题是这段代码是从字符串表生成的。

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

在这种情况下,线程池线程的“resourceCulture”设置不正确。我可以调用 'ResourceManager.GetString("IDS_MYSTRING", CorrectCulture);'但这意味着失去编译时检查字符串是否存在的好处。

我现在想知道解决方法是否是将字符串表可见性更改为公共(public),并设置使用反射枚举的所有程序集的 Culture 属性。

最佳答案

对于将来尝试此操作的任何人,我最终得到了以下代码:

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

关于.net - 如何本地化应用程序中所有线程的字符串资源查找?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1530506/

相关文章:

c# - 从表中删除行而不先检查它们是否存在

c# - 获取文件夹的根目录 +1

c# - 使用 PrincipalSearcher.FindAll() 时发生内存泄漏

c# - TextBlock 中动态字符串的 WPF 本地化

java - java多线程在不同操作系统上有不同吗?

MySql innoDB 自动增量锁定解决方法

C# Windows 服务 - 多个计时器

SwiftUI 文本在 ForEach 内部时不会本地化

c# - 我可以在 ASP.NET 中使用 C# 获取时间参数的本地化缩写吗?

python - PyQt5 : start, 停止并暂停带有进度更新的线程