Есть ли способ установить культуру для всего приложения? Все текущие темы и новые темы?

177

Есть ли способ установить культуру для всего приложения? Все текущие темы и новые темы?

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

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

Но, конечно, это «теряется», когда мы хотим что-то сделать в новом потоке. Есть ли способ установить это CurrentCultureи CurrentUICultureдля всего приложения? Так что новые темы также получают эту культуру? Или происходит какое-то событие при создании нового потока, к которому я могу подключиться?

Svish
источник
4
Если вы используете ресурсы, вы можете вручную форсировать их: Resource1.Culture = new System.Globalization.CultureInfo ("fr"); Таким образом, каждый раз, когда вы хотите получить строку, она локализуется и возвращается
Reza S

Ответы:

196

В .NET 4.5 вы можете использовать это CultureInfo.DefaultThreadCurrentCultureсвойство, чтобы изменить культуру домена приложений.

Для версий до 4.5 вы должны использовать отражение, чтобы манипулировать культурой домена приложений. Есть частное статическое поле наCultureInfo ( m_userDefaultCultureв .NET 2.0 mscorlib, s_userDefaultCultureв .NET 4.0 mscorlib), которое управляет тем, что CurrentCultureвозвращается, если поток не установил это свойство для себя.

Это не меняет локали нативного потока, и, вероятно, не стоит отправлять код, который меняет культуру таким образом. Это может быть полезно для тестирования, хотя.

Остин
источник
9
Будьте осторожны с этим параметром в приложениях ASP.NET. Установка культуры в AppDomain установит культуру для всех пользователей. Так что английскому пользователю будет не очень хорошо видеть сайт на немецком языке, например.
Димитар Цонев
2
Я унаследовал приложение ASP.NET, которое работает, только если все культуры находятся в en-US. Это сценарий, в котором этот параметр идеально подходит для того времени, пока этот недостаток не будет устранен
Даниэль Хилгарт,
37

Это часто спрашивают. В принципе, нет, не для .NET 4.0. Вы должны сделать это вручную в начале каждого нового потока (или ThreadPoolфункции). Возможно, вы можете сохранить название культуры (или просто объект культуры) в статическом поле, чтобы избежать необходимости попадания в БД, но это все.

Марк Гравелл
источник
1
отчасти раздражает ... кажется, что ты прав, хе-хе. Итак, мы делаем это сейчас (и у нас есть статический класс), но у нас все еще есть проблемы с некоторыми потоками, которые мы не можем контролировать. Как обработка потоков в программе просмотра отчетов Microsoft. Нашли работу вокруг хотя. Спасибо за информацию :)
Свиш
8
Это действительно раздражает :( Было бы неплохо, если бы существовал способ автоматически установить текущую (UI) культуру для вашего приложения, и чтобы все новые потоки
принимали
1
Я так давно работал с этим, поэтому я не помню точно, что мы сделали. Но я думаю, что, возможно, мы вместо того, чтобы использовать даты в наборе данных, отправленном в средство просмотра отчетов, сначала отформатировали дату в строку, используя культуру, которую мы установили в приложении. Тогда уже не имело значения, что потоки рендеринга отчетов использовали неправильную культуру. Поэтому мы не обошли проблему с потоками, использующими неправильную культуру. Мы только что решили проблему с неправильным форматированием дат :)
Свиш
2
В нашем приложении я попытался «захватить» потоки (вызвав специальный метод из определенных мест) и сохранить их в коллекции для последующего использования (в данном случае для установки культуры), что, похоже, работает далеко.
г-н Т.А.
1
Не могли бы вы сохранить объект cultureinfo в сеансе и (если вы используете мастер-страницы) проверить его в коде вашей мастер-страницы и установить текущий поток?
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;
    }
}

Теперь каждый раз, когда вы ссылаетесь на вашу отдельную строку в этом ресурсе, он переопределяет культуру (поток или процесс) с указанным resourceCulture.

Вы можете указать язык как «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;
Andreas
источник
6

На самом деле вы можете установить культуру потоков по умолчанию и культуру пользовательского интерфейса, но только с Framework 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;
}

и установите точку останова в методе Convert объекта ValueConverter, чтобы увидеть, что получено на другом конце. CultureInfo.CurrentUICulture перестал быть en-US и вместо этого стал en-AU с моим маленьким хакером, чтобы он учитывал региональные настройки для ShortTimePattern.

Ура, все хорошо на свете! Или не. Параметр культуры, переданный методу Convert, по- прежнему используется в США. Хм, WTF ?! Но это начало. По крайней мере, так

  • вы можете исправить культуру пользовательского интерфейса один раз, когда ваше приложение загружается
  • это всегда доступно из CultureInfo.CurrentUICulture
  • string.Format("{0}", DateTime.Now) будет использовать ваши индивидуальные региональные настройки

Если вы не можете использовать версию 4.5 платформы, то откажитесь от установки CurrentUICulture как статического свойства CultureInfo и установите его как статическое свойство одного из ваших собственных классов. Это не исправит поведение по умолчанию для string.Format или заставит StringFormat работать должным образом в привязках, затем обойдёт логическое дерево вашего приложения, чтобы воссоздать все привязки в вашем приложении и установить их культуру конвертера.

Питер Воне
источник
1
Ответ Остина уже дал представление о том, что CultureInfo.DefaultThreadCurrentCulture можно изменить, чтобы изменить культуру домена приложений. CultureInfo.DefaultThreadCurrentUICulture предназначен для CurrentUICulture, так как CultureInfo.DefaultThreadCurrentCulture предназначен для CurrentCulture. На самом деле нет причин получать значение непосредственно из реестра в вашем коде. Если вы хотите изменить CurrentCulture на какую-то конкретную культуру, но сохранить переопределения пользователей, то вам просто нужно получить значение из DateTimeFormat CurrentCulture перед тем, как переопределить его.
Эрик MSFT
1
Мне придется проверить, но, похоже, я помню, что пытался сделать это безуспешно, что привело к получению его из реестра. Вы действительно думаете, что я пошел бы ко всем этим неприятностям без причины? Доступ к реестру является гигантской PITA в этом дивном новом мире UAC.
Питер Воне
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, и меня огорчает, как легко я справился с этим с другим базовым сайтом по сравнению! Я работаю в многонациональной компании, и для внутренних сайтов все наше форматирование выполняется в США, чтобы избежать путаницы с датами. Но этот устаревший сайт создан для en-US, en-CA и fr-CA. И в «хакерском» смысле, поэтому, когда я собираюсь это исправить, все их преобразования типов взрываются на уровне данных! (Французские денежные значения равны «1 234,56 $»
Эндрю С
1
@ Шон, ваша ссылка не работает. Может быть, это указывает на это , это и это
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
HCAxel
источник
1
Этот работал для меня. Хотя используется Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = culture; Который выглядит так же, не сделал. Для ясности это используется в приложении 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, добавьте этот атрибут. (это работает для всего приложения)

    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). Класс App будет выглядеть так:
    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;
        }
    }
Рустам Шафигуллин
источник