Existe-t-il un moyen de définir la culture pour toute une application? Tous les fils actuels et nouveaux fils?

177

Existe-t-il un moyen de définir la culture pour toute une application? Tous les fils actuels et nouveaux fils?

Nous avons le nom de la culture stocké dans une base de données, et lorsque notre application démarre, nous faisons

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

Mais, bien sûr, cela se «perd» lorsque nous voulons faire quelque chose dans un nouveau fil. Existe-t-il un moyen de définir cela CurrentCultureet CurrentUICulturepour l'ensemble de l'application? Alors que les nouveaux fils obtiennent également cette culture? Ou est-ce un événement déclenché chaque fois qu'un nouveau fil est créé auquel je peux me connecter?

Svish
la source
4
Si vous utilisez des ressources, vous pouvez le forcer manuellement par: Resource1.Culture = new System.Globalization.CultureInfo ("fr"); De cette façon, chaque fois que vous souhaitez récupérer une chaîne, elle est localisée et renvoyée
Reza S

Réponses:

196

Dans .NET 4.5, vous pouvez utiliser la CultureInfo.DefaultThreadCurrentCulturepropriété pour modifier la culture d'un AppDomain.

Pour les versions antérieures à 4.5, vous devez utiliser la réflexion pour manipuler la culture d'un AppDomain. Il existe un champ statique privé sur CultureInfo( m_userDefaultCulturedans .NET 2.0 mscorlib, s_userDefaultCulturedans .NET 4.0 mscorlib) qui contrôle ce qui CurrentCulturerevient si un thread n'a pas défini cette propriété sur lui-même.

Cela ne modifie pas les paramètres régionaux du thread natif et ce n'est probablement pas une bonne idée d'envoyer du code qui change la culture de cette façon. Cela peut être utile pour les tests.

Austin
la source
9
Soyez prudent avec ce paramètre dans les applications ASP.NET. La définition de la culture sur AppDomain définira la culture pour tous les utilisateurs. Il ne sera donc pas bon pour un utilisateur anglais de voir le site Web en allemand par exemple.
Dimitar Tsonev
2
J'ai hérité d'une application ASP.NET qui ne fonctionne que si toutes les cultures sont en-US. C'est un scénario où ce paramètre est parfait pour le temps jusqu'à ce que cette faille soit corrigée
Daniel Hilgarth
37

Cela est souvent demandé. En gros, non, pas pour .NET 4.0. Vous devez le faire manuellement au début de chaque nouveau thread (ou ThreadPoolfonction). Vous pouvez peut-être stocker le nom de la culture (ou simplement l'objet de culture) dans un champ statique pour éviter d'avoir à accéder à la base de données, mais c'est à peu près tout.

Marc Gravell
la source
1
un peu ennuyeux ... semble que tu as raison, hehe. Nous faisons donc cela maintenant (et avons la culture une classe statique), mais nous avons toujours un problème avec certains threads sur lesquels nous n'avons pas de contrôle. Comme le traitement des threads dans la visionneuse de rapports Microsoft. J'ai trouvé un travail. Merci pour l'info :)
Svish
8
C'est vraiment ennuyeux :( Ce serait bien s'il y avait un moyen de définir automatiquement la culture (UI) actuelle pour votre application et que tous les nouveaux threads prennent cette valeur.
Jedidja
1
Cela fait tellement longtemps que je n'ai pas travaillé avec ça maintenant, donc je ne me souviens pas exactement de ce que nous avons fait. Mais je pense qu'il se peut qu'au lieu d'utiliser des dates dans l'ensemble de données envoyé à la visionneuse de rapports, nous avons d'abord formaté la date dans une chaîne, en utilisant la culture que nous avions définie dans l'application. Le fait que les threads de rendu de rapport utilisent la mauvaise culture n'a plus vraiment d'importance. Nous n'avons donc pas contourné le problème avec les threads en utilisant la mauvaise culture. Nous venons de contourner le problème du mauvais formatage des dates :)
Svish
2
Dans notre application, j'ai essayé de «capturer» les threads (en appelant une méthode spéciale à partir de certains endroits) et de les stocker dans une collection pour une utilisation ultérieure (dans ce cas pour définir la culture), ce qui semble fonctionner trop loin.
M. TA
1
Ne pourriez-vous pas stocker l'objet cultureinfo en session et (si vous utilisez des pages maîtres) le vérifier dans votre code de page maître derrière et définir le thread actuel?
user609926
20

Si vous utilisez des ressources, vous pouvez le forcer manuellement en:

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

Dans le gestionnaire de ressources, il existe un code généré automatiquement qui se présente comme suit:

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

Désormais, chaque fois que vous faites référence à votre chaîne individuelle dans cette ressource, elle remplace la culture (thread ou processus) par la resourceCulture spécifiée.

Vous pouvez soit spécifier la langue comme dans «fr», «de» etc. ou mettre le code de langue comme dans 0x0409 pour en-US ou 0x0410 pour it-IT. Pour une liste complète des codes de langue, veuillez vous référer à: Identificateurs de langue et paramètres régionaux

Reza S
la source
Il peut être "non forcé" en le définissant sur Null:Resource1.Culture = Null;
habakuk
8

Pour .net 4.5 et supérieur, vous devez utiliser

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

