Esiste un modo per impostare la cultura per un'intera applicazione? Tutti i thread attuali e i nuovi thread?


177

Esiste un modo per impostare la cultura per un'intera applicazione? Tutti i thread attuali e i nuovi thread?

Abbiamo il nome della cultura memorizzato in un database e quando viene avviata la nostra applicazione, lo facciamo

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

Ma, naturalmente, questo viene "perso" quando vogliamo fare qualcosa in un nuovo thread. Esiste un modo per impostarlo CurrentCulturee CurrentUICultureper l'intera applicazione? In modo che anche i nuovi thread ottengano quella cultura? Oppure viene generato un evento ogni volta che viene creato un nuovo thread a cui posso collegarmi?


4
Se si utilizzano risorse, è possibile forzarle manualmente: Resource1.Culture = new System.Globalization.CultureInfo ("fr"); In questo modo ogni volta che desideri recuperare una stringa, questa viene localizzata e restituita
Reza S

Risposte:


196

In .NET 4.5, è possibile utilizzare la CultureInfo.DefaultThreadCurrentCultureproprietà per modificare la cultura di un AppDomain.

Per le versioni precedenti alla 4.5 devi usare la riflessione per manipolare la cultura di un AppDomain. C'è un campo statico privato su CultureInfo( m_userDefaultCulturein .NET 2.0 mscorlib, s_userDefaultCulturein .NET 4.0 mscorlib) che controlla ciò che CurrentCultureritorna se un thread non ha impostato quella proprietà su se stesso.

Questo non cambia le impostazioni locali del thread nativo e probabilmente non è una buona idea spedire il codice che modifica la cultura in questo modo. Tuttavia, può essere utile per i test.


9
Prestare attenzione con questa impostazione nelle applicazioni ASP.NET. L'impostazione della cultura su AppDomain imposterà la cultura per tutti gli utenti. Quindi non sarà utile per l'utente inglese vedere ad esempio il sito web in tedesco.
Dimitar Tsonev,

2
Ho ereditato un'applicazione ASP.NET che funziona solo se tutte le culture si trovano negli Stati Uniti. Questo è uno scenario in cui questa impostazione è perfetta per il tempo fino a quando quel difetto non sarà risolto
Daniel Hilgarth,

37

Questo viene chiesto molto. Fondamentalmente, no non c'è, non per .NET 4.0. Devi farlo manualmente all'inizio di ogni nuovo thread (o ThreadPoolfunzione). Potresti forse memorizzare il nome della cultura (o solo l'oggetto della cultura) in un campo statico per evitare di dover colpire il DB, ma questo è tutto.


1
un po 'fastidioso ... sembra che tu abbia ragione, hehe. Quindi lo facciamo ora (e abbiamo la cultura una classe statica), ma abbiamo ancora un problema con alcuni thread sui quali non abbiamo il controllo. Come l'elaborazione di thread nel Visualizzatore report di Microsoft. Ho trovato un lavoro però. Grazie per le informazioni :)
Svish,

