API Impérative
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 →
React Intl repose sur plusieurs couches d'API. Lors de son utilisation, vous interagirez avec son API impérative (documentée ici) et ses composants React.
Pourquoi une API Impérative ?
Bien que nos composants offrent une intégration transparente avec React, l'API impérative est recommandée (parfois indispensable) dans plusieurs cas d'usage :
-
Définir des attributs textuels comme
title,aria-labeldans des contextes où un composant React ne peut être utilisé (ex:<img title/>) -
Formater du texte/dates-heures... dans des environnements non-React comme Node, API serveur, store Redux, tests...
-
Sc énarios haute performance où le nombre de composants React devient un goulot d'étranglement (ex: rendu de portefeuilles boursiers, tables virtuelles avec nombreuses cellules...)
L'objet intl
Le cœur de react-intl est l'objet intl (de type IntlShape), qui stocke le cache des APIs Intl.*, configurations, messages compilés, etc. Le cycle de vie de l'objet intl est lié à la locale et à la liste de messages qu'il contient, ce qui signifie que lorsque vous changez de locale, cet objet doit être recréé.
L'objet intl doit être réutilisé autant que possible pour des raisons de performance.
Plusieurs méthodes permettent d'accéder à l'objet intl :
-
Hook
useIntl: Après avoir déclaré votreIntlProvider, accédez àintlen appelant ce hook dans vos composants fonctionnels React -
HOC
injectIntl: Pour les composants React basés sur desclass, encapsulez-les avec le HOCinjectIntlpour accéder àintlvia uneprop -
createIntl: Dans des environnements non-React (Node, Vue, Angular, tests...), créez directement un objetintlavec la même configuration queIntlProvider
Le hook useIntl
Pour les composants fonctionnels, le hook useIntl est pratique. Ce hook useIntl ne requiert aucun argument lors de son appel. Voici un exemple d'utilisation typique :
import React from 'react'
import {useIntl, FormattedDate} from 'react-intl'
const FunctionComponent: React.FC<{date: number | Date}> = ({date}) => {
const intl = useIntl()
return (
<span title={intl.formatDate(date)}>
<FormattedDate value={date} />
</span>
)
}
export default FunctionComponent
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.
Le HOC injectIntl
type WrappedComponentProps<IntlPropName extends string = 'intl'> = {
[k in IntlPropName]: IntlShape
}
type WithIntlProps<P> = Omit<P, keyof WrappedComponentProps> & {
forwardedRef?: React.Ref<any>
}
function injectIntl<
IntlPropName extends string = 'intl',
P extends WrappedComponentProps<IntlPropName> = WrappedComponentProps<any>,
>(
WrappedComponent: React.ComponentType<P>,
options?: Opts<IntlPropName>
): React.ComponentType<WithIntlProps<P>> & {
WrappedComponent: typeof WrappedComponent
}
Exporté par react-intl, ce composant d'ordre supérieur (HOC) encapsule vos composants React pour injecter l'API de formatage via leurs props. (Similaire au pattern connect-to-stores des implémentations Flux.)
Par défaut, l'API est injectée via props.intl, mais ce nom peut être modifié via options.intlPropName. La prop sera de type IntlShape, défini dans la section suivante.
import React from 'react'
import {injectIntl, FormattedDate} from 'react-intl'
interface Props {
date: Date | number
}
const FunctionalComponent: React.FC<Props> = props => {
const {
date,
intl, // Injected by `injectIntl`
} = props
return (
<span title={intl.formatDate(date)}>
<FormattedDate value={date} />
</span>
)
}
export default injectIntl(FunctionalComponent)
createIntl
Permet de créer un objet IntlShape sans Provider, pour formater 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>
createIntlCache
Crée une instance de cache utilisable globalement pour toutes les locales. Cela mémorise les constructeurs Intl.* précédemment créés pour des performances optimales et ne constitue qu'un cache en mémoire.
IntlShape
interface IntlConfig {
locale: string
timeZone?: string
formats: CustomFormats
textComponent?: React.ComponentType | keyof React.JSX.IntrinsicElements
messages: Record<string, string> | Record<string, MessageFormatElement[]>
defaultLocale: string
defaultFormats: CustomFormats
onError(err: string): void
onWarn(warning: string): void
}
interface IntlFormatters {
formatDate(value: number | Date | string, opts?: FormatDateOptions): string
formatTime(value: number | Date | string, opts?: FormatDateOptions): string
formatDateToParts(
value: number | Date | string,
opts?: FormatDateOptions
): Intl.DateTimeFormatPart[]
formatTimeToParts(
value: number | Date | string,
opts?: FormatDateOptions
): Intl.DateTimeFormatPart[]
formatRelativeTime(
value: number,
unit?: FormattableUnit,
opts?: FormatRelativeTimeOptions
): string
formatNumber(value: number, opts?: FormatNumberOptions): string
formatNumberToParts(
value: number,
opts?: FormatNumberOptions
): Intl.NumberFormatPart[]
formatPlural(
value: number | string,
opts?: FormatPluralOptions
): ReturnType<Intl.PluralRules['select']>
formatMessage(
descriptor: MessageDescriptor,
values?: Record<string, PrimitiveType | FormatXMLElementFn<string, string>>
): string
formatMessage(
descriptor: MessageDescriptor,
values?: Record<string, PrimitiveType | T | FormatXMLElementFn<T, R>>
): R
formatList(values: Iterable<string>, opts?: FormatListOptions): string
formatList(
values: Iterable<string | T>,
opts?: FormatListOptions
): T | string | Array<string | T>
formatListToParts(
values: Iterable<string | T>,
opts?: FormatListOptions
): Part[]
formatDisplayName(
value: string,
opts?: FormatDisplayNameOptions
): string | undefined
}
type IntlShape = IntlConfig & IntlFormatters
Cette interface, exportée par react-intl, est utilisée avec la fabrique de HOC injectIntl.
La définition ci-dessus montre la structure de props.intl injecté via injectIntl, composé de deux parties :
-
IntlConfig: Les métadonnées intl passées comme props au parent<IntlProvider>. -
IntlFormatters: L'API de formatage impératif décrite ci-dessous.
locale, formats et messages
La locale actuelle de l'utilisateur et celle dans laquelle l'application doit être rendue. Tandis que defaultLocale et defaultFormats servent de fallbacks ou durant le développement pour représenter les valeurs par défaut de l'application. Notez l'absence de defaultMessages car chaque Message Descriptor fournit déjà un defaultMessage.
defaultLocale et defaultFormats
Locale et formats par défaut utilisés lorsqu'un message n'est pas traduit (absent de messages). defaultLocale doit correspondre à la locale dans laquelle les defaultMessage sont déclarés pour garantir la cohérence des phrases dans une seule locale. Sans defaultLocale ou si celui-ci est mal configuré, vous pourriez obtenir des phrases en anglais avec des dates/heures intégrées en espagnol.
textComponent
Configure le wrapper par défaut des composants <Formatted*> de React Intl. Par défaut, <React.Fragment> est utilisé. Avant la version 3, span était employé ; consultez le guide de migration pour plus de détails.
onError
Permet à l'utilisateur de fournir un gestionnaire d'erreurs personnalisé. Par défaut, les erreurs sont journalisées via console.error si NODE_ENV n'est pas défini sur production.
wrapRichTextChunksInFragment
Lors du formatage de texte enrichi, la sortie est de type Array<string | React.ReactElement>, ce qui génère une erreur de clé. Cette option englobe le résultat dans un React.Fragment unique pour résoudre ce problème.
defaultRichTextElements
Une correspondance entre balises et fonctions de formatage de texte enrichi. Conçu pour centraliser le formatage de balises courantes comme <b>, <p>... ou imposer un Design System spécifique dans la base de code (ex: <a> ou <button> standardisés). Voir https://github.com/formatjs/formatjs/issues/1752 pour plus de contexte.
formatDate
function formatDate(
value: number | Date | string,
options?: Intl.DateTimeFormatOptions & {format?: string}
): string
Cette fonction retourne une chaîne de caractères représentant une date formatée. Elle attend une value interprétable comme date (c'est-à-dire isFinite(new Date(value))), et accepte des options conformes à DateTimeFormatOptions.
intl.formatDate(Date.now(), { year: 'numeric', month: 'numeric', day: 'numeric', })
formatTime
function formatTime(
value: number | Date | string,
options?: Intl.DateTimeFormatOptions & {format?: string}
): string
Cette fonction retourne également une chaîne de caractères de date formatée, mais diffère de formatDate par ses options par défaut :
{
hour: 'numeric',
minute: 'numeric',
}
Elle attend une value interprétable comme date (c'est-à-dire isFinite(new Date(value))), et accepte des options conformes à DateTimeFormatOptions.
intl.formatTime(Date.now()) /* "4:03 PM" */
formatDateTimeRange
Cela nécessite Intl.DateTimeFormat.prototype.formatRange qui a un support navigateur limité. Utilisez notre polyfill si vous prévoyez de les supporter.
function formatDateTimeRange(
from: number | Date | string,
to: number | Date | string,
options?: Intl.DateTimeFormatOptions & {format?: string}
): string
Cette fonction retourne une chaîne de caractères représentant une plage de dates/heures formatée. from et to doivent être des valeurs interprétables comme dates (c'est-à-dire isFinite(new Date(value))).
Elle attend 2 valeurs (une date from et une date to) et accepte des options conformes à DateTimeFormatOptions.
intl.formatDateTimeRange(new Date('2020-1-1'), new Date('2020-1-15'))
formatRelativeTime
Cette fonctionnalité nécessite Intl.RelativeTimeFormat qui a un support navigateur limité. Utilisez notre polyfill si vous devez la supporter.
type Unit =
| 'second'
| 'minute'
| 'hour'
| 'day'
| 'week'
| 'month'
| 'quarter'
| 'year'
type RelativeTimeFormatOptions = {
numeric?: 'always' | 'auto'
style?: 'long' | 'short' | 'narrow'
}
function formatRelativeTime(
value: number,
unit: Unit,
options?: Intl.RelativeTimeFormatOptions & {
format?: string
}
): string
Cette fonction retourne une chaîne de temps relatif formatée (ex : "il y a 1 heure"). Elle attend une value numérique, une unit et des options conformes à Intl.RelativeTimeFormatOptions.
intl.formatRelativeTime(0)
intl.formatRelativeTime(-24, 'hour', {style: 'narrow'})
formatNumber
Cette fonction utilise les options d'Intl.NumberFormat.
function formatNumber(
value: number,
options?: Intl.NumberFormatOptions & {format?: string}
): string
Cette fonction retourne une chaîne de nombre formatée. Elle attend une value interprétable comme nombre, et accepte des options conformes à NumberFormatOptions.
intl.formatNumber(1000, {style: 'currency', currency: 'USD'})
Formatage numérique avec unit
Actuellement, cette fonctionnalité fait partie d'ES2020 NumberFormat.
Nous fournissons un polyfill ici et les types de react-intl permettent d'utiliser
une unité sanctionnée :
intl.formatNumber(1000, { style: 'unit', unit: 'kilobyte', unitDisplay: 'narrow', })
intl.formatNumber(1000, { unit: 'fahrenheit', unitDisplay: 'long', style: 'unit', })
formatPlural
type PluralFormatOptions = {
type?: 'cardinal' | 'ordinal' = 'cardinal'
}
function formatPlural(
value: number,
options?: Intl.PluralFormatOptions
): 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'
Cette fonction retourne une catégorie de pluriel : "zero", "one", "two", "few", "many", ou "other". Elle attend une value interprétable comme nombre, et accepte des options conformes à PluralFormatOptions.
C'est une utilitaire bas niveau dont le résultat peut être utilisé dans une instruction switch pour sélectionner une chaîne spécifique à afficher.
intl.formatPlural(1)
intl.formatPlural(3, {style: 'ordinal'})
intl.formatPlural(4, {style: 'ordinal'})
Cette fonction ne doit être utilisée que pour des applications supportant une seule langue. Pour des applications multilingues, utilisez plutôt formatMessage.
formatList
Cette fonctionnalité nécessite Intl.ListFormat dont le support navigateur est limité. Utilisez notre polyfill si vous prévoyez de les prendre en charge.
type ListFormatOptions = {
type?: 'disjunction' | 'conjunction' | 'unit'
style?: 'long' | 'short' | 'narrow'
}
function formatList(
elements: (string | React.ReactNode)[],
options?: Intl.ListFormatOptions
): string | React.ReactNode[]
Cette fonction permet de joindre des listes d'éléments de manière compatible i18n. Par exemple, lorsque la locale est en :
intl.formatList(['Me', 'myself', 'I'], {type: 'conjunction'})
intl.formatList(['5 hours', '3 minutes'], {type: 'unit'})
formatDisplayName
Cette fonctionnalité nécessite Intl.DisplayNames dont le support navigateur est limité. Utilisez notre polyfill si vous prévoyez de les prendre en charge.
type FormatDisplayNameOptions = {
style?: 'narrow' | 'short' | 'long'
type?: 'language' | 'region' | 'script' | 'currency'
fallback?: 'code' | 'none'
}
function formatDisplayName(
value: string | number | Record<string, unknown>,
options?: FormatDisplayNameOptions
): string | undefined
Exemples d'utilisation :
intl.formatDisplayName('zh-Hans-SG', {type: 'language'})
// ISO-15924 four letters script code to localized display name intl.formatDisplayName('Deva', {type: 'script'})
// ISO-4217 currency code to localized display name intl.formatDisplayName('CNY', {type: 'currency'})
// ISO-3166 two letters region code to localized display name intl.formatDisplayName('UN', {type: 'region'})
formatMessage
Syntaxe des messages
Le formatage des chaînes/messages est une fonctionnalité essentielle de React Intl, basée sur le formatage de messages ICU via la syntaxe ICU. Cette syntaxe permet de définir des messages simples à complexes, de les traduire, puis de les formater à l'exécution.
Message simple :
Hello, {name}
Message complexe :
Hello, {name}, you have {itemCount, plural,
=0 {no items}
one {# item}
other {# items}
}.
Voir : Le guide de syntaxe des messages.
Message Descriptor
React Intl utilise le concept de Descripteur de message pour définir les messages/chaînes par défaut de votre application, qui est passé à formatMessage. Les Descripteurs de message sont optimaux pour fournir les données nécessaires à la traduction des chaînes/messages et contiennent les propriétés suivantes :
-
id: Identifiant unique et stable pour le message -
description: Contexte pour le traducteur sur l'utilisation dans l'UI -
defaultMessage: Le message par défaut (probablement en anglais)
type MessageDescriptor = {
id: string
defaultMessage?: string
description?: string | object
}
Vous pouvez extraire les messages déclarés en ligne des fichiers sources en utilisant notre CLI.
Fallbacks de formatage des messages
Les API de formatage de messages vont au-delà pour fournir des solutions de repli dans les situations courantes où le formatage échoue ; au minimum, une chaîne non vide devrait toujours être retournée. Voici l'algorithme de repli pour le formatage des messages :
-
Rechercher et formater le message traduit à l'
id, passé à<IntlProvider>. -
Retomber sur le formatage du
defaultMessage. -
Retomber sur la source du message traduit à l'
id. -
Retomber sur la source du
defaultMessage. -
Retomber sur l'
idlittéral du message.
Ci-dessus, "source" fait référence à l'utilisation du modèle tel quel, sans aucune substitution.
Utilisation
type MessageFormatPrimitiveValue = string | number | boolean | null | undefined
function formatMessage(
descriptor: MessageDescriptor,
values?: Record<string, MessageFormatPrimitiveValue>
): string
function formatMessage(
descriptor: MessageDescriptor,
values?: Record<
string,
MessageFormatPrimitiveValue | React.ReactElement | FormatXMLElementFn
>
): string | React.ReactNode[]
Cette fonction retourne une chaîne de message formatée. Elle attend un MessageDescriptor avec au moins une propriété id, et accepte un objet values peu profond qui est utilisé pour remplir les espaces réservés dans le message.
Si un message traduit avec l'id a été passé au <IntlProvider> via sa prop messages, il sera formaté ; sinon, il retombera sur le formatage du defaultMessage. Voir : Fallbacks de formatage de message pour plus de détails.
function () { const messages = defineMessages({ greeting: { id: 'app.greeting', defaultMessage: 'Hello, {name}!', description: 'Greeting to welcome the user to the app', }, }) return intl.formatMessage(messages.greeting, {name: 'Eric'}) }
avec ReactElement
function () { const messages = defineMessages({ greeting: { id: 'app.greeting', defaultMessage: 'Hello, {name}!', description: 'Greeting to welcome the user to the app', }, }) return intl.formatMessage(messages.greeting, {name: <b>Eric</b>}) }
avec formatage de texte enrichi
function () { const messages = defineMessages({ greeting: { id: 'app.greeting', defaultMessage: 'Hello, <bold>{name}</bold>!', description: 'Greeting to welcome the user to the app', }, }) return intl.formatMessage(messages.greeting, { name: 'Eric', bold: str => <b>{str}</b>, }) }
Le message que nous avons défini en utilisant defineMessages pour supporter l'extraction via babel-plugin-formatjs, mais ce n'est pas obligatoire si vous n'utilisez pas le plugin Babel.
Les messages peuvent être des chaînes simples sans espaces réservés, et c'est le type de message le plus courant.
defineMessages/defineMessage
interface MessageDescriptor {
id?: string
description?: string | object
defaultMessage?: string
}
function defineMessages(
messageDescriptors: Record<string, MessageDescriptor>
): Record<string, MessageDescriptor>
function defineMessage(messageDescriptor: MessageDescriptor): MessageDescriptor
Ces fonctions sont exportées par le package react-intl et constituent simplement un hook pour notre CLI et les plugins babel/TS à utiliser lors de la compilation des messages par défaut définis dans les fichiers source JavaScript. Cette fonction retourne simplement l'objet map de Descripteur de message qui lui est passé.
import {defineMessages, defineMessage} from 'react-intl'
const messages = defineMessages({
greeting: {
id: 'app.home.greeting',
description: 'Message to greet the user.',
defaultMessage: 'Hello, {name}!',
},
})
const msg = defineMessage({
id: 'single',
defaultMessage: 'single message',
description: 'header',
})