Guide de mise à jour (v2 -> v3)
Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →
Changements cassants de l'API
-
addLocaleDataa été supprimé. Voir Migrer vers les APIs Intl natives pour plus de détails. -
ReactIntlLocaleDataa été supprimé. Voir Migrer vers les APIs Intl natives pour plus de détails. -
intlShapea été supprimé. Voir Support TypeScript pour plus de détails. -
La valeur par défaut de
textComponentdansIntlProviderdevientReact.Fragment. Pour conserver l'ancien comportement, définissez explicitementtextComponentsurspan.
<IntlProvider textComponent="span" />
-
FormattedRelativea été renommé enFormattedRelativeTimeet son API a significativement changé. Voir FormattedRelativeTime pour plus de détails. -
formatRelativea été renommé enformatRelativeTimeet son API a significativement changé. Voir FormattedRelativeTime pour plus de détails. -
Changements de syntaxe du format de message. Voir Changements de syntaxe du format de message pour plus de détails.
-
IntlProvidern'hérite plus duIntlProviderparent.
Utiliser React 16.3 et supérieur
React Intl v3 prend en charge la nouvelle API de contexte, résolvant divers problèmes de mise à jour d'arbre 🎉
De plus, il utilise les nouveaux hooks de cycle de vie (et supprime ceux dépréciés).
Il prend également en charge React.forwardRef(), permettant d'accéder directement aux refs via la prop standard ref (voir ci-dessous pour plus d'informations).
Migrer de withRef vers forwardRef
Avec la mise à jour vers React >= 16.3, nous pouvons utiliser React.forwardRef(). Par conséquent, l'option withRef du HOC injectIntl est dépréciée au profit de forwardRef.
Lorsque forwardRef est défini sur true, vous pouvez désormais ignorer complètement la présence du 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} />
}
}
Nouveau hook useIntl comme alternative au HOC injectIntl
Cette version v3 prend également en charge la dernière API de hooks React pour React >= 16.8. Le hook useIntl constitue désormais une alternative au HOC injectIntl dans les composants fonctionnels. Les deux méthodes permettent d'accéder à l'instance intl, voici une comparaison rapide :
// 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
}
Pour conserver une API simple et épurée, nous fournissons uniquement le hook useIntl. Si préféré, les utilisateurs peuvent encapsuler ce hook pour créer des hooks personnalisés comme useFormatMessage. Consultez le site officiel de React pour une introduction générale aux hooks React.
Migrer vers les APIs Intl natives
React Intl v3 n'inclut plus de données CLDR et s'appuie désormais sur l'API Intl native. Plus précisément, les nouvelles API utilisées sont :
-
Intl.PluralRules : Peut être polyfillé via ce package.
-
Intl.RelativeTimeFormat : Peut être polyfillé via ce package.
Ce changement vise à pérenniser React Intl car ces API sont stables et implémentées dans les navigateurs modernes. Cela signifie également que nous n'incluons plus les CLDR dans ce package.
Si vous utilisiez auparavant addLocaleData pour la prise en charge de navigateurs anciens, nous vous recommandons de procéder comme suit :
-
Si vous prenez en charge des navigateurs sans Intl.PluralRules (ex. IE11 & Safari 12-), incluez ce polyfill dans votre build.
-
Si vous prenez en charge des navigateurs sans Intl.RelativeTimeFormat (ex. IE11, Edge, Safari 13-), incluez ce polyfill dans votre build ainsi que les données CLDR individuelles pour chaque langue prise en charge.
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
Lorsque vous utilisez React Intl dans Node.js, votre binaire node doit soit :
- Être compilé avec
full-icuen suivant ces instructions
OU
- Utiliser le package npm
full-icu
Prise en charge de TypeScript
react-intl a été réécrit en TypeScript et bénéficie donc d'une prise en charge native. Nous avons également supprimé la dépendance prop-types et exposons désormais IntlShape sous forme d'interface.
Tous les types doivent être accessibles depuis le fichier index racine sans import depuis des sous-fichiers spécifiques. Par exemple :
import {IntlShape} from 'react-intl' // Correct
import {IntlShape} from 'react-intl/lib/types' // Incorrect
Si une interface manque au niveau racine, merci de nous le signaler et/ou soumettre une PR serait grandement apprécié :)
Vous devrez peut-être modifier votre code si vous utilisiez le package désormais déprécié @types/react-intl. L'exemple le plus courant est InjectedIntlProps qui doit être remplacé par WrappedComponentProps.
FormattedRelativeTime
Lors de l'introduction de FormattedRelative, la spécification Intl.RelativeTimeFormat était encore instable. Elle a maintenant atteint le stade 3 et plusieurs navigateurs l'implémentent. Son API différant de FormattedRelative, nous l'avons ajustée pour respecter la spécification, ce qui la rend non rétrocompatible.
- Toutes les
units(ex.day-short) deviennent une combinaison deunitetstyle:
<FormattedRelative units="second-short"/>
// will be
<FormattedRelativeTime unit="second" style="short"/>
styledevientnumeric(valeur par défaut) :
<FormattedRelative style="numeric"/>
// will be
<FormattedRelativeTime />
<FormattedRelative style="best fit"/>
// will be
<FormattedRelativeTime numeric="auto"/>
- Le type de
valuen'est plusDatemais undeltadans l'unitspécifiée :
<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" />
updateIntervaldevientupdateIntervalInSecondset n'accepte que le delta temporel en secondes. Le comportement de mise à jour reste identique, ex :
<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`
initialNowa été supprimé.
De même, la contrepartie fonctionnelle formatRelative a été renommée formatRelativeTime et ses paramètres ont été adaptés pour refléter les props de ce composant.
- Implémentation du comportement
FormattedRelative
Vous pouvez utiliser @formatjs/intl-utils pour reproduire l'ancien comportement ainsi :
import {selectUnit} from '@formatjs/intl-utils'
const {value, unit} = selectUnit(Date.now() - 48 * 3600 * 1000)
// render
;<FormattedRelativeTime value={value} unit={unit} />
Formatage de texte enrichi amélioré pour FormattedMessage et formatMessage
Dans la v2, le formatage de texte enrichi (intégration d'un ReactElement) nécessitait :
<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>,
}}
/>
Désormais :
<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>,
}}
/>
Ce changement résout plusieurs problèmes :
-
Perte d'informations contextuelles lors du style : dans l'exemple ci-dessus,
linkest une boîte noire pour le traducteur (personne, animal, horodatage). Transmettre le contexte viadescriptionetplaceholders'avère souvent insuffisant pour des variables complexes. -
Harmonisation avec d'autres bibliothèques comme fluent de Mozilla (via les Overlays).
Dans les cas où vous passiez précédemment un ReactElement comme placeholder, nous recommandons vivement de repenser la structure pour déclarer autant de texte que possible :
Avant
<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>
}}
/>
Après
<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 et ses bibliothèques sous-jacentes (intl-messageformat-parser, intl-messageformat, @formatjs/intl-relativetimeformat, intl-format-cache, intl-utils) exportent désormais des artefacts ESM. Vous devez donc configurer votre chaîne de build pour transpiler ces bibliothèques.
Jest
Ajoutez transformIgnorePatterns pour inclure systématiquement ces bibliothèques, par exemple :
{
transformIgnorePatterns: [
'/node_modules/(?!intl-messageformat|intl-messageformat-parser).+\\.js$',
],
}
webpack
Si vous utilisez babel-loader, ajoutez ces bibliothèques dans include, par exemple :
include: [
path.join(__dirname, "node_modules/react-intl"),
path.join(__dirname, "node_modules/intl-messageformat"),
path.join(__dirname, "node_modules/intl-messageformat-parser"),
],
Créer intl sans utiliser Provider
Nous avons ajouté une nouvelle API appelée createIntl qui permet de créer un objet IntlShape sans utiliser Provider. Cela vous permet de formater des éléments en dehors du cycle de vie React tout en réutilisant le même objet intl. Exemple :
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>
Ceci est particulièrement bénéfique en SSR où vous pouvez réutiliser le même objet intl entre les requêtes.
Changements de syntaxe du format de message
Nous avons réécrit notre parser pour mieux respecter le format ICU Message, notamment pour supporter potentiellement les squelettes. Les changements rétro-incompatibles sont :
Le caractère d'échappement est désormais l'apostrophe (').
Précédemment, nous utilisions le backslash (\) comme caractère d'échappement, ce qui posait des problèmes de compatibilité avec les outils de traduction stricts supportant d'autres implémentations comme ICU4J/ICU4C. Grâce à @pyrocat101, ce comportement est désormais conforme à la spécification. Cela signifie :
// Before
<FormattedMessage defaultMessage="\\{foo\\}" /> //prints out "{foo}"
// After
<FormattedMessage defaultMessage="'{foo}'" /> //prints out "{foo}"
Nous recommandons vivement de consulter la spécification sur les mécanismes de citation/échappement ici dans la section Quoting/Escaping.
Changement de syntaxe des arguments de placeholder
Les arguments de placeholder ne peuvent plus contenir de - (ex : this is a {placeholder-var} est invalide mais this is a {placeholder_var} est valide).
Tests
Nous avons supprimé IntlProvider.getChildContext pour les tests. Utilisez désormais createIntl pour créer un objet intl autonome en dehors de React à des fins de test. Voir Testing with React Intl pour plus de détails.