8
Questo è davvero fastidioso :( Sarebbe bello se ci fosse un modo per impostare automaticamente la Cultura corrente (UI) per la tua applicazione e fare in modo che tutti i nuovi thread prendano quel valore.
Jedidja

1
È passato molto tempo da quando ho lavorato con questo ora, quindi non ricordo esattamente cosa abbiamo fatto. Ma penso che sarebbe stato possibile invece di utilizzare le date nel set di dati inviato al visualizzatore di report, prima abbiamo formattato la data in una stringa, usando la cultura che avevamo impostato nell'applicazione. Quindi non importava più che i thread di rendering del report usassero la cultura sbagliata. Quindi non abbiamo aggirato il problema con i thread usando la cultura sbagliata. Abbiamo appena
risolto

2
Nella nostra app, ho provato a "catturare" i thread (chiamando un metodo speciale da determinati luoghi) e memorizzandoli in una raccolta per un uso successivo (in questo caso per impostare la cultura), che sembra funzionare troppo lontano.
Sig. TA,

1
Non è possibile memorizzare l'oggetto cultureinfo in sessione e (se si utilizzano pagine master) controllarlo nel codice della pagina master dietro e impostare il thread corrente?
user609926

20

Se si utilizzano risorse, è possibile forzarle manualmente:

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

Nel gestore risorse, esiste un codice generato automaticamente che è il seguente:

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

Ora ogni volta che fai riferimento alla tua singola stringa all'interno di questa risorsa, sostituisce la cultura (thread o processo) con la risorsaCultura specificata.

È possibile specificare la lingua come in "fr", "de" ecc. Oppure inserire il codice della lingua come in 0x0409 per en-US o 0x0410 per it-IT. Per un elenco completo dei codici delle lingue, consultare: Identificatori delle lingue e impostazioni locali


Può essere "non forzato" impostandolo su Null:Resource1.Culture = Null;
habakuk,

8

Per .net 4.5 e versioni successive è necessario utilizzare

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

6

In realtà è possibile impostare la cultura del thread e la cultura dell'interfaccia utente predefinite, ma solo con Framework 4.5+

Ho inserito questo costruttore statico

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

e inserire un punto di interruzione nel metodo Convert di ValueConverter per vedere cosa è arrivato dall'altra parte. CultureInfo.CurrentUICulture ha smesso di essere en-US e è diventato invece en-AU completo del mio piccolo hack per farlo rispettare le impostazioni regionali per ShortTimePattern.

Evviva, va tutto bene nel mondo! O no. Il parametro cultura passato al metodo Convert è ancora en-US. Ehm, WTF ?! Ma è un inizio. Almeno così

  • puoi correggere la cultura dell'interfaccia utente una volta che l'app viene caricata
  • è sempre accessibile da CultureInfo.CurrentUICulture
  • string.Format("{0}", DateTime.Now) utilizzerà le impostazioni internazionali personalizzate

Se non è possibile utilizzare la versione 4.5 del framework, rinunciare a impostare CurrentUICulture come proprietà statica di CultureInfo e impostarlo come proprietà statica di una delle proprie classi. Questo non risolverà il comportamento predefinito di string.Format o farà in modo che StringFormat funzioni correttamente nei bind, quindi cammina nell'albero logico della tua app per ricreare tutti i bind nella tua app e impostare la loro cultura del convertitore.


1
La risposta di Austin ha già fornito l'intuizione che CultureInfo.DefaultThreadCurrentCulture può essere impostato per cambiare la cultura di AppDomain. CultureInfo.DefaultThreadCurrentUICulture è su CurrentUICulture come CultureInfo.DefaultThreadCurrentCulture è su CurrentCulture. Non c'è davvero alcun motivo per ottenere il valore direttamente dal registro nel codice. Se vuoi cambiare CurrentCulture in una cultura specifica ma mantenere le sostituzioni dell'utente, devi solo ottenere il valore dai DateTimeFormat di CurrentCulture prima di sovrascriverlo.
Eric MSFT,

1
Dovrò controllare ma mi sembra di averlo provato senza successo, portando a ottenerlo dal registro. Pensi davvero che sarei andato a tutti quei problemi senza motivo? L'accesso al registro è una PITA gigante in questo nuovo coraggioso mondo di Controllo dell'account utente.
Peter finito il

4

Per ASP.NET5, ovvero ASPNETCORE, è possibile effettuare le seguenti operazioni in 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")
    }
});

Ecco una serie di post sul blog che fornisce ulteriori informazioni.


1
Sono venuto alla ricerca di un modo per farlo in un sito ASP.NET 2.0 legacy ed è frustrante quanto facilmente lo abbia gestito con un altro sito Core in confronto! Sono in una multinazionale e, per i siti interni, tutta la nostra formattazione è negli Stati Uniti per prevenire la confusione delle date. Ma questo sito legacy è impostato per en-US, en-CA e fr-CA. E in modo "hack-y", quindi quando vado a sistemarlo, tutte le loro conversioni di tipo esplodono nel livello dati! (Il valore del denaro francese è "1 234,56 $"
Andrew S,

1
@Sean il tuo link è rotto. Forse indica questo , questo e questo
Fer R

4

Questa risposta è un po 'di espansione per l'ottima risposta di @ rastating. È possibile utilizzare il seguente codice per tutte le versioni di .NET senza preoccupazioni:

    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

DefaultThreadCurrentCulturee DefaultThreadCurrentUICulturesono presenti anche in Framework 4.0, ma sono privati. Usando Reflection puoi facilmente impostarli. Ciò influirà su tutti i thread in cui CurrentCulturenon è impostato esplicitamente (anche i thread in esecuzione).

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
Questo ha funzionato per me. Sebbene utilizzi Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = cultura; Che sembra lo stesso no. Per chiarezza, questo viene utilizzato in un'app WPF in cui l'app stessa stava cambiando la sua cultura, tuttavia i controlli in altri progetti utilizzavano la cultura del browser e non la cultura selezionata dall'utente
chillfire

3

Ecco la soluzione per c # MVC:

  1. Primo: crea un attributo personalizzato e sostituisci il metodo in questo modo:

    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. Secondo: in App_Start, trova FilterConfig.cs, aggiungi questo attributo. (funziona per TUTTA l'applicazione)

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

Questo è tutto !

Se si desidera definire la cultura per ciascun controller / azione anziché l'intera applicazione, è possibile utilizzare questo attributo in questo modo:

[Culture]
public class StudentsController : Controller
{
}

O:

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

1

Soluzione funzionante per impostare CultureInfo per tutti i thread e le finestre.

  1. Apri il file App.xaml e aggiungi un nuovo attributo "Avvio" per assegnare il gestore eventi di avvio per l'app:
<Application ........
             Startup="Application_Startup"
>
  1. Apri il file App.xaml.cs e aggiungi questo codice al gestore di avvio creato (Application_Startup in questo caso). L'app della classe avrà questo aspetto:
    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;
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.