有没有一种方法可以为整个应用程序设置区域性?所有当前线程和新线程?


177

有没有一种方法可以为整个应用程序设置区域性?所有当前线程和新线程?

我们将文化名称存储在数据库中,当我们的应用程序启动时,

CultureInfo ci = new CultureInfo(theCultureString);
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;

但是,当然,当我们想在新线程中执行某些操作时,这会“丢失”。是否有设置,的一种方式CurrentCulture,并CurrentUICulture为整个应用程序?这样,新线程也可以得到这种文化?还是每当创建一个我可以连接的新线程时就会触发某个事件?


4
如果您使用的是资源,则可以通过以下方式手动强制使用:Resource1.Culture = new System.Globalization.CultureInfo(“ fr”); 这样,每次您想检索一个字符串时,它都会被本地化并返回
Reza S

Answers:


196

在.NET 4.5中,您可以使用该CultureInfo.DefaultThreadCurrentCulture属性来更改AppDomain的区域性。

对于4.5之前的版本,您必须使用反射来操纵AppDomain的区域性。CultureInfom_userDefaultCulture在.NET 2.0 mscorlib中,s_userDefaultCulture在.NET 4.0 mscorlib中)上有一个私有静态字段,该字段控制CurrentCulture如果线程未在其自身上设置该属性的结果。

这不会更改本地线程的语言环境,并且以这种方式发布更改区域性的代码可能不是一个好主意。不过,它可能对测试很有用。


9
在ASP.NET应用程序中,请谨慎使用此设置。在AppDomain上设置区域性将为所有用户设置区域性。因此,例如对于英语用户来说,以德语查看该网站并不合适。
Dimitar Tsonev 2015年

2
我继承了一个ASP.NET应用程序,该应用程序仅在所有区域性都在美国时才起作​​用。在这种情况下,此设置非常适合修复此缺陷之前的时间
Daniel Hilgarth,2016年

37

这被问了很多。基本上,没有。NET4.0也没有。您必须在每个新线程(或ThreadPool函数)开始时手动进行操作。您可以将区域性名称(或仅区域性对象)存储在静态字段中,以省去打数据库的麻烦,仅此而已。


1
有点烦人...看来您是对的,呵呵。因此,我们现在就这样做(并且将区域性设置为静态类),但是对于某些无法控制的线程仍然存在问题。就像在Microsoft报表查看器中处理线程一样。虽然找到了解决方法。谢谢您提供的信息:)
Svish

8
这真的很烦人:(如果能为您的应用程序自动设置Current(UI)Culture并让所有新线程都采用该值
那就太好了。– Jedidja 2010年

1
自从我现在从事这项工作已经很久了,所以我不记得我们到底做了什么。但是我认为可能是我们不是在发送给报表查看器的数据集中使用日期,而是使用在应用程序中设置的区域性将日期首先格式化为字符串。然后,报表呈现线程使用了错误的文化就不再重要了。因此,我们没有使用错误的文化解决线程问题。我们只是解决了日期格式错误的问题:)
Svish

2
在我们的应用程序中,我尝试“捕获”线程(通过从某些位置调用特殊方法)并将其存储在集合中以供以后使用(在这种情况下设置区域性),这似乎已经奏效了。
TA先生

1
您是否无法将文化信息对象存储在会话中,并且(如果您使用的是母版页)在后面的母版页代码中检查它并设置当前线程?
user609926 '16

20

如果您正在使用资源,则可以通过以下方式手动强制使用资源:

Resource1.Culture = new System.Globalization.CultureInfo("fr"); 

在资源管理器中,有一个自动生成的代码,如下所示:

/// <summary>
///   Overrides the current thread's CurrentUICulture property for all
///   resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
    get {
        return resourceCulture;
    }
    set {
        resourceCulture = value;
    }
}

现在,每次您在该资源中引用您的单个字符串时,它都会使用指定的resourceCulture覆盖区域性(线程或进程)。

