¿Hay alguna manera de establecer la cultura para una aplicación completa? Todos los hilos actuales y nuevos hilos?

177

¿Hay alguna manera de establecer la cultura para una aplicación completa? Todos los hilos actuales y nuevos hilos?

Tenemos el nombre de la cultura almacenada en una base de datos, y cuando se inicia nuestra aplicación, lo hacemos

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

Pero, por supuesto, esto se "pierde" cuando queremos hacer algo en un nuevo hilo. ¿Hay alguna forma de configurar eso CurrentCulturey CurrentUICulturepara toda la aplicación? ¿Entonces esos nuevos hilos también adquieren esa cultura? ¿O se produce algún evento cada vez que se crea un nuevo hilo al que puedo conectarme?

Svish
fuente
44
Si está utilizando recursos, puede forzarlo manualmente: Resource1.Culture = new System.Globalization.CultureInfo ("fr"); De esta manera, cada vez que desee recuperar una cadena, se localiza y se devuelve
Reza S

Respuestas:

196

En .NET 4.5, puede usar la CultureInfo.DefaultThreadCurrentCulturepropiedad para cambiar la cultura de un dominio de aplicación.

Para las versiones anteriores a la 4.5, debe usar la reflexión para manipular la cultura de un AppDomain. Hay un campo estático privado en CultureInfo( m_userDefaultCultureen .NET 2.0 mscorlib, s_userDefaultCultureen .NET 4.0 mscorlib) que controla lo que se CurrentCulturedevuelve si un hilo no ha establecido esa propiedad en sí mismo.

Esto no cambia la configuración regional del hilo nativo y probablemente no sea una buena idea enviar código que cambie la cultura de esta manera. Sin embargo, puede ser útil para probar.

Austin
fuente
9
Tenga cuidado con esta configuración en las aplicaciones ASP.NET. Establecer la cultura en el dominio de aplicación establecerá la cultura para todos los usuarios. Por lo tanto, no será bueno para el usuario en inglés ver el sitio web en alemán, por ejemplo.
Dimitar Tsonev
2
Heredé una aplicación ASP.NET que solo funciona si todas las culturas están en EE. UU. Este es un escenario en el que esta configuración es perfecta para el tiempo hasta que se solucione ese defecto
Daniel Hilgarth
37

Esto se pregunta mucho. Básicamente, no, no hay, no para .NET 4.0. Debe hacerlo manualmente al comienzo de cada nuevo hilo (o ThreadPoolfunción). Tal vez podría almacenar el nombre de la cultura (o solo el objeto de la cultura) en un campo estático para evitar tener que presionar la base de datos, pero eso es todo.

Marc Gravell
fuente
1
un poco molesto ... parece que tienes razón, jeje. Así que lo hacemos ahora (y la cultura es una clase estática), pero todavía tenemos un problema con algunos hilos sobre los que no tenemos control. Como procesar subprocesos en el visor de informes de microsoft. Sin embargo, encontré una solución. Gracias por la información :)
Svish
8
Esto es realmente molesto :( Sería bueno si hubiera una manera de configurar la Cultura actual (UI) automáticamente para su aplicación y hacer que todos los nuevos hilos recojan ese valor.
Jedidja
1
Hace tanto tiempo que trabajé con esto ahora, así que no recuerdo exactamente lo que hicimos. Pero creo que podría haber sido que en lugar de usar fechas en el conjunto de datos enviado al visor de informes, primero formateamos la fecha en una cadena, usando la cultura que habíamos establecido en la aplicación. Entonces realmente ya no importaba que los hilos de representación de informes utilizaran la cultura incorrecta. Así que no solucionamos el problema con los hilos usando la cultura incorrecta. Simplemente solucionamos el problema de formatear incorrectamente las fechas :)
Svish
2
En nuestra aplicación, traté de "capturar" los hilos (llamando a un método especial desde ciertos lugares) y almacenarlos en una colección para su uso posterior (en este caso para establecer la cultura), que parece estar funcionando demasiado lejos.
Sr. TA
1
¿No podría almacenar el objeto cultureinfo en sesión y (si está usando páginas maestras) verificarlo en el código de su página maestra detrás y establecer el hilo actual?
user609926
20

Si está utilizando recursos, puede forzarlo manualmente:

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

En el administrador de recursos, hay un código generado automáticamente que es el siguiente:

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

Ahora, cada vez que se refiere a su cadena individual dentro de este recurso, anula la cultura (hilo o proceso) con la ResourceCulture especificada.

Puede especificar el idioma como "fr", "de" etc. o poner el código del idioma como 0x0409 para en-US o 0x0410 para it-IT. Para obtener una lista completa de códigos de idioma, consulte: Identificadores de idioma y configuraciones regionales

Reza S
fuente
Puede ser "no forzado" estableciéndolo en Nulo:Resource1.Culture = Null;
habakuk
8

Para .net 4.5 y superior, debe usar

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

