Distribuindo bibliotecas compatíveis com i18n
Esta página foi traduzida por PageTurner AI (beta). Não é oficialmente endossada pelo projeto. Encontrou um erro? Reportar problema →
Em aplicações/monorepositórios de grande escala, nem todos os componentes/bibliotecas residem no mesmo repositório/projeto e podem ser distribuídos de forma diferente. Embora existam múltiplas formas de resolver este problema, este guia apresenta uma abordagem que observamos funcionar bem em grandes organizações de engenharia.
Conceito de alto nível
Strings traduzidas são basicamente ativos, assim como CSS, configuração estática ou imagens. A estrutura de alto nível geralmente contém várias camadas:
-
Componentes/Bibliotecas reutilizáveis que contêm strings traduzidas, podendo ser aninhados.
-
Aplicações de nível superior que consomem esses componentes/bibliotecas.
Cada funcionalidade/biblioteca seria responsável por:
-
Integrar-se com o pipeline de tradução.
-
Declarar suas strings traduzidas e agregadas usando um manifesto como package.json, uma convenção (sempre gerar saída em local específico) ou ambos.
Declaração no package.json
Isso é semelhante ao uso do atributo style para declarar CSS. Você pode declarar algo como:
{
"name": "my-library",
"version": "1.0.0",
"lang": "my-strings",
"supportedLocales": ["en", "en-GB", "ja"]
}
onde my-strings é a pasta contendo suas strings traduzidas nos seus supportedLocales:
my-strings
|- en.json
|- en-GB.json
|- ja.json
A aplicação consumidora pode percorrer node_modules buscando arquivos package.json com esses campos e agregar as strings em um único bundle (ou múltiplos bundles), servindo esses JSON conforme escolher.
Isso oferece flexibilidade para gerar traduções em qualquer local desejado, desde que declarado no package.json. Entretanto, também acarreta custo adicional de processamento no nível da aplicação e incentiva inconsistência no local de saída.
Declaração por convenção
Similar à Declaração no package.json, mas as traduções são sempre geradas em lang/{locale}.json. A aplicação principal pode:
formatjs compile "node_modules/**/lang/en.json" --ast --out-file lang/en.json
para agregar todas as strings pré-traduzidas de suas bibliotecas.
my-lib
|- src
|- lang
|- en.json
|- en-GB.json
|- ja.json
|- node_modules
|- library1
|- lang
|- en.json
|- en-GB.json
|- ja.json
|- library2
|- lang
|- en.json
|- en-GB.json
|- ja.json
Isso garante consistência e minimiza o custo de processamento de diferentes manifestos, mas é menos flexível.
Observamos que a abordagem por convention funciona melhor em grandes organizações devido à imposição de padrões, enquanto a abordagem por manifest se adapta melhor a ambientes mais abertos.
Passando o objeto intl
O núcleo de uma aplicação i18n é o objeto intl, que contém mensagens pré-compiladas, configurações de locale, formatos e cache. Portanto, ele deve ser inicializado apenas no nível superior da aplicação.
Bibliotecas de componentes podem declarar intl: IntlShape como prop e subsequentemente repassá-lo diretamente como:
- React
- Vue3
import {IntlShape} from 'react-intl'
import {MyButton, MyForm} from 'my-components'
interface Props {
intl: IntlShape
}
function MyFeature(props: Props) {
return (
<div>
<MyButton intl={props.intl} />
<MyForm intl={props.intl} />
</div>
)
}
or passing down via context using RawIntlProvider:
import {IntlShape, RawIntlProvider} from 'react-intl'
import {MyButton, MyForm} from 'my-components'
interface Props {
intl: IntlShape
}
function MyFeature(props: Props) {
return (
<RawIntlProvider value={props.intl}>
<MyButton />
<MyForm />
</RawIntlProvider>
)
}
import {useIntl} from 'vue-intl'
const MyFeature = {
setup() {
const intl = useIntl()
return () =>
h(
'p',
{},
intl.formatMessage({
id: 'foo',
defaultMessage: 'Hello',
})
)
},
}
or passing down via provideIntl
import {createIntl} from '@formatjs/intl'
import {provideIntl, useIntl} from 'vue-intl'
const Ancestor = {
setup() {
provideIntl(
createIntl({
locale: 'en',
defaultLocale: 'en',
messages: {
foo: 'Composed',
},
})
)
},
render() {
return h(MyFeature)
},
}