您既可以将语言指定为“ fr”,“ de”等,也可以将语言代码输入为en-US的0x0409或it-IT的0x0410。有关语言代码的完整列表,请参阅:语言标识符和语言环境


可以通过将其设置为Null来“ Resource1.Culture = Null;
解除

8

对于.net 4.5及更高版本,您应该使用

var culture = new CultureInfo("en-US");
        CultureInfo.DefaultThreadCurrentCulture = culture;
        CultureInfo.DefaultThreadCurrentUICulture = culture;

6

其实你 可以设置默认的线程文化和UI文化,但只能使用Framework 4.5+

我放入了这个静态构造函数

static MainWindow()
{
  CultureInfo culture = CultureInfo
    .CreateSpecificCulture(CultureInfo.CurrentCulture.Name);
  var dtf = culture.DateTimeFormat;
  dtf.ShortTimePattern = (string)Microsoft.Win32.Registry.GetValue(
    "HKEY_CURRENT_USER\\Control Panel\\International", "sShortTime", "hh:mm tt");
  CultureInfo.DefaultThreadCurrentUICulture = culture;
}

并将断点放在ValueConverter的Convert方法中以查看到达另一端的内容。CultureInfo.CurrentUICulture不再是美国的,而是通过我的小技巧变成en-AU的完整版,以使其尊重ShortTimePattern的区域设置。

呼啦,世界上一切都很好!或不。传递给Convert方法的区域性参数仍为 en-US。恩,WTF ?!但这是一个开始。至少这样

  • 您可以在应用加载后修复UI文化
  • 它总是可以从 CultureInfo.CurrentUICulture
  • string.Format("{0}", DateTime.Now) 将使用您自定义的区域设置

如果您不能使用框架的版本4.5,则放弃将CurrentUICulture设置为CultureInfo的静态属性,并将其设置为您自己的类之一的静态属性。这将无法修复string.Format的默认行为,也无法使StringFormat在绑定中正常工作,然后遍历应用程序的逻辑树以重新创建应用程序中的所有绑定并设置其转换器区域性。


1
奥斯汀的答案已经提供了一种见解,即可以将CultureInfo.DefaultThreadCurrentCulture设置为更改AppDomain的区域性。CultureInfo.DefaultThreadCurrentUICulture对应于CurrentUICulture,就像CultureInfo.DefaultThreadCurrentCulture对应于CurrentCulture。确实没有理由直接从代码中的注册表中获取值。如果您想将CurrentCulture更改为某些特定区域性,但保留用户替代,那么您只需在重写之前从CurrentCulture的DateTimeFormat获取值即可。
Eric MSFT 2014年

1
我将不得不检查,但我似乎记得尝试过但没有成功,导致从注册表中获取它。您是否真的认为我会无缘无故地去解决所有麻烦?在UAC这个崭新的世界中,注册表访问权限是一个巨大的PITA。
Peter Wone 2014年

4

对于ASP.NET5,即ASPNETCORE,可以在以下位置执行以下操作configure

app.UseRequestLocalization(new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture(new CultureInfo("en-gb")),
    SupportedCultures = new List<CultureInfo>
    {
        new CultureInfo("en-gb")
    },
            SupportedUICultures = new List<CultureInfo>
    {
        new CultureInfo("en-gb")
    }
});

这是一系列提供更多信息的博客文章