En realidad, puede establecer la cultura de subprocesos predeterminada y la cultura de la interfaz de usuario, pero solo con Framework 4.5+

Puse en este constructor estático

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

y poner un punto de interrupción en el método Convert de un ValueConverter para ver qué llegó al otro extremo. CultureInfo.CurrentUICulture dejó de ser en-US y se convirtió en en-AU completo con mi pequeño truco para que respete la configuración regional de ShortTimePattern.

¡Hurra, todo está bien en el mundo! O no. El parámetro de cultura pasado al método Convert todavía está en EE. UU. Erm, ¿WTF? Pero es un comienzo. Al menos de esta manera

  • puedes arreglar la cultura de la interfaz de usuario una vez cuando se carga tu aplicación
  • siempre es accesible desde CultureInfo.CurrentUICulture
  • string.Format("{0}", DateTime.Now) utilizará su configuración regional personalizada

Si no puede usar la versión 4.5 del marco, deje de configurar CurrentUICulture como una propiedad estática de CultureInfo y configúrelo como una propiedad estática de una de sus propias clases. Esto no solucionará el comportamiento predeterminado de string.Format o hará que StringFormat funcione correctamente en enlaces, luego recorra el árbol lógico de su aplicación para recrear todos los enlaces en su aplicación y establecer su cultura de conversión.

Peter Wone
fuente
1
La respuesta de Austin ya proporcionó la idea de que CultureInfo.DefaultThreadCurrentCulture se puede configurar para cambiar la cultura del AppDomain. CultureInfo.DefaultThreadCurrentUICulture es a CurrentUICulture como CultureInfo.DefaultThreadCurrentCulture es a CurrentCulture. Realmente no hay razón para obtener el valor directamente del registro en su código. Si desea cambiar el CurrentCulture a una cultura específica, pero retiene las anulaciones del usuario, entonces solo necesita obtener el valor del DateTimeFormat de CurrentCulture antes de anularlo.
Eric MSFT
1
Tendré que comprobarlo, pero parece recordar haberlo intentado sin éxito, lo que me ha llevado a obtenerlo del registro. ¿De verdad crees que me tomaría todas esas molestias sin ninguna razón? El acceso al registro es un PITA gigante en este valiente nuevo mundo de UAC.
Peter Wone
4

Para ASP.NET5, es decir, ASPNETCORE, puede hacer lo siguiente en 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")
    }
});

Aquí hay una serie de publicaciones de blog que brindan más información.

Sean
fuente
1
¡Vine buscando una manera de hacer esto en un sitio ASP.NET 2.0 heredado, y es frustrante lo fácil que lo logré con un sitio Core diferente en comparación! Estoy en una empresa multinacional, y para sitios internos, todo nuestro formato es en EE. UU. Para evitar confusiones de fechas. Pero este sitio heredado está configurado para en-US, en-CA y fr-CA. Y de una manera "pirateada", así que cuando voy a arreglarlo, ¡todas sus conversiones de tipo explotan en la capa de datos! (Los valores monetarios franceses son "1 234,56 $"
Andrew S
1
@Sean su enlace está roto. Tal vez apunta a esto , esto y esto
Fer R
4

Esta respuesta es un poco de expansión para la gran respuesta de @ rastating. Puede usar el siguiente código para todas las versiones de .NET sin preocupaciones:

    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
        }
    }
}
polfosol ఠ_ఠ
fuente
4

DefaultThreadCurrentCulturey también DefaultThreadCurrentUICultureestán presentes en Framework 4.0, pero son privados. Usando Reflection puedes configurarlos fácilmente. Esto afectará a todos los subprocesos donde CurrentCultureno se establece explícitamente (ejecutando subprocesos también).

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
HCAxel
fuente
1
Este me funcionó. Aunque usando Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = cultura; Que se ve igual no. Para mayor claridad, esto se está utilizando en una aplicación WPF donde la aplicación misma estaba cambiando su cultura, sin embargo
controles de
3

Aquí está la solución para c # MVC:

  1. Primero: cree un atributo personalizado y anule un método como este:

    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. Segundo: en App_Start, busque FilterConfig.cs, agregue este atributo. (esto funciona para TODA la aplicación)

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

Eso es !

Si desea definir la cultura para cada controlador / acción en lugar de la aplicación completa, puede usar este atributo de esta manera:

[Culture]
public class StudentsController : Controller
{
}

O:

[Culture]
public ActionResult Index()
{
    return View();
}
Meng Xue
fuente
1

Solución de trabajo para configurar CultureInfo para todos los hilos y ventanas.

  1. Abra el archivo App.xaml y agregue un nuevo atributo "Inicio" para asignar el controlador de eventos de inicio para la aplicación:
<Application ........
             Startup="Application_Startup"
>
  1. Abra el archivo App.xaml.cs y agregue este código al controlador de inicio creado (Application_Startup en este caso). La aplicación de clase se verá así:
    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;
        }
    }
Rustam Shafigullin
fuente