Чи існує спосіб встановлення культури для цілого додатка? Усі поточні теми та нові теми?


177

Чи існує спосіб встановлення культури для цілого додатка? Усі поточні теми та нові теми?

У нас є назва культури, що зберігається в базі даних, і коли наша програма запускається, ми це робимо

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

Але, звичайно, це «втрачається», коли ми хочемо щось зробити в новій нитці. Чи є спосіб встановити це CurrentCultureта CurrentUICultureдля всієї програми? Тож нові теми також отримують цю культуру? Або це якась подія, яку запускають, коли створюється нова нитка, до якої я можу підключити?


4
Якщо ви використовуєте ресурси, ви можете їх вручну примусити: Resource1.Culture = нова System.Globalization.CultureInfo ("fr"); Таким чином, кожного разу, коли ви хочете отримати рядок, вона локалізується та повертається
Reza S

Відповіді:


196

У .NET 4.5 ви можете використовувати CultureInfo.DefaultThreadCurrentCultureвластивість для зміни культури AppDomain.

Для версій до 4,5 вам потрібно використовувати відображення для маніпулювання культурою AppDomain. У приватному статичному полі CultureInfo( m_userDefaultCultureв .NET 2.0 mscorlib, s_userDefaultCultureв .NET 4.0 mscorlib), яке контролює те, що CurrentCultureповертається, якщо потік не встановив це властивість на себе.

Це не змінює локальну локальну нитку, і, ймовірно, не дуже гарна ідея надсилати код, який змінює культуру таким чином. Це може бути корисним для тестування.


9
Будьте уважні з цим налаштуванням у програмах ASP.NET. Якщо встановити культуру на AppDomain, вона встановить культуру для всіх користувачів. Тому англійському користувачеві не буде корисно бачити веб-сайт, наприклад, німецькою мовою.
Димитър Цонев

2
Я успадкував додаток ASP.NET, яке працює лише в тому випадку, якщо всі культури перебувають у США. Це сценарій, коли цей параметр ідеально підходить до часу, поки ця вада не буде виправлена
Даніель Гільгарт

37

Про це багато запитують. В основному, ні там немає, не для .NET 4.0. Ви повинні зробити це вручну на початку кожного нового потоку (або ThreadPoolфункції). Ви, можливо, можете зберегти ім'я культури (або просто об’єкт культури) у статичному полі, щоб зберегти необхідність потрапляти в БД, але це стосується цього.


1
щось дратує ... здається, ти маєш рацію, хе-хе. Тож ми робимо це зараз (і маємо культуру статичним класом), але у нас все ще є проблема з деякими потоками, над якими ми не маємо контролю. Як і обробка потоків у переглядачі звітів Microsoft. Знайшли роботу, хоча. Дякую за інформацію :)
Свиш

8
Це справді дратує :( Було б добре, якби був спосіб автоматичної установки поточної культури (інтерфейсу інтерфейсу) для вашої програми і всі нові теми
підбирали

1
Це так давно, як я зараз працював з цим, тому не пам’ятаю, що саме ми робили. Але я думаю, що це могло бути, що ми замість того, щоб використати дати в наборі даних, що надсилаються глядачеві звітів, ми відформатували дату спочатку в рядок, використовуючи культуру, яку ми встановили в додатку. Тоді вже не мало значення, що потоки візуалізації звітів використовували неправильну культуру. Тому ми не вирішили проблему з потоками, використовуючи неправильну культуру. Ми щойно вирішили проблему неправильного форматування дат :)
Svish

2
У нашому додатку я спробував «захопити» теми (зателефонувавши спеціальним методом з певних місць) та зберігаючи їх у колекції для подальшого використання (у цьому випадку для встановлення культури), яка, здається, працює далеко.
Містер ТА

1
Не вдалося ви зберегти об’єкт cultureinfo під час сеансу та (якщо ви використовуєте masterpages) перевірити його у коді основної сторінки позаду та встановити поточну нитку?
user609926

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

Тепер кожного разу, коли ви посилаєтесь на вашу окрему рядок у цьому ресурсі, вона переосмислює культуру (потік чи процес) із заданим ресурсомCulture.

Ви можете вказати мову як у "fr", "de" і т.д. або ввести код мови, як у 0x0409 для en-US, або 0x0410 для it-IT. Повний список мовних кодів див. У розділі: Ідентифікатори мови та локальні мови


Це можна "безпрецедентно" встановити на Null:Resource1.Culture = Null;
habakuk

8

Для .net 4.5 і вище ви повинні використовувати

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

6

Насправді ви можете встановити культуру потоків за замовчуванням та культуру інтерфейсу користувача, але лише за допомогою версії 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, щоб побачити, що надійшло на інший кінець. CultureInfo.CurrentUICulture перестала бути американською, і натомість стала en-AU у комплекті з моїм маленьким хаком, щоб змусити її дотримуватися регіональних налаштувань для ShortTimePattern.

Ура, у світі все добре! Чи ні. Параметр культури, переданий методу Convert, все ще знаходиться в режимі «США». Ем, WTF ?! Але це початок. Принаймні таким чином

  • ви можете виправити культуру інтерфейсу один раз, коли додаток завантажується
  • це завжди доступно з 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 на якусь конкретну культуру, але при цьому зберегти переваги користувача, тоді вам просто потрібно отримати значення з DateTimeFormat's CurrentCulture перед тим, як ви його замініть.
Eric MSFT

1
Мені доведеться перевірити, але я, мабуть, пам’ятаю, що намагався це зробити без успіху, що призвело до отримання його з реєстру. Ви справді думаєте, що я б пішов на всю цю неприємність без причини? Доступ до реєстру - це гігантська PITA у цьому новому відважному світі UAC.
Peter Wone

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-US, en-CA та fr-CA. І таким чином "хак-у", тому коли я переходжу до виправлення, всі перетворення їх типів вибухають у шарі даних! (Французькі значення грошей становлять "1 234,56 $"
Andrew 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, але вони є приватними. За допомогою Reflection ви можете їх легко встановити. Це вплине на всі потоки, де 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 = культура; Котрий виглядає той же didnt. Для наочності це використовується в додатку WPF, де саме додаток змінював свою культуру, однак користувальницькі контролери в інших проектах використовували культуру браузера, а не культуру, вибрану користувачем
chillfire

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, додайте цей атрибут. (це працює для додатка WHOLE)

    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;
        }
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.