1
我来寻找一个在旧版ASP.NET 2.0网站中执行此操作的方法,相比之下,使用另一个Core站点管理它的难易程度令人沮丧!我在一家跨国公司中,对于内部网站,我们所有的格式都为英语,以防止日期混淆。但是,该旧站点是针对美国,en-CA和fr-CA设置的。并且以一种“ hack-y”的方式,所以当我修复它时,它们的所有类型转换都会在数据层中爆炸!(法国货币值为“ 1 234,56 $”
安德鲁·S

1
@Sean您的链接已损坏。也许它指向了这个这个这个
Fer R

4

对于@rastating的出色答案,此答案有所扩展。您可以对所有.NET版本使用以下代码,而无需担心:

    public static void SetDefaultCulture(CultureInfo culture)
    {
        Type type = typeof (CultureInfo);
        try
        {
            // Class "ReflectionContext" exists from .NET 4.5 onwards.
            if (Type.GetType("System.Reflection.ReflectionContext", false) != null)
            {
                type.GetProperty("DefaultThreadCurrentCulture")
                    .SetValue(System.Threading.Thread.CurrentThread.CurrentCulture,
                        culture, null);

                type.GetProperty("DefaultThreadCurrentUICulture")
                    .SetValue(System.Threading.Thread.CurrentThread.CurrentCulture,
                        culture, null);
            }
            else //.NET 4 and lower
            {
                type.InvokeMember("s_userDefaultCulture",
                    BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                    null,
                    culture,
                    new object[] {culture});

                type.InvokeMember("s_userDefaultUICulture",
                    BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                    null,
                    culture,
                    new object[] {culture});

                type.InvokeMember("m_userDefaultCulture",
                    BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                    null,
                    culture,
                    new object[] {culture});

                type.InvokeMember("m_userDefaultUICulture",
                    BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
                    null,
                    culture,
                    new object[] {culture});
            }
        }
        catch
        {
            // ignored
        }
    }
}

4

DefaultThreadCurrentCulture并且DefaultThreadCurrentUICulture也存在于Framework 4.0中,但是它们是私有的。使用反射,您可以轻松设置它们。这将影响所有CurrentCulture未明确设置的线程(也正在运行线程)。

Public Sub SetDefaultThreadCurrentCulture(paCulture As CultureInfo)
    Thread.CurrentThread.CurrentCulture.GetType().GetProperty("DefaultThreadCurrentCulture").SetValue(Thread.CurrentThread.CurrentCulture, paCulture, Nothing)
    Thread.CurrentThread.CurrentCulture.GetType().GetProperty("DefaultThreadCurrentUICulture").SetValue(Thread.CurrentThread.CurrentCulture, paCulture, Nothing)
End Sub

1
这个为我工作。尽管使用Thread.CurrentThread.CurrentCulture =文化;Thread.CurrentThread.CurrentUICulture =文化;看起来一样没。为了清楚起见,这是在WPF应用程序中使用的,该应用程序本身正在更改其区域性,但是其他项目中的用户控件使用的是浏览器区域性,而不是用户选择的区域性
chillfire 2015年

3

这是c#MVC的解决方案:

  1. 首先:创建一个自定义属性并覆盖如下方法:

    public class CultureAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            // Retreive culture from GET
            string currentCulture = filterContext.HttpContext.Request.QueryString["culture"];
    
            // Also, you can retreive culture from Cookie like this :
            //string currentCulture = filterContext.HttpContext.Request.Cookies["cookie"].Value;
    
            // Set culture
            Thread.CurrentThread.CurrentCulture = new CultureInfo(currentCulture);
            Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(currentCulture);
        }
    }
  2. 其次:在App_Start中,找到FilterConfig.cs,添加此属性。(这适用于整个应用程序)

    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            // Add custom attribute here
            filters.Add(new CultureAttribute());
        }
    }    

而已 !

如果要为每个控制器/操作而不是整个应用程序定义区域性,则可以使用以下属性:

[Culture]
public class StudentsController : Controller
{
}

要么:

[Culture]
public ActionResult Index()
{
    return View();
}

1

为所有线程和窗口设置CultureInfo的工作解决方案。

  1. 打开App.xaml文件并添加一个新的“启动”属性,以为该应用分配启动事件处理程序:
<Application ........
             Startup="Application_Startup"
>
  1. 打开App.xaml.cs文件,并将此代码添加到创建的启动处理程序(在这种情况下为Application_Startup)。该类的应用程序将如下所示:
    public partial class App : Application
    {
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            CultureInfo cultureInfo = CultureInfo.GetCultureInfo("en-US");
            System.Globalization.CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
            System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
            Thread.CurrentThread.CurrentCulture = cultureInfo;
        }
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.