Internacionalização
O pacote oficial @adonisjs/i18n
adiciona suporte para internacionalização e localização aos seus aplicativos AdonisJS.
- Os auxiliares de internacionalização permitem que você execute formatação sensível ao idioma de valores específicos, como data, moeda e nome.
- A camada de localização permite que você armazene traduções e faça referência a elas dentro dos modelos Edge, erros de validação, exceções de autenticação e assim por diante.
O pacote I18n (abreviação de Internacionalização) deve ser instalado e configurado separadamente.
npm i @adonisjs/i18n@1.6.0
node ace configure @adonisjs/i18n
# CREATE: app/Middleware/DetectUserLocale.ts
# CREATE: ./resources/lang
# CREATE: config/i18n.ts
# UPDATE: .adonisrc.json { providers += "@adonisjs/i18n" }
- Auxiliares para executar formatação sensível ao idioma para datas, moedas, nomes e assim por diante. Formato de mensagens ICU.
- Adicione seu formatador de mensagens personalizado e carregador de traduções.
Ver no npmVer no GitHubReferência de API
Uso
A seguir está um exemplo básico de importação do pacote instalado e valores de formatação.
NOTA
O método I18n.locale
retorna uma instância da classe I18n para uma localidade específica. O código de localidade deve ser um código de idioma padrão ISO 639-1 válido.
import I18n from '@ioc:Adonis/Addons/I18n'
I18n.locale('en-US').formatDate(new Date())
// 10/8/2021
I18n.locale('fr').formatCurrency(100, { currency: 'EUR' })
// 100,00 €
const luxonDate = DateTime.local().minus({ minutes: 10 })
I18n.locale('pt').formatRelativeTime(luxonDate, 'auto')
// há 10 minutos
Você pode usar o método formatMessage
para formatar traduções armazenadas. O método aceita a chave da mensagem como o primeiro argumento e os dados como o segundo argumento.
import I18n from '@ioc:Adonis/Addons/I18n'
I18n
.locale('en-US')
.formatMessage('messages.greeting', { name: 'Virk' })
Saiba mais sobre formatação de traduções →
Uso durante solicitações HTTP
É recomendável usar o objeto ctx.i18n
durante as solicitações HTTP. É uma instância isolada da classe I18n para a solicitação atual.
Route.get('/', async ({ i18n }) => {
return i18n.formatCurrency(100, { currency: 'EUR' })
})
Por padrão, a localidade de ctx.i18n
é definida como a localidade padrão do aplicativo. Portanto, é recomendável usar o middleware DetectUserLocale para encontrar a localidade do usuário e atualizá-la para o restante da solicitação.
Config
A configuração é armazenada dentro do arquivo config/i18n.ts
. Você sempre pode encontrar o config stub atualizado no GitHub.
import Application from '@ioc:Adonis/Core/Application'
import { I18nConfig } from '@ioc:Adonis/Addons/I18n'
const i18nConfig: I18nConfig = {
translationsFormat: 'icu',
defaultLocale: 'en',
// Opcional
supportedLocales: [],
fallbackLocales: {},
provideValidatorMessages: true,
loaders: {
fs: {
enabled: true,
location: Application.resourcesPath('lang'),
},
},
}
export default i18nConfig
translationsFormat
O formato a ser usado para formatar traduções. Oficialmente, apenas o formato de mensagens ICU é suportado.
defaultLocale
O defaultLocale
é o idioma padrão do seu aplicativo. Ele é sempre estático e não pode ser alterado em tempo de execução. Nós procuramos traduções do local padrão quando o idioma do usuário atual não é suportado e, também, não há fallback disponível.
const i18nConfig: I18nConfig = {
defaultLocale: 'en'
}
supportedLocales
É uma matriz de códigos de idioma formatados ISO 639-1 que seu aplicativo suporta. Se o idioma do usuário não for mencionado dentro deste array, usaremos o defaultLocale
para procurar traduções.
Você pode opcionalmente definir o supportedLocales
dentro do arquivo de configuração. Caso contrário, inferiremos os locais suportados dos diretórios de idiomas que você criou dentro do diretório resources/lang
.
const i18nConfig: I18nConfig = {
supportedLocales: ['fr', 'en', 'it']
}
fallbackLocales
O fallbackLocales
é um par de chave-valor dos locais que seu aplicativo suporta junto com seus locais de fallback.
Por exemplo: usar o espanhol como fallback para o idioma catalão faz mais sentido do que usar o inglês. Portanto, você mesmo pode definir os locais de fallback.
NOTA
O local para o qual você definiu o fallback deve fazer parte do array supportedLocales
.
const i18nConfig: I18nConfig = {
fallbackLocales: {
ca: 'es'
}
}
provideValidatorMessages
Habilite/desabilite o suporte para fornecer mensagens de validação por meio de arquivos de tradução. As mensagens são fornecidas quando o sinalizador é definido como true
.
Saiba mais sobre traduzir mensagens de validação.
loaders
Os loaders
são usados para carregar mensagens de algum tipo de armazenamento. Oficialmente, enviamos com uma implementação do carregador fs
que carrega arquivos .json
ou .yaml
do sistema de arquivos.
Correspondência de localidade
Permitimos que você defina traduções para uma região específica ou use um código de idioma de dois dígitos para suporte genérico.
Por exemplo: se você armazenar as traduções para o idioma francês dentro do diretório fr
, todas as variações do idioma francês verão as mesmas mensagens.
No entanto, se você criar diretórios específicos da região, como fr-ca
ou fr-ch
, as traduções do melhor local correspondente serão servidas.
O estilo de correspondência de localidade é conhecido como negociação de conteúdo. Em vez de procurar a correspondência exata, negociamos a correspondência mais próxima.
Encontrando o melhor local correspondente
Você deve usar o método I18n.getSupportedLocale
para encontrar o melhor local para o idioma do usuário.
O método aceita uma string ou uma matriz de idiomas do usuário e retorna o local correspondente suportado pelo seu aplicativo. null
é retornado quando nenhuma correspondência é encontrada.
import I18n from '@ioc:Adonis/Addons/I18n'
const userLanguage = 'en-US'
const bestMatch = I18n.getSupportedLocale(userLanguage)
if (bestMatch) {
I18n.locale(bestMatch).formatMessage()
} else {
I18n.locale(I18n.defaultLocale).formatMessage()
}
Detectando a localidade do usuário
Você deve usar o middleware DetectUserLocale
armazenado dentro do diretório app/Middleware
para encontrar a localidade para a solicitação HTTP de entrada.
Por padrão, o middleware usa o cabeçalho HTTP Accept-language para encontrar o idioma do navegador do usuário.
No entanto, você pode alterar a implementação deste middleware e usar qualquer estratégia que se ajuste ao seu caso de uso e às necessidades do aplicativo. Apenas tenha os seguintes pontos em mente.
- Certifique-se de sempre passar a localidade selecionada pelo usuário para o método
I18n.getSupportedLocale(userLocale)
para encontrar a melhor localidade possível suportada pelo seu aplicativo. - Se uma correspondência for encontrada, chame o método
ctx.i18n.switchLocale(locale)
para alternar o local para o restante da solicitação.
Além disso, certifique-se de registrar o middleware dentro do arquivo start/kernel.ts
.
// start/kernel.ts
Server.middleware.register([
// ... other middleware(s)
() => import('App/Middleware/DetectUserLocale')
])
Confira este projeto de exemplo que usa o alternador de idioma no aplicativo e sessões para gerenciar o idioma preferido do usuário.
Armazenamento de traduções
O carregador fs
(padrão) procura as traduções dentro do diretório resources/lang
. Você deve criar um subdiretório para cada local que seu aplicativo suporta. Por exemplo:
NOTA
O diretório de idioma deve ser nomeado após um código de idioma ISO 639-1 válido
# resources/lang
├── en
└── fr
O carregador lerá todos os arquivos .json
e .yaml
. Além disso, sinta-se à vontade para criar vários subdiretórios ou arquivos dentro de um diretório de idioma.
# resources/lang
├── en
│ ├── emails.yaml
│ └── validator.json
└── fr
└── validator.json
// resources/lang/fr/validator.json
{
"shared": {
"required": "Ce champ est requis"
}
}
# resources/lang/en/emails.yaml
welcome:
content: >-
<h2> Welcome to AdonisJS </h2>
<p> Click <a href="{ url }"> here </a> to verify your account </p>
Formatando traduções
O formatador icu
permite que você escreva traduções usando o formato de mensagens ICU. É um formato padrão da indústria para escrever traduções e é suportado por muitos serviços de tradução como Crowdin e Lokalise.
Dada a seguinte mensagem dentro do arquivo en/messages.json
.
// resources/lang/en/messages.json
{
"title": "A fully featured web framework for Node.js."
}
Você pode renderizá-lo da seguinte forma.
import I18n from '@ioc:Adonis/Addons/I18n'
I18n.locale('en').formatMessage('messages.title')
E renderizá-lo dentro de modelos usando o método auxiliar t
.
<h1> {{ t('messages.title') }} </h1>
Interpolação
A sintaxe de mensagens ICU usa uma única chave para referenciar valores dinâmicos. Por exemplo:
NOTA
A sintaxe de mensagens ICU não suporta conjuntos de dados aninhados e, portanto, você só pode acessar propriedades de um objeto plano durante a interpolação.
{
"greeting": "Hello { username }"
}
{{ t('messages.greeting', { username: 'Virk' }) }}
Você também pode escrever HTML dentro das mensagens. No entanto, certifique-se de usar três chaves dentro dos modelos do Edge para renderizar HTML sem escapar dele.
Formato numérico
Você pode formatar valores numéricos dentro das mensagens de tradução usando a sintaxe {key, type, format}
. No exemplo a seguir:
- O
amount
é o valor de tempo de execução. - O
number
é o tipo de formatação. esqueleto numérico
{
"bagel_price": "The price of this bagel is {amount, number, ::currency/USD}"
}
{{ t('bagel_price', { amount: 2.49 }) }}
The price of this bagel is $2.49
A seguir estão alguns exemplos usando o formato number
com diferentes estilos de formatação e esqueletos numéricos.
Length of the pole: {price, number, ::measure-unit/length-meter}
Account balance: {price, number, ::currency/USD compact-long}
Formato de data/hora
Você pode formatar as instâncias Date ou as instâncias luxon DateTime usando a sintaxe {key, type, format}
. No exemplo a seguir:
expectedDate
é o valor de tempo de execução.date
é o tipo de formatação.- E o
medium
é o formato da data.
{
"shipment_update": "Your package will arrive on {expectedDate, date, medium}"
}
{{ t('shipment_update', { expectedDate: luxonDateTime }) }}
Your package will arrive on Oct 16, 2021
Da mesma forma, você pode usar o formato de hora para formatar a hora para o local atual.
{
"appointment": "You have an appointment today at {appointmentAt, time, ::h:m a}"
}
You have an appointment today at 2:48 PM
Esqueletos de data/hora disponíveis
A ICU fornece uma ampla gama de padrões para personalizar o formato de data e hora. No entanto, nem todos eles estão disponíveis via API Intl do ECMA402. Portanto, oferecemos suporte apenas aos seguintes padrões.
Símbolo | Descrição |
---|---|
G | Designador de era |
y | ano |
M | mês no ano |
L | mês autônomo no ano |
d | dia no mês |
E | dia da semana |
e | dia local da semana e..eee não é suportado |
c | dia local autônomo da semana c..ccc não é suportado |
a | Marcador AM/PM |
h | Hora [1-12] |
H | Hora [0-23] |
K | Hora [0-11] |
k | Hora [1-24] |
m | Minuto |
s | Segundo |
z | Fuso horário |
Regras plurais
A sintaxe da mensagem ICU tem suporte de primeira classe para definir as regras plurais dentro de suas mensagens. Por exemplo:
NOTA
No exemplo a seguir, usamos YAML em vez de JSON, pois é mais fácil escrever texto multilinha em YAML.
cart_summary:
"You have {itemsCount, plural,
=0 {no items}
one {1 item}
other {# items}
} in your cart"
{{ t('messages.cart_summary', { itemsCount: 1 }) }}
You have 1 item in your cart.
O #
é um token especial a ser usado como um espaço reservado para o valor numérico. Ele será formatado como {key, number}
.
{{ t('messages.cart_summary', { itemsCount: 1000 }) }}
<!-- Output -->
<!-- You have 1,000 items in your cart -->
Categorias plurais disponíveis
A regra plural usa a sintaxe {key, plural, matches}
. O matches
é um valor literal e corresponde a uma das seguintes categorias plurais.
Categoria | Descrição |
---|---|
zero | Esta categoria é usada para idiomas com gramática especializada especificamente para número zero de itens. (Exemplos são árabe e letão) |
one | Esta categoria é usada para idiomas com gramática explicitamente especializada para um item. Muitos idiomas, mas não todos, usam esta categoria plural. (Muitos idiomas asiáticos populares, como chinês e japonês, não usam esta categoria.) |
two | Esta categoria é usada para idiomas que têm gramática explicitamente especializada para dois itens. (Exemplos são árabe e galês.) |
few | Esta categoria é usada para idiomas com gramática explicitamente especializada para um pequeno número de itens. Para alguns idiomas, isso é usado para 2-4 itens, para alguns 3-10 itens, e outros idiomas têm regras ainda mais complexas. |
many | Esta categoria é usada para idiomas que têm uma gramática especializada para um número mais significativo de itens. (Exemplos são árabe, polonês e russo.) |
other | Esta categoria é usada se o valor não corresponder a uma das outras categorias plurais. Observe que isso é usado para "plural" para idiomas (como inglês) que têm uma dicotomia simples "singular" versus "plural". |
=value | Isso é usado para corresponder a um valor específico, independentemente das categorias plurais do local atual. |
Tabela: O conteúdo da tabela é referenciado em formatjs.io
Selecionar
O formato select
permite que você escolha a saída comparando um valor com uma das muitas opções. Escrever texto específico de gênero é um excelente exemplo do formato select
.
# Yaml
auto_reply:
"{gender, select,
male {He}
female {She}
other {They}
} will respond shortly."
{{ t('messages.auto_reply', { gender: 'female' }) }}
She will respond shortly.
Selecionar ordinal
O formato select ordinal
permite que você escolha a saída com base nas regras de pluralização ordinal. O formato é semelhante ao formato plural
. No entanto, o valor é mapeado para uma categoria plural ordinal.
anniversary_greeting:
"It's my {years, selectordinal,
one {#st}
two {#nd}
few {#rd}
other {#th}
} anniversary"
{{ t('messages.anniversary_greeting', { years: 2 }) }}
It's my 2nd anniversary
Categorias ordinais selecionadas disponíveis
O formato ordinal selecionado usa a sintaxe {key, selectordinal, matches}. A correspondência é um valor literal e corresponde a uma das seguintes categorias plurais.
Categoria | Descrição |
---|---|
zero | Esta categoria é usada para idiomas com gramática especializada especificamente para zero número de itens. (Exemplos são árabe e letão.) |
one | Esta categoria é usada para idiomas com gramática explicitamente especializada para um item. Muitos idiomas, mas não todos, usam esta categoria plural. (Muitos idiomas asiáticos populares, como chinês e japonês, não usam esta categoria.) |
two | Esta categoria é usada para idiomas com gramática explicitamente especializada para dois itens. (Exemplos são árabe e galês.) |
few | Esta categoria é usada para idiomas com gramática explicitamente especializada para um pequeno número de itens. Para alguns idiomas, isto é usado para 2-4 itens, para alguns 3-10 itens, e outros idiomas têm regras ainda mais complexas. |
many | Esta categoria é usada para idiomas com gramática especializada para um número maior de itens. (Exemplos são árabe, polonês e russo.) |
other | Esta categoria é usada se o valor não corresponder a uma das outras categorias plurais. Observe que isto é usado para "plural" para idiomas (como inglês) que têm uma dicotomia simples "singular" versus "plural". |
=value | Isso é usado para corresponder a um valor específico, independentemente das categorias plurais do local atual. |
Tabela: O conteúdo da tabela é referenciado de formatjs.io
Formatadores Intl
Os formatadores Intl são wrappers finos sobre a API Intl do Node.js. Criar uma nova instância das classes Intl é lento, então memorizamos os construtores para acelerar as coisas. Veja benchmarks
formatNumber
O formatNumber
usa a classe Intl.NumberFormat para formatar um valor numérico.
- O primeiro argumento é o valor a ser formatado. Deve ser um número, bigint ou uma representação de string de um número.
- O segundo argumento são as opções. Elas são as mesmas que as opções aceitas pela classe
Intl.NumberFormat
.
import I18n from '@ioc:Adonis/Addons/I18n'
I18n
.locale('en')
.formatNumber(123456.789, {
maximumSignificantDigits: 3
})
formatCurrency
O método formatCurrency
usa a classe Intl.NumberFormat
, mas implicitamente define o style
para moeda.
import I18n from '@ioc:Adonis/Addons/I18n'
I18n
.locale('en')
.formatCurrency(200, {
currency: 'USD'
})
formatDate
O método formatDate
usa a classe Intl.DateTimeFormat para formatar uma data.
- O primeiro argumento é a data a ser formatada. Pode ser uma
string de data ISO
, umtimestamp
, uma instância da classe JavaScriptDate
ou um luxonDateTime
. - O segundo argumento são as opções. Elas são as mesmas que as opções aceitas pela classe
Intl.DateTimeFormat
.
import I18n from '@ioc:Adonis/Addons/I18n'
I18n
.locale('en')
.formatDate(new Date(), {
dateStyle: 'long'
})
formatTime
O método formatTime
usa a classe Intl.DateTimeFormat
, mas implicitamente define o timeStyle
como médio.
import I18n from '@ioc:Adonis/Addons/I18n'
I18n
.locale('en')
.formatTime(new Date(), {
timeStyle: 'long'
})
formatRelativeTime
O método formatRelativeTime
usando a classe Intl.RelativeTimeFormat para formatar um valor para uma string de representação de tempo relativo.
- O primeiro argumento é o valor do tempo relativo. Pode ser uma
string de data ISO
, um diff numérico absoluto, uma instância da classe JavaScriptDate
ou uma instância de luxonDateTime
. unidades oficialmente suportadas, também suportamos uma unidadeauto
adicional. - O terceiro argumento são as opções. Elas são as mesmas que as opções aceitas pela classe
Intl.RelativeTimeFormat
.
import { DateTime } from 'luxon'
import I18n from '@ioc:Adonis/Addons/I18n'
const luxonDate = DateTime.local().plus({ hours: 2 })
I18n
.locale('en')
.formatRelativeTime(luxonDate, 'hours')
Encontraremos a melhor unidade ao usar a unidade de formatação definida como auto
. Por exemplo:
const luxonDate = DateTime.local().plus({ hours: 2 })
I18n
.locale('en')
.formatRelativeTime(luxonDate, 'auto')
// In 2 hours 👈
const luxonDate = DateTime.local().plus({ hours: 200 })
I18n
.locale('en')
.formatRelativeTime(luxonDate, 'auto')
// In 8 days 👈
formatPlural
O método formatPlural
usa o Intl.PluralRules e retorna uma categoria plural para um determinado valor numérico.
- O primeiro argumento é o valor. Deve ser um número ou uma representação de string de um número.
- O segundo argumento são as opções. Elas são as mesmas que as opções aceitas pela classe
Intl.PluralRules
.
import I18n from '@ioc:Adonis/Addons/I18n'
I18n.locale('en').formatPlural(0)
// other
I18n.locale('en').formatPlural(1)
// one
I18n.locale('en').formatPlural(2)
// other
Mensagens do validador
A seguir estão as etapas para configurar o pacote i18n
para fornecer as mensagens de validação dos arquivos de traduções.
- Defina o valor de
provideValidatorMessages = true
dentro do arquivo de configuração. - Crie um arquivo
validator.json
dentro de cada diretório de idioma. - Defina mensagens para as regras de validação dentro do objeto
shared
.
// resources/lang/en/validator.json
{
"shared": {
"required": "The value for the field is required",
"unique": "Email is already in use",
"minLength": "The field must have { minLength } items"
}
}
As mensagens da chave shared
são fornecidas automaticamente ao validador. Você também pode ser específico e definir uma mensagem para uma combinação field + rule
. Por exemplo:
{
"shared": {
"required": "The value for the field is required",
"username.required": "Username is required to create an account"
}
}
Saco de mensagens personalizadas
Se alguma parte do seu aplicativo precisar de mensagens de validação específicas, você pode defini-las dentro do arquivo validator.json
sob uma chave de nível superior diferente e, em seguida, referenciá-las usando o método i18n.validatorMessages()
.
// resources/lang/en/validator.json
{
"shared": {},
"contact": {
"email.required": "Enter the email so that we can contact you",
"message.required": "Describe your project in a few words."
}
}
Agora, você pode referenciar as mensagens do objeto contact
no validador da seguinte forma.
import { schema } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class ContactValidator {
constructor(protected ctx: HttpContextContract) {}
public schema = schema.create({})
public messages = this.ctx.i18n.validatorMessages('validator.contact')
}
Mensagens de autenticação
Você também pode fornecer traduções para as exceções geradas pelo pacote auth. As traduções devem ser definidas dentro do arquivo auth.json
usando o código de exceção como a chave de tradução.
NOTA
As traduções são usadas para o texto de resposta e não para a propriedade error.message
. Elas ainda estarão em inglês e codificadas.
{
"E_INVALID_AUTH_SESSION": "Your session has expired",
"E_INVALID_API_TOKEN": "Invalid or expired API token",
"E_INVALID_BASIC_CREDENTIALS": "Invalid credentials",
"E_INVALID_AUTH_UID": "Invalid credentials",
"E_INVALID_AUTH_PASSWORD": "Invalid credentials"
}
Traduzindo e-mails
Como os e-mails geralmente são enviados em segundo plano (fora do ciclo de vida da solicitação HTTP), você deve passar explicitamente a instância i18n
para os modelos de e-mail.
O método auxiliar t
é um alias para i18n.formatMessage
, ele formatará mensagens no mesmo idioma para o qual você criou a instância da classe i18n
e a passou para o estado do modelo.
import Mail from '@ioc:Adonis/Addons/Mail'
import I18n from '@ioc:Adonis/Addons/I18n'
const i18n = I18n.locale(customerLocale)
await Mail.send((message) => {
message
.subject(i18n.formatMessage('emails.welcome_subject'))
.htmlView('emails/welcome', { i18n })
})
Recarregando traduções
As traduções são carregadas e armazenadas em cache na memória na inicialização do aplicativo. Portanto, quaisquer alterações feitas nos arquivos de tradução não serão refletidas até que você reinicie o processo.
Durante o desenvolvimento, o servidor dev será reiniciado na alteração do arquivo. No entanto, na produção, você terá que reiniciar o servidor manualmente (como uma nova implantação).
Se por algum motivo você quiser recarregar as traduções dentro do processo em execução, poderá usar o método I18n.reloadTranslations()
para fazer isso.
import I18n from '@ioc:Adonis/Addons/I18n'
await I18n.reloadTranslations()
Relatando traduções ausentes
Para ajudar você a adicionar progressivamente traduções para novos idiomas, relatamos as traduções ausentes emitindo o evento i18n:missing:translation
.
Crie um novo arquivo de pré-carregamento start/i18n.ts
executando o seguinte comando Ace. Selecione todos os ambientes.
node ace make:prldfile i18n
Abra o arquivo recém-criado e cole o seguinte conteúdo dentro dele. Atualmente, estamos usando o método I18n.prettyPrint
para registrar a mensagem no console. No entanto, você também pode usar o logger para registrar a mensagem.
// start/i18n.ts
import Event from '@ioc:Adonis/Core/Event'
import I18n from '@ioc:Adonis/Addons/I18n'
Event.on('i18n:missing:translation', I18n.prettyPrint)
Adicionar formatador de mensagem personalizado
O formatador de mensagem define a sintaxe e os recursos das traduções armazenadas. O pacote é fornecido com um formatador icu
que usa a sintaxe de mensagens ICU para escrever traduções.
No entanto, você também pode registrar um formatador de mensagem personalizado usando o método I18n.extend
. A implementação do formatador deve aderir à interface TranslationsFormatterContract.
interface TranslationsFormatterContract {
readonly name: string
format(message: string, locale: string, data?: Record<string, any>): string
}
name
Um nome exclusivo para o formatador. Será um valor de string estático.
format
O método format recebe os seguintes argumentos e deve retornar uma string formatada.
- O primeiro argumento é o texto da mensagem.
- O segundo argumento é o
locale
para o qual a formatação deve acontecer. - Finalmente, o objeto de dados para valores dinâmicos.
Implementação fictícia
A seguir está uma implementação muito direta que usa o mecanismo de modelo Edge para formatar traduções.
Etapa 1. Crie a classe formatadora.
Crie um novo arquivo MustacheFormatter.ts
dentro do diretório providers
e cole o seguinte conteúdo dentro dele.
import type { ViewContract } from '@ioc:Adonis/Core/View'
import type { TranslationsFormatterContract } from '@ioc:Adonis/Addons/I18n'
export class MustacheFormatter implements TranslationsFormatterContract {
public readonly name = 'mustache'
constructor(private view: ViewContract) {}
public format(message: string, _: string, data?: Record<string, any>) {
return this.view.renderRawSync(message, data)
}
}
Etapa 2. Estenda o I18n e registre o formatador
Abra o arquivo providers/AppProvider.ts
e registre o formatador dentro do método boot
.
// providers/AppProvider.ts
import { ApplicationContract } from '@ioc:Adonis/Core/Application'
import { MustacheFormatter } from './MustacheFormatter'
export default class AppProvider {
constructor(protected app: ApplicationContract) {}
public register() {
// Registre suas próprias ligações
}
public async boot() {
const I18n = this.app.container.resolveBinding('Adonis/Addons/I18n')
const View = this.app.container.resolveBinding('Adonis/Core/View')
I18n.extend('mustache', 'formatter', () => new MustacheFormatter(View))
}
public async ready() {
// O aplicativo está pronto
}
public async shutdown() {
// Limpeza, pois o aplicativo está fechando
}
}
Etapa 3. Use o formatador mustache
Atualize o arquivo de configuração e defina o translationsFormat
para mustache.
{
translationsFormat: 'mustache'
}
Adicione um carregador de mensagens personalizado
O carregador de mensagens é responsável por carregar as mensagens de uma fonte permanente. O pacote é fornecido com um formatador fs
que lê os arquivos .json
e .yaml
do sistema de arquivos.
No entanto, você também pode registrar carregadores personalizados usando o método I18n.extend
. A implementação do carregador deve aderir à interface LoaderContract.
type Translations = {
[lang: string]: Record<string, string>
}
interface LoaderContract {
load(): Promise<Translations>
}
Os carregadores só precisam implementar um único método chamado load
que retorna todas as traduções como um objeto.
As chaves de nível superior do objeto são os códigos de idioma, e o valor é outro objeto de mensagens.
{
en: {},
fr: {},
it: {}
}
Além disso, certifique-se de converter mensagens aninhadas dentro de um objeto de idioma para um objeto simples. Por exemplo:
{
en: {
'messages.title': '',
'messages.subtitle': ''
}
}
Implementação fictícia
A seguir está uma implementação muito direta que lê as mensagens do banco de dados usando Lucid.
Etapa 1. Crie a classe do carregador.
Crie um novo arquivo DbLoader.ts
dentro do diretório providers
e cole o seguinte conteúdo dentro dele.
import type { DatabaseContract } from '@ioc:Adonis/Lucid/Database'
import type {
Translations,
LoaderContract
} from '@ioc:Adonis/Addons/I18n'
export type DbLoaderConfig = {
enabled: boolean
table: string
}
export class DbLoader implements LoaderContract {
constructor(private db: DatabaseContract, private config: DbLoaderConfig) {}
public async load() {
const rows = await this.db.from(this.config.table)
return rows.reduce<Translations>((result, row) => {
result[row.locale] = result[row.locale] || {}
result[row.locale][row.key] = row.message
return result
}, {})
}
}
Etapa 2. Estenda o I18n e registre o carregador
Abra o arquivo providers/AppProvider.ts
e registre o carregador dentro do método boot
.
// providers/AppProvider.ts
import { ApplicationContract } from '@ioc:Adonis/Core/Application'
import { DbLoader } from './DbLoader'
export default class AppProvider {
constructor(protected app: ApplicationContract) {}
public register() {
// Registre suas próprias ligações
}
public async boot() {
const I18n = this.app.container.resolveBinding('Adonis/Addons/I18n')
const Db = this.app.container.resolveBinding('Adonis/Lucid/Database')
I18n.extend('db', 'loader', (_, config) => {
return new DbLoader(Db, config)
})
}
public async ready() {
// O aplicativo está pronto
}
public async shutdown() {
// Limpeza, pois o aplicativo está fechando
}
}
Etapa 3. Use o carregador db
Atualize o arquivo de configuração e adicione a chave do carregador db
ao objeto loaders
.
{
loaders: {
fs: {},
db: {
enabled: true,
table: 'translations'
}
}
}
Etapa 4. Crie a tabela de traduções
Use a migração a seguir para criar a tabela de traduções.
NOTA
Ao executar a migração, você terá que desabilitar o carregador db
dentro do arquivo de configuração. Caso contrário, o carregador tentará ler as mensagens de uma tabela não existente.
import BaseSchema from '@ioc:Adonis/Lucid/Schema'
export default class Translations extends BaseSchema {
protected tableName = 'translations'
public async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id')
table.string('locale', 8).notNullable()
table.string('key').notNullable()
table.text('message', 'longtext').notNullable()
table.timestamp('created_at', { useTz: true })
table.timestamp('updated_at', { useTz: true })
table.unique(['locale', 'key'])
})
}
public async down() {
this.schema.dropTable(this.tableName)
}
}
Leitura adicional
Certifique-se de ler o guia de referência da API para visualizar todas as propriedades e métodos disponíveis.