QPANC - Parte 6 - ASP.NET - Regionalização
Tags: quasar, postgres, aspnetcore, docker
Published: 2020-04-19 23:36:25

> **QPANC** são as iniciais de **Q**uasar **P**ostgreSQL **A**SP **N**ET **C**ore.

  * [Source](https://github.com/TobyMosque/qpanc)
  * [Introdução](https://dev.to/quasar-pt-br/arquiteturando-uma-aplicacao-com-quasar-postgresql-asp-net-core-e-docker-introducao-o7o)
  * [Parte I - ASP.NET - Inicializando os Projetos](https://dev.to/quasar-pt-br/qpanc-parte-1-asp-net-inicializando-os-projetos-lkm)
  * [Parte 2 - PostgreSQL](https://dev.to/quasar-pt-br/qpanc-parte-2-postgresql-51pb)
  * [Parte 3 - ASP.NET - Registrando Serviços e Lendo Variáveis de Ambiente](https://dev.to/quasar-pt-br/qpanc-parte-3-asp-net-registrando-servicos-e-lendo-variaveis-de-ambiente-3do2)
  * [Parte 4 - ASP.NET - Entity Framework e ASP.NET Core Identity](https://dev.to/quasar-pt-br/qpanc-parte-4-asp-net-entity-framework-e-asp-net-core-identity-40f9)
  * [Parte 5 - ASP.NET - Documentação Interativa com Swagger](https://dev.to/quasar-pt-br/qpanc-parte-5-asp-net-documentacao-interativa-com-swagger-4n0o)
  * [Parte 6 - ASP.NET - Regionalização](https://dev.to/quasar-pt-br/qpanc-parte-6-asp-net-regionalizacao-13p5)
  * [Parte 7 - ASP.NET - Autenticação e Autorização](https://dev.to/quasar-pt-br/qpanc-parte-7-asp-net-autenticacao-e-autorizacao-bpd)
  * [Parte 8 - ASP.NET - CORS](https://dev.to/quasar-pt-br/qpanc-parte-8-asp-net-cors-52dn)
  * [Parte 9 - Quasar - Criação e Configuração do Projeto](https://dev.to/quasar-pt-br/qpanc-parte-9-quasar-criacao-e-configuracao-do-projeto-5hbi)
  * [Parte 10 - Quasar - Configurações e Customizações](https://dev.to/quasar-pt-br/qpanc-parte-10-quasar-configuracoes-e-customizacoes-2i86)
  * [Parte 11 - Quasar - Componentes - Diferença entre SPA e SSR](https://dev.to/quasar-pt-br/qpanc-parte-11-quasar-componentes-diferenca-entre-spa-e-ssr-1a3c)
  * [Parte 12 - Quasar - Serviços](https://dev.to/quasar-pt-br/qpanc-parte-12-quasar-servicos-4e61)
  * [Parte 13 - Quasar - Regionalização e Stores](https://dev.to/quasar-pt-br/qpanc-parte-13-quasar-regionalizacao-e-stores-1kep)
  * [Parte 14 - Quasar - Consumindo a API](https://dev.to/quasar-pt-br/qpanc-parte-14-quasar-consumindo-a-api-fn6)
  * [Parte 15 - Quasar - Login](https://dev.to/quasar-pt-br/qpanc-parte-15-quasar-login-jc6)
  * [Parte 16 - Quasar - Áreas Protegidas](https://dev.to/quasar-pt-br/qpanc-parte-16-quasar-areas-protegidas-3jkn)
  * [Parte 17 - Quasar - Registro](https://dev.to/quasar-pt-br/qpanc-parte-17-quasar-registro-3b8c)
  * [Parte 18 - Docker - Maquina Virtual Linux](https://dev.to/quasar-pt-br/qpanc-parte-18-docker-maquina-virtual-linux-g5c)
  * [Parte 19 - Docker - Registro e Build](https://dev.to/quasar-pt-br/qpanc-parte-19-docker-registro-e-build-31i4)
  * [Parte 20 - Docker - Traefik e Publicação](https://dev.to/quasar-pt-br/qpanc-parte-20-docker-traefik-e-publicacao-3g3d)
  * [Demo Online](https://qpanc.tobiasmesquita.dev)

##11 Regionalização

> Por hora iremos utilizar arquivos `RESX` para armazenar os textos à serem localizados, porém arquivos arquivos `JSON` são mais apropriados para esta função, por serem mais legíveis e simples para nós humanos, podendo ser facilmente traduzidos por alguém que na seja da area.

> Porém, não existe uma implementação oficial para substituir os arquivos `RESX` por `JSON`, cabendo ao desenvolvedor implementar e registrar as interfaces `IStringLocalizer` e `IStringLocalizerFactory`.

> Como esta implementação detem uma certa complexibilidade, irei deixa-la para um outro Artigo.

Agora, iremos tratar de outro aspecto vital para boa parte das aplicações, que é a regionalização, e aqui eu ouso dizer, que mesmo que a aplicação não tenha planos de ser publicada para públicos estrangeiros, é interessante que a aplicação esteja pronta para esta demanda desde o começo.

Adicione o pacote `Microsoft.Extensions.Localization.Abstractions` ao projeto `QPANC.Services.Abstract`.

```
cd QPANC.Services.Abstract
dotnet add package Microsoft.Extensions.Localization.Abstractions
```

Crie a classe `Messages.cs` na raiz do projeto `QPANC.Services.Abstract`, assim como a pasta `Resources`. A classe `Messages.cs` será uma classe vazia, porém necessária.

**QPANC.Services.Abstract/Messages.cs**
```cs
namespace QPANC.Services.Abstract
{
    public class Messages
    {
    }
}
```

Dentro da pasta Resources, crie os arquivos de resource, `Messages.en.resx` e `Messages.pt.resx`. após criar os arquivos, verifique se ambos estão usando o `access modifier` `no code generation`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/gm78fs7fkfc3m9ys2ud0.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nfg3whs4daoc0b1jh2du.png)

Agora precisamos adicionar 24 entradas para estes dois arquivos: 

**Messages.en.resx**

|Name|Value|Comment|
|:-----|:-----|:-----|
|ErrorMessage_Compare|'{0}' and '{1}' do not match|   |
|ErrorMessage_CreditCard|The {0} field is not a valid credit card number|   |
|ErrorMessage_CustomValidation|{0} is not valid|   |
|ErrorMessage_Email|The {0} field is not a valid e-mail address|   |
|ErrorMessage_IncorrectPasswordOrUsername|Incorrect password of username not found|   |
|ErrorMessage_MaxLength|The field {0} must be a string with a maximum length of '{1}'|   |
|ErrorMessage_MaxLengthArray|The field {0} must be a array type with a maximum length of '{1}'|   |
|ErrorMessage_MinLength|The field {0} must be a string with a minimum length of '{1}'|   |
|ErrorMessage_MinLengthArray|The field {0} must be a array type with a minimum length of '{1}'|   |
|ErrorMessage_PasswordTooWeak|Password is too weak, please improve your strength|   |
|ErrorMessage_Range|The field {0} must be between {1} and {2}|   |
|ErrorMessage_Regex|The field {0} must match the regular expression '{1}'|   |
|ErrorMessage_Required|The {0} field is required|   |
|ErrorMessage_StringLength|The field {0} must be a string with a maximum length of {1}|   |
|ErrorMessage_StringLengthIncludingMinimum|The field {0} must be a string with a minimum length of {2} and a maximum length of {1}|   |
|ErrorMessage_UserNameAlreadyTaken|Email already taken by another user|   |
|ErrorMessage_Validation|The field {0} is invalid|   |
|Field_ConfirmPassword|Confirm your Password|   |
|Field_ConfirmUserName|Confirm your Email|   |
|Field_FirstName|First Name|   |
|Field_FirstLastName|Last Name|   |
|Field_Password|Password|   |
|Field_UserName|Email|   |
|Text_ProblemDetails|One or more validation errors occurred.|   |

**Messages.pt.resx**

|Name|Value|Comment|
|:-----|:-----|:-----|
|ErrorMessage_Compare|'{0}' e '{1}' não são iguais|   |
|ErrorMessage_CreditCard|O campo {0} não possui um numero de cartão de crédito válido|   |
|ErrorMessage_CustomValidation|{0} não é válido|   |
|ErrorMessage_Email|O campo {0} não possui um email válido|   |
|ErrorMessage_IncorrectPasswordOrUsername|Senha incorreta ou usuario não encontrado|   |
|ErrorMessage_MaxLength|O campo {0} deve ser um texto com tamanho maximo de '{1}' caracter(es)|   |
|ErrorMessage_MaxLengthArray|O campo {0} deve ser uma lista com tamanho maximo de '{1}' elemento(s)|   |
|ErrorMessage_MinLength|O campo {0} deve ser um texto com tamanho minimo de '{1}' caracter(es)|   |
|ErrorMessage_MinLengthArray|O campo {0} deve ser uma lista com tamanho minimo de '{1}' elemento(s)|   |
|ErrorMessage_PasswordTooWeak|Senha é muito fraca, por favor torne ela mais forte|   |
|ErrorMessage_Range|O campo {0} deve ter um valor entre {1} e {2}|   |
|ErrorMessage_Regex|O campo {0} tem de respeitar a seguinte expressão regular: '{1}'|   |
|ErrorMessage_Required|O campo {0} é requerido|   |
|ErrorMessage_StringLength|O campo {0} deve ser um texto com tamanho maximo de {1} caracter(es)|   |
|ErrorMessage_StringLengthIncludingMinimum|O campo {0} deve ser um texto com tamanho minimo de {2} caracter(es) e maximo de {1} caracter(es)|   |
|ErrorMessage_UserNameAlreadyTaken|Email já em uso por outro usuário|   |
|ErrorMessage_Validation|O campo {0} é inválido|   |
|Field_ConfirmPassword|Confirme à Senha|   |
|Field_ConfirmUserName|Confirme o Email|   |
|Field_FirstName|Nome|   |
|Field_LastName|Sobrenome|   |
|Field_Password|Senha|   |
|Field_UserName|Email|   |
|Text_ProblemDetails|Ocorreram um ou mais erros de validação|   |

Caso esteja usando o VSCore, crie dois arquivos `xml`, renomeie eles para `Messages.en.resx` e `Messages.pt.resx`, e copie o seguinte conteúdo:

**Messages.en.resx**
```xml
<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="ErrorMessage_Compare" xml:space="preserve">
    <value>'{0}' and '{1}' do not match</value>
  </data>
  <data name="ErrorMessage_CreditCard" xml:space="preserve">
    <value>The {0} field is not a valid credit card number</value>
  </data>
  <data name="ErrorMessage_CustomValidation" xml:space="preserve">
    <value>{0} is not valid</value>
  </data>
  <data name="ErrorMessage_Email" xml:space="preserve">
    <value>The {0} field is not a valid e-mail address</value>
  </data>
  <data name="ErrorMessage_IncorrectPasswordOrUsername" xml:space="preserve">
    <value>Incorrect password of username not found</value>
  </data>
  <data name="ErrorMessage_MaxLength" xml:space="preserve">
    <value>The field {0} must be a string with a maximum length of '{1}'</value>
  </data>
  <data name="ErrorMessage_MaxLengthArray" xml:space="preserve">
    <value>The field {0} must be a array type with a maximum length of '{1}'</value>
  </data>
  <data name="ErrorMessage_MinLength" xml:space="preserve">
    <value>The field {0} must be a string with a minimum length of '{1}'</value>
  </data>
  <data name="ErrorMessage_MinLengthArray" xml:space="preserve">
    <value>The field {0} must be a array type with a minimum length of '{1}'</value>
  </data>
  <data name="ErrorMessage_PasswordTooWeak" xml:space="preserve">
    <value>Password is too weak, please improve your strength</value>
  </data>
  <data name="ErrorMessage_Range" xml:space="preserve">
    <value>The field {0} must be between {1} and {2}</value>
  </data>
  <data name="ErrorMessage_Regex" xml:space="preserve">
    <value>The field {0} must match the regular expression '{1}'</value>
  </data>
  <data name="ErrorMessage_Required" xml:space="preserve">
    <value>The {0} field is required</value>
  </data>
  <data name="ErrorMessage_StringLength" xml:space="preserve">
    <value>The field {0} must be a string with a maximum length of {1}</value>
  </data>
  <data name="ErrorMessage_StringLengthIncludingMinimum" xml:space="preserve">
    <value>The field {0} must be a string with a minimum length of {2} and a maximum length of {1}</value>
  </data>
  <data name="ErrorMessage_UserNameAlreadyTaken" xml:space="preserve">
    <value>UserName already taken by another user</value>
  </data>
  <data name="ErrorMessage_Validation" xml:space="preserve">
    <value>The field {0} is invalid</value>
  </data>
  <data name="Field_ConfirmPassword" xml:space="preserve">
    <value>Confirm your Password</value>
  </data>
  <data name="Field_ConfirmUserName" xml:space="preserve">
    <value>Confirm your Email</value>
  </data>
  <data name="Field_FirstName" xml:space="preserve">
    <value>First Name</value>
  </data>
  <data name="Field_LastName" xml:space="preserve">
    <value>Last Name</value>
  </data>
  <data name="Field_Password" xml:space="preserve">
    <value>Password</value>
  </data>
  <data name="Field_UserName" xml:space="preserve">
    <value>Email</value>
  </data>
  <data name="Text_ProblemDetails" xml:space="preserve">
    <value>One or more validation errors occurred.</value>
  </data>
</root>
```
**Messages.pt.resx**
```xml
<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="ErrorMessage_Compare" xml:space="preserve">
    <value>'{0}' e '{1}' não são iguais</value>
  </data>
  <data name="ErrorMessage_CreditCard" xml:space="preserve">
    <value>O campo {0} não possui um numero de cartão de crédito válido</value>
  </data>
  <data name="ErrorMessage_CustomValidation" xml:space="preserve">
    <value>{0} não é válido</value>
  </data>
  <data name="ErrorMessage_Email" xml:space="preserve">
    <value>O campo {0} não possui um email válido</value>
  </data>
  <data name="ErrorMessage_IncorrectPasswordOrUsername" xml:space="preserve">
    <value>Senha incorreta ou usuario não encontrado</value>
  </data>
  <data name="ErrorMessage_MaxLength" xml:space="preserve">
    <value>O campo {0} deve ser um texto com tamanho maximo de '{1}' caracter(es)</value>
  </data>
  <data name="ErrorMessage_MaxLengthArray" xml:space="preserve">
    <value>O campo {0} deve ser uma lista com tamanho maximo de '{1}' elemento(s)</value>
  </data>
  <data name="ErrorMessage_MinLength" xml:space="preserve">
    <value>O campo {0} deve ser um texto com tamanho minimo de '{1}' caracter(es)</value>
  </data>
  <data name="ErrorMessage_MinLengthArray" xml:space="preserve">
    <value>O campo {0} deve ser uma lista com tamanho minimo de '{1}' elemento(s)</value>
  </data>
  <data name="ErrorMessage_PasswordTooWeak" xml:space="preserve">
    <value>Senha é muito fraca, por favor torne ela mais forte</value>
  </data>
  <data name="ErrorMessage_Range" xml:space="preserve">
    <value>O campo {0} deve ter um valor entre {1} e {2}</value>
  </data>
  <data name="ErrorMessage_Regex" xml:space="preserve">
    <value>O campo {0} tem de respeitar a seguinte expressão regular: '{1}'</value>
  </data>
  <data name="ErrorMessage_Required" xml:space="preserve">
    <value>O campo {0} é requerido</value>
  </data>
  <data name="ErrorMessage_StringLength" xml:space="preserve">
    <value>O campo {0} deve ser um texto com tamanho maximo de {1} caracter(es)</value>
  </data>
  <data name="ErrorMessage_StringLengthIncludingMinimum" xml:space="preserve">
    <value>O campo {0} deve ser um texto com tamanho minimo de {2} caracter(es) e maximo de {1} caracter(es)</value>
  </data>
  <data name="ErrorMessage_UserNameAlreadyTaken" xml:space="preserve">
    <value>Email já em uso por outro usuário</value>
  </data>
  <data name="ErrorMessage_Validation" xml:space="preserve">
    <value>O campo {0} é inválido</value>
  </data>
  <data name="Field_ConfirmPassword" xml:space="preserve">
    <value>Confirme à Senha</value>
  </data>
  <data name="Field_ConfirmUserName" xml:space="preserve">
    <value>Confirme o Email</value>
  </data>
  <data name="Field_FirstName" xml:space="preserve">
    <value>Nome</value>
  </data>
  <data name="Field_LastName" xml:space="preserve">
    <value>Sobrenome</value>
  </data>
  <data name="Field_Password" xml:space="preserve">
    <value>Senha</value>
  </data>
  <data name="Field_UserName" xml:space="preserve">
    <value>Email</value>
  </data>
  <data name="Text_ProblemDetails" xml:space="preserve">
    <value>Ocorreram um ou mais erros de validação</value>
  </data>
</root>
```

Por fim, vamos atualizar a classe `Messages.cs` no projeto `QPANC.Services.Abstract`, iremos adicionar uma propriedade para cada entrada no arquivo `Messages.[culture].resx`, o tipo e o valor destas propriedades não tem importância, porém iremos utilizar elas, para manter a consistência das traduções, e evitar erros de digitação.

**QPANC.Services.Abstract/Messages.cs**
```cs
namespace QPANC.Services.Abstract
{
    public class Messages
    {
        public string ErrorMessage_Compare { get; }
        public string ErrorMessage_CreditCard { get; }
        public string ErrorMessage_CustomValidation { get; }
        public string ErrorMessage_Email { get; }
        public string ErrorMessage_IncorrectPasswordOrUsername { get; }
        public string ErrorMessage_MaxLength { get; }
        public string ErrorMessage_MaxLengthArray { get; }
        public string ErrorMessage_MinLength { get; }
        public string ErrorMessage_MinLengthArray { get; }
        public string ErrorMessage_PasswordTooWeak { get; }
        public string ErrorMessage_Range { get; }
        public string ErrorMessage_Regex { get; }
        public string ErrorMessage_Required { get; }
        public string ErrorMessage_StringLength { get; }
        public string ErrorMessage_StringLengthIncludingMinimum { get; }
        public string ErrorMessage_UserNameAlreadyTaken { get; }
        public string ErrorMessage_Validation { get; }
        public string Field_ConfirmPassword { get; }
        public string Field_ConfirmUserName { get; }
        public string Field_FirstName { get; }
        public string Field_LastName { get; }
        public string Field_Password { get; }
        public string Field_UserName { get; }
        public string Text_ProblemDetails { get; }
    }
}
```

Agora, podemos voltar à nossa atenção para o projeto `QPANC.Api`. Crie a classe de configuração `ApiBehavior.cs` na pasta `Options`

```cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using QPANC.Services.Abstract;
using System.Net;

namespace QPANC.Api.Options
{
    public class ApiBehavior : IConfigureOptions<ApiBehaviorOptions>
    {
        private readonly IStringLocalizer _localizer;

        public ApiBehavior(IStringLocalizer<Messages> localizer)
        {
            this._localizer = localizer;
        }

        public void Configure(ApiBehaviorOptions options)
        {
            options.InvalidModelStateResponseFactory = context =>
            {
                var details = new ValidationProblemDetails(context.ModelState)
                {
                    Title = this._localizer[nameof(Messages.Text_ProblemDetails)],
                    Status = (int)HttpStatusCode.UnprocessableEntity
                };
                return new UnprocessableEntityObjectResult(details);
            };
        }
    }
}
```

Note que `this._localizer[nameof(Messages.Text_ProblemDetails)]` é equivalente à `this._localizer["ProblemDetailsTitle"]`, porém no primeiro, é possível usar o intellisense, localizar todos os pontos que usam este texto localizado, assim como colabora para preveni erros de digitação.

Por fim, vamos configurar a regionalização no `Startup.cs`

```cs
using Microsoft.AspNetCore.Localization;
using System.Globalization;

namespace QPANC.Api
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddLocalization(options =>
            {
                options.ResourcesPath = "Resources";
            });

            services.Configure<RequestLocalizationOptions>(options =>
            {
                var supportedCultures = new[]
                {
                    new CultureInfo("pt"),
                    new CultureInfo("en")
                };

                options.DefaultRequestCulture = new RequestCulture("en");
                options.SupportedCultures = supportedCultures;
                options.SupportedUICultures = supportedCultures;
            });

            services.AddControllers()
                .AddDataAnnotationsLocalization(options => 
                {
                    options.DataAnnotationLocalizerProvider = (type, factory) =>
                    {
                        return factory.Create(typeof(Messages));
                    };
                });
            services.ConfigureOptions<Options.ApiBehavior>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ISeeder seeder)
        {
            app.UseRequestLocalization();
        }
    }
}
```

Por hora não temos como testar, a configuração acima, mais iremos faze-lo nos próximos capitulos.