Czy istnieje sposób na ustalenie kultury dla całej aplikacji? Wszystkie aktualne i nowe wątki?

177

Czy istnieje sposób na ustalenie kultury dla całej aplikacji? Wszystkie aktualne i nowe wątki?

Mamy nazwę kultury przechowywanej w bazie danych i po uruchomieniu naszej aplikacji tak

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

Ale oczywiście to się „gubi”, gdy chcemy coś zrobić w nowym wątku. Czy istnieje sposób na ustawienie tego CurrentCulturei CurrentUICulturedla całej aplikacji? Czy więc nowe wątki również otrzymują tę kulturę? A może jest to jakieś zdarzenie uruchamiane za każdym razem, gdy tworzony jest nowy wątek, do którego mogę się podłączyć?

Svish
źródło
4
Jeśli używasz zasobów, możesz wymusić to ręcznie: Resource1.Culture = new System.Globalization.CultureInfo ("fr"); W ten sposób za każdym razem, gdy chcesz pobrać ciąg, jest on lokalizowany i zwracany
Reza S

Odpowiedzi:

196

W .NET 4,5 można użyć CultureInfo.DefaultThreadCurrentCulturewłaściwości, aby zmienić kulturę AppDomain.

W przypadku wersji wcześniejszych niż 4,5 musisz użyć odbicia, aby manipulować kulturą AppDomain. Istnieje prywatne pole statyczne CultureInfo( m_userDefaultCulturew .NET 2.0 mscorlib, s_userDefaultCulturew .NET 4.0 mscorlib), które kontroluje, co jest CurrentCulturezwracane, jeśli wątek nie ustawił tej właściwości na sobie.

Nie zmienia to rodzimych ustawień regionalnych wątku i prawdopodobnie nie jest dobrym pomysłem wysyłanie kodu, który zmienia kulturę w ten sposób. Może być jednak przydatne do testowania.

Austin
źródło
9
Uważaj na to ustawienie w aplikacjach ASP.NET. Ustawienie kultury w AppDomain spowoduje ustawienie kultury dla wszystkich użytkowników. Nie będzie więc dobrze dla anglojęzycznych użytkowników przeglądanie strony internetowej w języku niemieckim.
Dimitar Tsonev
2
Odziedziczyłem aplikację ASP.NET, która działa tylko wtedy, gdy wszystkie kultury znajdują się w en-US. To jest scenariusz, w którym to ustawienie jest idealne na czas, dopóki ta
usterka nie
37

To jest często zadawane. Zasadniczo nie, nie ma, nie dla .NET 4.0. Musisz to zrobić ręcznie na początku każdego nowego wątku (lub ThreadPoolfunkcji). Być może możesz przechowywać nazwę kultury (lub tylko obiekt kultury) w polu statycznym, aby zaoszczędzić konieczności trafiania do bazy danych, ale to wszystko.

Marc Gravell
źródło
1
trochę denerwujące ... wydaje się, że masz rację, hehe. Robimy to teraz (i mamy kulturę jako klasę statyczną), ale nadal mamy problem z niektórymi wątkami, nad którymi nie mamy kontroli. Podobnie jak przetwarzanie wątków w przeglądarce raportów firmy Microsoft. Znalazłem jednak obejście. Dziękuję za informację :)
Svish
8
To naprawdę irytujące :( Byłoby miło, gdyby istniał sposób na automatyczne ustawienie bieżącej kultury (UI) dla twojej aplikacji i aby wszystkie nowe wątki nabrały tej wartości.
Jedidja
1
Minęło tak dużo czasu, odkąd nad tym pracowałem, więc nie pamiętam dokładnie, co zrobiliśmy. Ale myślę, że mogło być tak, że zamiast używać dat w zbiorze danych przesłanym do przeglądarki raportów, najpierw sformatowaliśmy datę w łańcuchu, używając kultury, którą ustawiliśmy w aplikacji. Wtedy nie miało już znaczenia, że ​​wątki renderujące raport używały niewłaściwej kultury. Więc nie rozwiązaliśmy problemu z wątkami używającymi niewłaściwej kultury. Właśnie
poradziliśmy
2
W naszej aplikacji próbowałem „przechwytywać” wątki (wywołując specjalną metodę z określonych miejsc) i przechowywać je w kolekcji do późniejszego wykorzystania (w tym przypadku do ustawiania kultury), co wydaje się działać zbyt daleko.
Mr. TA
1
Czy nie mógłbyś przechowywać obiektu cultureinfo w sesji i (jeśli używasz masterpages) sprawdzić go w swoim kodowaniu masterpage i ustawić bieżący wątek?
user609926
20

Jeśli korzystasz z zasobów, możesz wymusić to ręcznie:

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

W menedżerze zasobów znajduje się automatycznie generowany kod, który wygląda następująco:

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

Teraz za każdym razem, gdy odwołujesz się do indywidualnego ciągu w tym zasobie, zastępuje on kulturę (wątek lub proces) określonym resourceCulture.

Możesz określić język jako „fr”, „de” itp. Lub wprowadzić kod języka jako 0x0409 dla en-US lub 0x0410 dla it-IT. Aby uzyskać pełną listę kodów języków, zobacz: Identyfikatory języków i ustawienia regionalne

Reza S
źródło
Można to "niewymuszone" ustawiając na Null:Resource1.Culture = Null;
habakuk
8

W przypadku .net 4.5 i nowszych należy użyć

var culture = new CultureInfo("en-US");
        CultureInfo.DefaultThreadCurrentCulture = culture;
        CultureInfo.DefaultThreadCurrentUICulture = culture;
Andreas
źródło
6

W rzeczywistości można ustawić domyślną kulturę wątku i kulturę interfejsu użytkownika, ale tylko w przypadku Framework 4.5+

Wstawiłem ten statyczny konstruktor

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

i umieść punkt przerwania w metodzie Convert elementu ValueConverter, aby zobaczyć, co dotarło na drugi koniec. CultureInfo.CurrentUICulture przestało być en-US i stało się en-AU kompletne z moim małym hackem, aby respektować ustawienia regionalne dla ShortTimePattern.

Hurra, wszystko jest dobrze na świecie! Albo nie. Parametr kultury przekazany do metody Convert jest nadal en-US. Erm, WTF ?! Ale to początek. Przynajmniej w ten sposób

  • możesz naprawić kulturę interfejsu użytkownika raz podczas ładowania aplikacji
  • jest zawsze dostępny z CultureInfo.CurrentUICulture
  • string.Format("{0}", DateTime.Now) użyje Twoich niestandardowych ustawień regionalnych

Jeśli nie możesz użyć wersji 4,5 struktury, zrezygnuj z ustawienia CurrentUICulture jako statycznej właściwości CultureInfo i ustaw ją jako statyczną właściwość jednej z własnych klas. Nie naprawi to domyślnego zachowania string.Format lub spraw, aby StringFormat działał poprawnie w powiązaniach, a następnie przejdź przez drzewo logiczne aplikacji, aby odtworzyć wszystkie powiązania w aplikacji i ustawić ich kulturę konwertera.

Peter Wone
źródło
1
Odpowiedź Austina zapewniła już wgląd w to, że CultureInfo.DefaultThreadCurrentCulture można ustawić, aby zmienić kulturę AppDomain. CultureInfo.DefaultThreadCurrentUICulture jest do CurrentUICulture, ponieważ CultureInfo.DefaultThreadCurrentCulture jest do CurrentCulture. Naprawdę nie ma powodu, aby pobierać wartość bezpośrednio z rejestru w swoim kodzie. Jeśli chcesz zmienić CurrentCulture na określoną kulturę, ale zachować zastąpienia użytkownika, wystarczy pobrać wartość z DateTimeFormat CurrentCulture, zanim ją zastąpisz.
Eric MSFT
1
Będę musiał to sprawdzić, ale wydaje mi się, że pamiętam, jak próbowałem to bez powodzenia, co doprowadziło do pobrania go z rejestru. Naprawdę myślisz, że zadałbym sobie tyle kłopotów bez powodu? Dostęp do rejestru to gigantyczny PITA w tym nowym, wspaniałym świecie UAC.
Peter wygrał
4

W przypadku ASP.NET5, czyli ASPNETCORE, możesz wykonać następujące czynności w 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")
    }
});