En fait, vous pouvez définir la culture de thread et la culture d'interface utilisateur par défaut, mais uniquement avec Framework 4.5+

J'ai mis dans ce constructeur statique

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

et mettez un point d'arrêt dans la méthode Convert d'un ValueConverter pour voir ce qui est arrivé à l'autre extrémité. CultureInfo.CurrentUICulture a cessé d'être en-US et est devenu à la place en-AU complet avec mon petit hack pour le faire respecter les paramètres régionaux de ShortTimePattern.

Hourra, tout va bien dans le monde! Ou pas. Le paramètre de culture passé à la méthode Convert est toujours en-US. Euh, WTF?! Mais c'est un début. Au moins de cette façon

  • vous pouvez corriger la culture de l'interface utilisateur une fois lorsque votre application se charge
  • c'est toujours accessible depuis CultureInfo.CurrentUICulture
  • string.Format("{0}", DateTime.Now) utilisera vos paramètres régionaux personnalisés

Si vous ne pouvez pas utiliser la version 4.5 du framework, abandonnez la définition de CurrentUICulture en tant que propriété statique de CultureInfo et définissez-la comme propriété statique de l'une de vos propres classes. Cela ne corrigera pas le comportement par défaut de string.Format ou ne fera pas fonctionner StringFormat correctement dans les liaisons, puis parcourez l'arborescence logique de votre application pour recréer toutes les liaisons dans votre application et définir leur culture de convertisseur.

Peter Wone
la source
1
La réponse d'Austin a déjà permis de comprendre que CultureInfo.DefaultThreadCurrentCulture peut être défini pour modifier la culture de l'AppDomain. CultureInfo.DefaultThreadCurrentUICulture est à CurrentUICulture comme CultureInfo.DefaultThreadCurrentCulture est à CurrentCulture. Il n'y a vraiment aucune raison d'obtenir la valeur directement du registre dans votre code. Si vous souhaitez modifier la CurrentCulture en une culture spécifique tout en conservant les substitutions utilisateur, il vous suffit d'obtenir la valeur du DateTimeFormat de CurrentCulture avant de la remplacer.
Eric MSFT
1
Je vais devoir vérifier, mais il me semble me souvenir d'avoir essayé cela sans succès, ce qui a conduit à l'obtenir du registre. Pensez-vous vraiment que je me donnerais tous ces ennuis sans raison? L'accès au registre est un PITA géant dans ce nouveau monde courageux de l'UAC.
Peter Wone
4

Pour ASP.NET5, c'est-à-dire ASPNETCORE, vous pouvez effectuer les opérations suivantes dans 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")
    }
});

Voici une série d'articles de blog qui donnent plus d'informations.

Sean
la source
1
Je suis venu chercher un moyen de le faire dans un ancien site ASP.NET 2.0, et c'est frustrant de voir avec quelle facilité je l'ai géré avec un autre site Core en comparaison! Je suis dans une entreprise multinationale, et pour les sites internes, tout notre formatage est en-US pour éviter la confusion des dates. Mais cet ancien site est configuré pour en-US, en-CA et fr-CA. Et d'une manière "hack-y", donc quand je vais le réparer, toutes leurs conversions de type explosent dans la couche de données! (Les valeurs monétaires françaises sont "1 234,56 $"
Andrew S
1
@Sean, votre lien est rompu. Peut-être que cela indique ceci , ceci et cela
Fer R
4

Cette réponse est un peu d'expansion pour la grande réponse de @ rastating. Vous pouvez utiliser le code suivant pour toutes les versions de .NET sans aucun souci:

    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 ఠ_ఠ
la source
4

DefaultThreadCurrentCultureet DefaultThreadCurrentUICulturesont également présents dans Framework 4.0, mais ils sont privés. En utilisant Reflection, vous pouvez facilement les définir. Cela affectera tous les threads où CurrentCulturen'est pas explicitement défini (les threads en cours d'exécution aussi).

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
la source
1
Celui-ci a fonctionné pour moi. Bien que vous utilisiez Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = culture; Ce qui n'a pas l'air pareil. Pour plus de clarté, cela est utilisé dans une application WPF où l'application elle-même changeait de culture, mais les contrôles d'utilisateurs dans d'autres projets utilisaient la culture du navigateur et non la culture sélectionnée par l'utilisateur
chillfire
3

Voici la solution pour c # MVC:

  1. Premièrement: créez un attribut personnalisé et une méthode de remplacement comme celle-ci:

    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. Deuxièmement: dans App_Start, recherchez FilterConfig.cs, ajoutez cet attribut. (cela fonctionne pour toute l'application)

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

C'est tout !

Si vous souhaitez définir la culture pour chaque contrôleur / action au lieu de l'application entière, vous pouvez utiliser cet attribut comme ceci:

[Culture]
public class StudentsController : Controller
{
}

Ou:

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

Solution de travail pour définir CultureInfo pour tous les threads et fenêtres.

  1. Ouvrez le fichier App.xaml et ajoutez un nouvel attribut "Startup" pour attribuer un gestionnaire d'événements de démarrage à l'application:
<Application ........
             Startup="Application_Startup"
>
  1. Ouvrez le fichier App.xaml.cs et ajoutez ce code au gestionnaire de démarrage créé (Application_Startup dans ce cas). L'application de classe ressemblera à ceci:
    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
la source