Existe uma maneira de definir a cultura para um aplicativo inteiro? Todos os tópicos atuais e novos tópicos?

177

Existe uma maneira de definir a cultura para um aplicativo inteiro? Todos os tópicos atuais e novos tópicos?

Temos o nome da cultura armazenada em um banco de dados e, quando nosso aplicativo é iniciado, fazemos

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

Mas, é claro, isso se "perde" quando queremos fazer algo em um novo segmento. Existe uma maneira de definir isso CurrentCulturee CurrentUICulturepara todo o aplicativo? Para que novos tópicos também recebam essa cultura? Ou é algum evento acionado sempre que um novo encadeamento é criado que eu posso conectar?

Svish
fonte
4
Se você estiver usando recursos, poderá forçá-lo manualmente: Resource1.Culture = new System.Globalization.CultureInfo ("fr"); Desta forma, toda vez que você deseja recuperar uma string, ela é localizada e devolvida
Reza S

Respostas:

196

No .NET 4.5, você pode usar a CultureInfo.DefaultThreadCurrentCulturepropriedade para alterar a cultura de um AppDomain.

Para versões anteriores à 4.5, é necessário usar a reflexão para manipular a cultura de um AppDomain. Há um campo estático privado no CultureInfo( m_userDefaultCultureno .NET 2.0 mscorlib, s_userDefaultCultureno .NET 4.0 mscorlib) que controla o que CurrentCultureretorna se um thread não tiver definido essa propriedade por si só.

Isso não altera o código do idioma nativo do encadeamento e provavelmente não é uma boa ideia enviar código que altera a cultura dessa maneira. No entanto, pode ser útil para testar.

Austin
fonte
9
Tenha cuidado com essa configuração nos aplicativos ASP.NET. Definir a cultura no AppDomain definirá a cultura para todos os usuários. Portanto, não será bom para o usuário em inglês ver o site em alemão, por exemplo.
Dimitar Tsonev
2
Eu herdei um aplicativo ASP.NET que só funciona se todas as culturas estiverem nos EUA. Este é um cenário em que essa configuração é perfeita para o tempo até que a falha seja corrigida.
Daniel Hilgarth
37

Isso é muito solicitado. Basicamente, não, não existe, não para o .NET 4.0. Você deve fazer isso manualmente no início de cada novo thread (ou ThreadPoolfunção). Talvez você possa armazenar o nome da cultura (ou apenas o objeto da cultura) em um campo estático para evitar ter que acessar o banco de dados, mas é isso.

Marc Gravell
fonte
1
meio chato ... parece que você está certo, hehe. Portanto, fazemos isso agora (e temos a cultura como uma classe estática), mas ainda temos um problema com alguns threads sobre os quais não temos controle. Como processar threads no visualizador de relatórios da Microsoft. Encontrei um trabalho em torno embora. Obrigado pela informação :)
Svish
8
Este é realmente irritante :( Seria bom se houvesse uma maneira de definir o atual (UI) Cultura automaticamente para a sua aplicação e tem todos os novos tópicos pegar esse valor.
Jedidja
1
Faz tanto tempo que eu trabalhei com isso agora, para não me lembrar exatamente do que fizemos. Mas acho que, em vez de usar datas no conjunto de dados enviado ao visualizador de relatórios, formatamos a data em uma string primeiro, usando a cultura que definimos no aplicativo. Então, não importava mais que os segmentos de renderização do relatório usassem a cultura errada. Portanto, não resolvemos o problema com os threads usando a cultura errada. Nós apenas contornado o problema da obtenção de datas formatado de forma errada :)
Svish
2
Em nosso aplicativo, tentei "capturar" os threads (chamando um método especial de determinados lugares) e armazená-los em uma coleção para uso posterior (neste caso, para definir a cultura), que parece estar funcionando muito longe.
Sr. TA
1
Não foi possível armazenar o objeto cultureinfo na sessão e (se você estiver usando páginas-mestre) verificá-lo no código da sua página-mestre e definir o segmento atual?
user609926
20

Se você estiver usando recursos, poderá forçá-lo manualmente:

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

No gerenciador de recursos, há um código gerado automaticamente, que é o seguinte:

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

Agora, toda vez que você se refere à sua sequência individual nesse recurso, ela substitui a cultura (encadeamento ou processo) pelo resourceCulture especificado.

Você pode especificar o idioma como em "fr", "de" etc., ou colocar o código do idioma como em 0x0409 para en-US ou 0x0410 para it-IT. Para obter uma lista completa dos códigos de idioma, consulte: Identificadores de idioma e localidades

Reza S
fonte
Pode ser "não forçado", definindo-o como Nulo:Resource1.Culture = Null;
habakuk
8

Para .net 4.5 e superior, você deve usar

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

