Guida all'aggiornamento (v2 -> v3)
Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →
Modifiche di Rottura nelle API
-
addLocaleDataè stato rimosso. Consulta Migrazione alle API Intl native per maggiori dettagli. -
ReactIntlLocaleDataè stato rimosso. Consulta Migrazione alle API Intl native per maggiori dettagli. -
intlShapeè stato rimosso. Consulta Supporto TypeScript per maggiori dettagli. -
Il componente predefinito
textComponentinIntlProviderè oraReact.Fragment. Per mantenere il comportamento precedente, imposta esplicitamentetextComponentsuspan.
<IntlProvider textComponent="span" />
-
FormattedRelativeè stato rinominatoFormattedRelativeTimecon modifiche significative all'API. Consulta FormattedRelativeTime per dettagli. -
formatRelativeè stato rinominatoformatRelativeTimecon modifiche significative all'API. Consulta FormattedRelativeTime per dettagli. -
Modifiche alla sintassi del Message Format. Consulta Modifiche alla sintassi del Message Format per dettagli.
-
IntlProvidernon eredita più dalIntlProvidersuperiore.
Utilizza React 16.3 e versioni successive
React Intl v3 supporta la nuova Context API, risolvendo vari problemi di aggiornamento dell'albero 🎉
Inoltre utilizza i nuovi lifecycle hook (rimuovendo quelli deprecati).
Supporta anche React.forwardRef(), consentendo l'accesso diretto ai ref tramite la prop standard ref (vedi sotto per dettagli).
Migra da withRef a forwardRef
Con l'aggiornamento a React >= 16.3 abbiamo introdotto React.forwardRef(), deprecando l'opzione withRef per l'HOC injectIntl in favore di forwardRef.
Quando forwardRef è impostato su true, puoi ora ignorare completamente la presenza dell'HOC.
Intl v2:
import React from 'react'
import {injectIntl} from 'react-intl'
class MyComponent extends React.Component {
doSomething = () => console.log(this.state || null)
render() {
return <div>Hello World</div>
}
}
export default injectIntl(MyComponent, {withRef: true})
// somewhere else
class Parent extends React.Component {
componentDidMount() {
this.myComponentRef.getWrappedInstance().doSomething()
}
render() {
return (
<MyComponent
ref={ref => {
this.myComponentRef = ref
}}
/>
)
}
}
Intl v3:
import React from 'react'
import {injectIntl} from 'react-intl'
class MyComponent extends React.Component {
doSomething = () => console.log(this.state || null)
render() {
return <div>Hello World</div>
}
}
export default injectIntl(MyComponent, {forwardRef: true})
// somewhere else
class Parent extends React.Component {
myComponentRef = React.createRef()
componentDidMount() {
this.myComponentRef.doSomething() // no need to call getWrappedInstance()
}
render() {
return <MyComponent ref={this.myComponentRef} />
}
}
Nuovo hook useIntl come alternativa a injectIntl HOC
Questa versione v3 supporta le React hook API per React >= 16.8. Puoi utilizzare l'hook useIntl come alternativa all'HOC injectIntl nei componenti funzionali. Entrambi i metodi consentono l'accesso all'istanza intl. Ecco un confronto rapido:
// injectIntl
import {injectIntl} from 'react-intl'
const MyComponentWithHOC = injectIntl(({intl, ...props}) => {
// do something
})
// useIntl
import {useIntl} from 'react-intl'
const MyComponentWithHook = props => {
const intl = useIntl()
// do something
}
Per mantenere l'API semplice e pulita, forniamo solo l'hook useIntl. Se preferito, puoi estenderlo per creare hook personalizzati come useFormatMessage. Consulta il sito ufficiale di React per un'introduzione generale agli hook.
Migrazione alle API Intl native
React Intl v3 non include più dati CLDR, basandosi invece sulle API Intl native. Nello specifico:
-
Intl.PluralRules: Polyfill disponibile qui.
-
Intl.RelativeTimeFormat: Polyfill disponibile qui.
Questo cambiamento rende React Intl più futuro-proof, poiché queste API sono stabili e implementate nei browser moderni. Ciò significa anche che non includiamo più dati CLDR nel pacchetto.
Se in precedenza utilizzavi addLocaleData per supportare browser obsoleti, ti consigliamo di procedere come segue:
-
Se supporti browser privi di Intl.PluralRules (es. IE11 e Safari 12-), includi questo polyfill nel tuo build.
-
Se supporti browser privi di Intl.RelativeTimeFormat (es. IE11, Edge, Safari 13-), includi questo polyfill nel tuo build insieme ai dati CLDR specifici per ogni lingua supportata.
require('@formatjs/intl-pluralrules/polyfill')
require('@formatjs/intl-pluralrules/locale-data/de') // Add locale data for de
require('@formatjs/intl-relativetimeformat/polyfill')
require('@formatjs/intl-relativetimeformat/locale-data/de') // Add locale data for de
Quando utilizzi React Intl in Node.js, il tuo binario node deve:
- Essere compilato con
full-icuseguendo queste istruzioni
OPPURE
- Utilizzare il pacchetto npm
full-icu
Supporto TypeScript
react-intl è stato riscritto in TypeScript e offre quindi supporto nativo. Abbiamo inoltre rimosso la dipendenza prop-types ed esposto IntlShape come interfaccia.
Tutti i tipi dovrebbero essere disponibili dal file index principale senza importare da sottocartelle specifiche. Esempio:
import {IntlShape} from 'react-intl' // Correct
import {IntlShape} from 'react-intl/lib/types' // Incorrect
Se manca il supporto per qualche interfaccia a livello principale, segnalacelo o invia una PR: sarà molto apprezzato :)
Potrebbero servire modifiche al codice se facevi affidamento sul deprecato pacchetto @types/react-intl. L'esempio più comune è InjectedIntlProps, da sostituire con WrappedComponentProps.
FormattedRelativeTime
Quando introdussimo FormattedRelative, la specifica di Intl.RelativeTimeFormat era instabile. Ora è allo stage 3 e implementata in vari browser. L'API differisce però da FormattedRelative, quindi l'abbiamo adeguata alla specifica rompendo la retrocompatibilità.
- Le
units(es.day-short) diventano combinazioni diunitestyle:
<FormattedRelative units="second-short"/>
// will be
<FormattedRelativeTime unit="second" style="short"/>
- Lo
stylediventanumeric(valore predefinito):
<FormattedRelative style="numeric"/>
// will be
<FormattedRelativeTime />
<FormattedRelative style="best fit"/>
// will be
<FormattedRelativeTime numeric="auto"/>
- Il tipo di
valuenon è piùDate, ma ildeltanell'unitspecificata:
<FormattedRelative value={Date.now() - 1000} units="second-narrow"/>
// will be
<FormattedRelativeTime value={-1} unit="second" style="narrow" />
<FormattedRelative value={Date.now() + 2000} units="second-narrow"/>
// will be
<FormattedRelativeTime value={2} unit="second" style="narrow" />
updateIntervaldiventaupdateIntervalInSecondsaccettando solo delta temporali in secondi. Il comportamento d'aggiornamento resta invariato, es:
<FormattedRelativeTime
value={2}
numeric="auto"
unit="second"
style="narrow"
updateIntervalInSeconds={1}
/>
// Initially prints: `in 2s`
// 1 second later: `in 1s`
// 1 second later: `now`
// 1 second later: `1s ago`
// 60 seconds later: `1m ago`
initialNowè stato rimosso.
Analogamente, la controparte funzionale formatRelative è stata rinominata formatRelativeTime con parametri allineati alle props del componente.
- Implementare il comportamento di
FormattedRelative
Puoi usare @formatjs/intl-utils per replicare parzialmente il vecchio comportamento:
import {selectUnit} from '@formatjs/intl-utils'
const {value, unit} = selectUnit(Date.now() - 48 * 3600 * 1000)
// render
;<FormattedRelativeTime value={value} unit={unit} />
Formattazione avanzata per FormattedMessage & formatMessage
Nella v2, per la formattazione avanzata (incorporare un ReactElement) occorreva:
<FormattedMessage
defaultMessage="To buy a shoe, { link } and { cta }"
values={{
link: (
<a class="external_link" target="_blank" href="https://www.shoe.com/">
visit our website
</a>
),
cta: <strong class="important">eat a shoe</strong>,
}}
/>
Ora puoi fare:
<FormattedMessage
defaultMessage="To buy a shoe, <a>visit our website</a> and <cta>eat a shoe</cta>"
values={{
a: msg => (
<a class="external_link" target="_blank" href="https://www.shoe.com/">
{msg}
</a>
),
cta: msg => <strong class="important">{msg}</strong>,
}}
/>
Questa modifica risolve diversi problemi:
-
Perdita di informazioni contestuali durante lo styling di parti della stringa: nell'esempio precedente,
linkè di fatto un placeholder a scatola nera per il traduttore. Può rappresentare una persona, un animale o un timestamp. Trasmettere informazioni contestuali tramite le variabilidescriptioneplaceholderspesso non è sufficiente poiché la variabile può diventare sufficientemente complessa. -
Allinea le funzionalità a librerie come fluent di Mozilla (usando Overlays).
Nei casi in cui in precedenza veniva passato un ReactElement come segnaposto, consigliamo vivamente di ripensare la struttura in modo che la maggior parte possibile del testo sia dichiarato esplicitamente:
Prima
<FormattedMessage
defaultMessage="Hello, {name} is {awesome} and {fun}"
values={{
name: <b>John</b>,
awesome: <span style="font-weight: bold;">awesome</span>
fun: <span>fun and <FormattedTime value={Date.now()}/></span>
}}
/>
Dopo
<FormattedMessage
defaultMessage="Hello, <b>John</b> is <custom>awesome</custom> and <more>fun and {ts, time}</more>"
values={{
b: name => <b>{name}</b>,
custom: str => <span style="font-weight: bold;">{str}</span>,
more: chunks => <span>{chunks}</span>,
}}
/>
Build ESM
react-intl e le librerie sottostanti (intl-messageformat-parser, intl-messageformat, @formatjs/intl-relativetimeformat, intl-format-cache, intl-utils) esportano artefatti ESM. Ciò significa che dovresti configurare la tua toolchain di build per transpilare queste librerie.
Jest
Aggiungi transformIgnorePatterns per includere sempre queste librerie, ad esempio:
{
transformIgnorePatterns: [
'/node_modules/(?!intl-messageformat|intl-messageformat-parser).+\\.js$',
],
}
webpack
Se utilizzi babel-loader, aggiungi queste librerie in include, ad esempio:
include: [
path.join(__dirname, "node_modules/react-intl"),
path.join(__dirname, "node_modules/intl-messageformat"),
path.join(__dirname, "node_modules/intl-messageformat-parser"),
],
Creazione di intl senza utilizzare Provider
Abbiamo aggiunto una nuova API chiamata createIntl che consente di creare un oggetto IntlShape senza utilizzare Provider. Ciò permette di formattare contenuti al di fuori del ciclo di vita di React riutilizzando lo stesso oggetto intl. Esempio:
import {createIntl, createIntlCache, RawIntlProvider} from 'react-intl'
// This is optional but highly recommended
// since it prevents memory leak
const cache = createIntlCache()
const intl = createIntl({
locale: 'fr-FR',
messages: {}
}, cache)
// Call imperatively
intl.formatNumber(20)
// Pass it to IntlProvider
<RawIntlProvider value={intl}>{foo}</RawIntlProvider>
Questo è particolarmente vantaggioso in SSR dove puoi riutilizzare lo stesso oggetto intl tra diverse richieste.
Modifiche alla sintassi del formato messaggio
Abbiamo riscritto il nostro parser per essere più fedele allo ICU Message Format, per supportare potenzialmente gli skeleton. Le modifiche non retrocompatibili finora sono:
Il carattere di escape è stato cambiato in apostrofo (')
In precedenza utilizzavamo il backslash (\) come carattere di escape nella sintassi ICU, ma ciò creava problemi con i sistemi di traduzione che supportano altre implementazioni come ICU4J/ICU4C. Grazie a @pyrocat101 abbiamo modificato questo comportamento per essere conforme alle specifiche. Ciò significa:
// Before
<FormattedMessage defaultMessage="\\{foo\\}" /> //prints out "{foo}"
// After
<FormattedMessage defaultMessage="'{foo}'" /> //prints out "{foo}"
Consigliamo vivamente di leggere le specifiche per approfondire il funzionamento di quoting/escaping qui nella sezione Quoting/Escaping.
Modifica della sintassi degli argomenti segnaposto
Gli argomenti segnaposto non possono più contenere - (es: this is a {placeholder-var} è invalido mentre this is a {placeholder_var} è valido).
Test
Abbiamo rimosso IntlProvider.getChildContext per i test. Ora puoi usare createIntl per creare un oggetto intl autonomo al di fuori di React e utilizzarlo per scopi di testing. Consulta Test con React Intl per maggiori dettagli.