Distribuzione di librerie compatibili con l'i18n
Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →
In applicazioni/monorepos di larga scala, non tutti i componenti/librerie risiedono nello stesso repository/progetto e potrebbero essere distribuiti diversamente. Sebbene esistano diversi approcci per risolvere questo problema, questa guida fornisce una metodologia che abbiamo visto funzionare efficacemente in grandi organizzazioni ingegneristiche.
Concetto di alto livello
Le stringhe tradotte sono essenzialmente asset, proprio come CSS, configurazioni statiche o immagini. La struttura di alto livello contiene tipicamente diversi livelli:
-
Componenti/Librerie riutilizzabili contenenti stringhe tradotte, che possono essere nidificate.
-
Applicazioni di livello superiore che consumano tali componenti/librerie.
Ogni funzionalità/libreria sarebbe responsabile di:
-
Integrarsi nella pipeline di traduzione.
-
Dichiarare le proprie stringhe tradotte e aggregate utilizzando un manifest come package.json, una convenzione (output sempre in posizione specifica) o entrambi.
Dichiarazione in package.json
Analogamente all'attributo style per il CSS, puoi dichiarare elementi come:
{
"name": "my-library",
"version": "1.0.0",
"lang": "my-strings",
"supportedLocales": ["en", "en-GB", "ja"]
}
dove my-strings è la cartella contenente le stringhe tradotte nei tuoi supportedLocales:
my-strings
|- en.json
|- en-GB.json
|- ja.json
L'applicazione consumatrice può scandire node_modules cercando file package.json con questi campi, aggregare le stringhe in un bundle singolo (o multipli) e servire tali JSON secondo le proprie scelte.
Questo offre flessibilità nell'output delle traduzioni in qualsiasi posizione, purché dichiarata in package.json. Tuttavia, comporta costi aggiuntivi di elaborazione a livello applicativo e può generare inconsistenza nelle posizioni di output.
Dichiarazione tramite convenzione
Simile alla Dichiarazione in package.json, ma l'output delle traduzioni è sempre in lang/{locale}.json. L'applicazione upstream può:
formatjs compile "node_modules/**/lang/en.json" --ast --out-file lang/en.json
aggregare tutte le stringhe pre-tradotte delle sue librerie.
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
Questo garantisce coerenza e minimizza i costi di elaborazione dei manifest, sacrificando parte della flessibilità.
Abbiamo osservato che l'approccio convention funziona meglio in grandi organizzazioni grazie all'imposizione di standard strutturati, mentre l'approccio manifest è più adatto ad ambienti aperti.
Passaggio dell'oggetto intl
Il nucleo di un'applicazione i18n è l'oggetto intl, contenente messaggi precompilati, impostazioni locali, formati e cache. Deve quindi essere inizializzato esclusivamente al livello superiore dell'applicazione.
Le librerie di componenti possono dichiarare intl: IntlShape come prop e propagarlo direttamente:
- 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)
},
}