Na verdade, você pode definir a cultura de thread padrão e a cultura da interface do usuário, mas apenas com o Framework 4.5+

Eu coloquei nesse construtor estático

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 coloque um ponto de interrupção no método Convert de um ValueConverter para ver o que chegou ao outro extremo. CultureInfo.CurrentUICulture deixou de ser en-US e tornou-se en-AU completo com meu pequeno truque para fazer respeitar as configurações regionais de ShortTimePattern.

Viva, tudo está bem no mundo! Ou não. O parâmetro de cultura passado para o método Convert ainda é en-US. Erm, WTF ?! Mas é um começo. Pelo menos assim

  • você pode corrigir a cultura da interface do usuário uma vez quando o aplicativo for carregado
  • é sempre acessível a partir de CultureInfo.CurrentUICulture
  • string.Format("{0}", DateTime.Now) usará suas configurações regionais personalizadas

Se você não pode usar a versão 4.5 da estrutura, desista de configurar o CurrentUICulture como uma propriedade estática de CultureInfo e defina-a como uma propriedade estática de uma de suas próprias classes. Isso não corrige o comportamento padrão de string.Format ou faz com que o StringFormat funcione corretamente nas ligações e percorre a árvore lógica do aplicativo para recriar todas as ligações no aplicativo e definir a cultura do conversor.

Peter Wone
fonte
1
A resposta de Austin já forneceu a ideia de que CultureInfo.DefaultThreadCurrentCulture pode ser definido para alterar a cultura do AppDomain. CultureInfo.DefaultThreadCurrentUICulture é CurrentUICulture como CultureInfo.DefaultThreadCurrentCulture é CurrentCulture. Realmente não há razão para obter o valor diretamente do registro no seu código. Se você deseja alterar o CurrentCulture para alguma cultura específica, mas retém as substituições do usuário, basta obter o valor de DateTimeFormat do CurrentCulture antes de substituí-lo.
Eric MSFT
1
Vou ter que verificar, mas me lembro de tentar isso sem sucesso, levando a obtê-lo do registro. Você realmente acha que eu faria todo esse problema sem motivo? O acesso ao registro é uma PITA gigante neste admirável mundo novo do UAC.
Peter Wone
4

Para o ASP.NET5, ou seja, ASPNETCORE, você pode fazer o seguinte em 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")
    }
});

Aqui está uma série de postagens no blog que fornece mais informações.

Sean
fonte
1
Eu vim procurando uma maneira de fazer isso em um site herdado do ASP.NET 2.0, e é frustrante a facilidade com que eu o gerenciei com outro site Core em comparação! Estou em uma empresa multinacional e, para sites internos, toda a nossa formatação é nos EUA para evitar confusões de datas. Mas este site herdado está configurado para en-US, en-CA e fr-CA. E de uma maneira "hackeada", então, quando vou corrigi-lo, todas as suas conversões de tipo explodem na camada de dados! (Valores em dinheiro francês sendo "1 234,56 $"
Andrew S
1
@ Sean, seu link está quebrado. Talvez aponte para isto , isto e isto #
Fer R
4

Esta resposta é um pouco de expansão para a ótima resposta do @ rastating. Você pode usar o código a seguir para todas as versões do .NET sem preocupações:

    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 ఠ_ఠ
fonte
4

DefaultThreadCurrentCulturee também DefaultThreadCurrentUICultureestão presentes no Framework 4.0, mas são privados. Usando o Reflection, você pode configurá-los facilmente. Isso afetará todos os threads em que CurrentCulturenão está explicitamente definido (executando threads também).

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
fonte
1
Este funcionou para mim. Embora esteja usando Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = cultura; Que parece o mesmo não. Para maior clareza isto está sendo usado em um aplicativo WPF onde o aplicativo em si estava mudando sua cultura no entanto usercontrols em outros projetos foram usando a cultura navegador não a cultura selecionada pelo usuário
chillfire
3

Aqui está a solução para c # MVC:

  1. Primeiro: crie um atributo personalizado e substitua o método como este:

    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. Segundo: no App_Start, localize FilterConfig.cs, adicione este atributo. (isso funciona para aplicação INTEIRA)

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

É isso aí !

Se você deseja definir a cultura para cada controlador / ação em vez de todo o aplicativo, você pode usar este atributo como este:

[Culture]
public class StudentsController : Controller
{
}

Ou:

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

Solução de trabalho para definir CultureInfo para todos os threads e janelas.

  1. Abra o arquivo App.xaml e adicione um novo atributo "Inicialização" para atribuir o manipulador de eventos de inicialização ao aplicativo:
<Application ........
             Startup="Application_Startup"
>
  1. Abra o arquivo App.xaml.cs e adicione esse código ao manipulador de inicialização criado (nesse caso, Application_Startup). A classe App ficará assim:
    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
fonte