Oto seria postów na blogu, które zawierają więcej informacji.

Sean
źródło
1
Szukałem sposobu na zrobienie tego w starszej witrynie ASP.NET 2.0 i frustrujące jest to, jak łatwo zarządzałem tym z inną witryną Core w porównaniu! Pracuję w międzynarodowej firmie i w przypadku witryn wewnętrznych całe nasze formatowanie to en-US, aby uniknąć pomyłki w datach. Ale ta starsza witryna jest skonfigurowana dla en-US, en-CA i fr-CA. I to na sposób "hack-y", więc kiedy idę to naprawić, wszystkie ich konwersje typów wybuchają w warstwie danych! (Francuskie ceny to „1 234,56 $”
Andrew S
1
@Sean, że Twój link jest uszkodzony. Może to wskazuje na to , to i to
Fer R
4

Ta odpowiedź jest trochę rozwinięciem wspaniałej odpowiedzi @ rastating. Możesz bez obaw użyć następującego kodu dla wszystkich wersji .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
        }
    }
}
polfosol ఠ_ఠ
źródło
4

DefaultThreadCurrentCulturei DefaultThreadCurrentUICulturesą również obecne w Framework 4.0, ale są prywatne. Korzystając z Odbicia możesz łatwo je ustawić. Wpłynie to na wszystkie wątki, w których CurrentCulturenie jest to jawnie ustawione (również na uruchamiane wątki).

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
źródło
1
Ten pracował dla mnie. Chociaż używasz Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = kultura; Który wygląda tak samo, nie. Dla jasności jest to używane w aplikacji WPF, w której sama aplikacja zmieniała swoją kulturę, jednak kontrolki użytkownika w innych projektach korzystały z kultury przeglądarki, a nie kultury wybranej przez użytkownika
chillfire
3

Oto rozwiązanie dla języka C # MVC:

  1. Po pierwsze: utwórz atrybut niestandardowy i zastąp metodę w następujący sposób:

    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. Po drugie: w App_Start znajdź FilterConfig.cs, dodaj ten atrybut. (to działa dla CAŁEJ aplikacji)

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

Otóż ​​to !

Jeśli chcesz zdefiniować kulturę dla każdego kontrolera / akcji zamiast całej aplikacji, możesz użyć tego atrybutu w następujący sposób:

[Culture]
public class StudentsController : Controller
{
}

Lub:

[Culture]
public ActionResult Index()
{
    return View();
}
Meng Xue
źródło
1

Rozwiązanie robocze do ustawiania CultureInfo dla wszystkich wątków i okien.

  1. Otwórz plik App.xaml i dodaj nowy atrybut „Startup”, aby przypisać moduł obsługi zdarzeń uruchamiania dla aplikacji:
<Application ........
             Startup="Application_Startup"
>
  1. Otwórz plik App.xaml.cs i dodaj ten kod do utworzonej procedury obsługi uruchamiania (w tym przypadku Application_Startup). Aplikacja klasowa będzie wyglądać następująco:
    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
źródło