API Imperativa
Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →
React Intl fornisce e si basa su diversi livelli di API. Durante l'utilizzo interagirai sia con la sua API (documentata qui) che con i suoi componenti React.
Perché un'API Imperativa?
Sebbene i nostri componenti offrano un'integrazione senza soluzione di continuità con React, l'API imperativa è consigliata (a volte necessaria) in diversi scenari:
-
Impostazione di attributi testuali come
title,aria-labele simili dove non è possibile utilizzare componenti React (es.<img title/>) -
Formattazione di testo/datetime... in ambienti non React come Node, API server, store Redux, testing...
-
Scenari ad alte prestazioni dove il numero di componenti React renderizzati diventa un collo di bottiglia (es. rendering di portafogli azionari, tabelle virtuali con molte celle...)
L'oggetto intl
Il cuore di react-intl è l'oggetto intl (di tipo IntlShape), che funge da contenitore per la cache di tutte le API Intl.*, configurazioni, messaggi compilati e simili. Il ciclo di vita dell'oggetto intl è tipicamente legato alla locale e alla lista di messages contenuti, quindi quando cambi locale l'oggetto dovrebbe essere ricreato.
Per motivi di prestazioni, l'oggetto intl dovrebbe essere riutilizzato il più possibile.
Esistono diversi modi per accedere all'oggetto intl:
-
Hook
useIntl: Dopo aver dichiarato il tuoIntlProvider, puoi accedere all'oggettointlrichiamando questo hook nei componenti funzionali React -
HOC
injectIntl: Nei componenti React basati suclass, puoi avvolgerli con l'HOCinjectIntleintlsarà disponibile comeprop -
createIntl: In ambienti non React (Node, Vue, Angular, testing... e chi più ne ha più ne metta), puoi creare direttamente un oggettointlchiamando questa funzione con la stessa configurazione diIntlProvider
Hook useIntl
Quando un componente può essere espresso come funzione, l'hook useIntl risulta particolarmente pratico. Questo hook useIntl non richiede alcuna opzione come argomento. Ecco un tipico utilizzo:
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
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.
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
}
Questa funzione, esportata dal pacchetto react-intl, è una factory per Higher-Order Components (HOC). Avvolge il componente React passato con un altro componente che fornisce l'API di formattazione imperativa tramite le sue props. (Simile al pattern connect-to-stores delle implementazioni Flux)
Per impostazione predefinita, l'API di formattazione è accessibile tramite props.intl, ma è possibile personalizzare il nome della prop con options.intlPropName. Il valore sarà di tipo IntlShape, definito nella sezione successiva.
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
Questo metodo consente di creare un oggetto IntlShape senza utilizzare Provider, permettendo la formattazione al di fuori del ciclo di vita 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>
createIntlCache
Crea un'istanza di cache da utilizzare globalmente tra le varie localizzazioni. Memorizza i costruttori Intl.* creati in precedenza per migliorare le prestazioni ed è esclusivamente una cache in memoria.
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
Questa interfaccia, esportata da react-intl, può essere utilizzata con la factory function injectIntl.
La definizione sopra mostra la struttura dell'oggetto props.intl iniettato nel componente tramite injectIntl, composto da due parti:
-
IntlConfig: I metadati intl passati come props nel componente genitore<IntlProvider>. -
IntlFormatters: L'API imperativa di formattazione descritta di seguito.
locale, formats e messages
La localizzazione corrente dell'utente e quella in cui dovrebbe essere renderizzata l'applicazione. defaultLocale e defaultFormats servono come fallback o durante lo sviluppo e rappresentano le impostazioni predefinite dell'app. Notare l'assenza di defaultMessages: ciò avviene perché ogni Message Descriptor fornisce un proprio defaultMessage.
defaultLocale e defaultFormats
Localizzazione e formati predefiniti da usare quando un messaggio non è tradotto (assente da messages). defaultLocale dovrebbe corrispondere alla localizzazione in cui sono dichiarati i defaultMessage per garantire coerenza testuale. Senza defaultLocale o se impostata in modo errato, potresti trovarti in scenari dove una frase è in inglese ma date/ore incorporate sono in spagnolo.
textComponent
Configura il wrapper predefinito per i componenti <Formatted*>. Se non specificato, viene usato <React.Fragment>. Prima della V3 si utilizzava span; consultare la migration guide per dettagli.
onError
Consente all'utente di fornire un gestore di errori personalizzato. Per impostazione predefinita, gli errori vengono registrati tramite console.error se NODE_ENV non è impostato su production.
wrapRichTextChunksInFragment
Nella formattazione di messaggi rich text, l'output è di tipo Array<string | React.ReactElement>, che causerebbe errori di chiave. Questa opzione avvolge l'output in un singolo React.Fragment per evitare il problema.
defaultRichTextElements
Una mappa che associa tag a funzioni di formattazione per rich text. Offre un modo centralizzato per formattare tag comuni come <b>, <p>... o per applicare sistemi di design specifici nel codice (es. <a> o <button> standardizzati). Vedi https://github.com/formatjs/formatjs/issues/1752 per maggiori dettagli.
formatDate
function formatDate(
value: number | Date | string,
options?: Intl.DateTimeFormatOptions & {format?: string}
): string
Questa funzione restituisce una stringa di data formattata. Si aspetta un value parsabile come data (es. isFinite(new Date(value))) e accetta options conformi a DateTimeFormatOptions.
intl.formatDate(Date.now(), { year: 'numeric', month: 'numeric', day: 'numeric', })
formatTime
function formatTime(
value: number | Date | string,
options?: Intl.DateTimeFormatOptions & {format?: string}
): string
Questa funzione restituisce una stringa di data formattata, ma differisce da formatDate per le seguenti opzioni predefinite:
{
hour: 'numeric',
minute: 'numeric',
}
Si aspetta un value parsabile come data (es. isFinite(new Date(value))) e accetta options conformi a DateTimeFormatOptions.
intl.formatTime(Date.now()) /* "4:03 PM" */
formatDateTimeRange
Richiede Intl.DateTimeFormat.prototype.formatRange con supporto browser limitato. Utilizza il nostro polyfill se intendi supportarli.
function formatDateTimeRange(
from: number | Date | string,
to: number | Date | string,
options?: Intl.DateTimeFormatOptions & {format?: string}
): string
Questa funzione restituisce una stringa formattata di intervallo data/ora. Sia from che to devono essere valori parsabili come date (es. isFinite(new Date(value))).
Si aspetta 2 valori (una data from e una data to) e accetta options conformi a DateTimeFormatOptions.
intl.formatDateTimeRange(new Date('2020-1-1'), new Date('2020-1-15'))
formatRelativeTime
Richiede Intl.RelativeTimeFormat con supporto browser limitato. Utilizza il nostro polyfill per aggiungere supporto.
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
Questa funzione restituisce una stringa formattata di tempo relativo (es. "1 ora fa"). Si aspetta un value numerico, un unit e options conformi a Intl.RelativeTimeFormatOptions.
intl.formatRelativeTime(0)
intl.formatRelativeTime(-24, 'hour', {style: 'narrow'})
formatNumber
Questa funzione utilizza le opzioni di Intl.NumberFormat.
function formatNumber(
value: number,
options?: Intl.NumberFormatOptions & {format?: string}
): string
Questa funzione restituisce una stringa numerica formattata. Si aspetta un value parsabile come numero e accetta options conformi a NumberFormatOptions.
intl.formatNumber(1000, {style: 'currency', currency: 'USD'})
Formattazione numeri usando unit
Attualmente fa parte dello standard ES2020 NumberFormat.
Abbiamo fornito un polyfill qui e i tipi di react-intl consentono di passare
una unità supportata:
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'
Questa funzione restituisce una categoria plurale: "zero", "one", "two", "few", "many" o "other". Si aspetta un value parsabile come numero e accetta options conformi a PluralFormatOptions.
È un'utility di basso livello il cui output può essere fornito a uno statement switch per selezionare una stringa specifica da visualizzare.
intl.formatPlural(1)
intl.formatPlural(3, {style: 'ordinal'})
intl.formatPlural(4, {style: 'ordinal'})
Questa funzione dovrebbe essere usata solo in app che supportano una singola lingua. Per app multilingua usa invece formatMessage.
formatList
Richiede Intl.ListFormat con supporto browser limitato. Utilizzare il nostro polyfill se necessario.
type ListFormatOptions = {
type?: 'disjunction' | 'conjunction' | 'unit'
style?: 'long' | 'short' | 'narrow'
}
function formatList(
elements: (string | React.ReactNode)[],
options?: Intl.ListFormatOptions
): string | React.ReactNode[]
Questa funzione consente di unire elementi in modo sicuro per l'i18n. Ad esempio, con localizzazione en:
intl.formatList(['Me', 'myself', 'I'], {type: 'conjunction'})
intl.formatList(['5 hours', '3 minutes'], {type: 'unit'})
formatDisplayName
Richiede Intl.DisplayNames con supporto browser limitato. Utilizzare il nostro polyfill se necessario.
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
Esempi di utilizzo:
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
Sintassi dei Messaggi
La formattazione di stringhe/messaggi è funzionalità fondamentale di React Intl, basata sulla ICU Message Formatting tramite la Sintassi Messaggi ICU. Questa sintassi consente di definire messaggi semplici o complessi, tradurli e formattarli a runtime.
Messaggio semplice:
Hello, {name}
Messaggio complesso:
Hello, {name}, you have {itemCount, plural,
=0 {no items}
one {# item}
other {# items}
}.
Vedi: Guida alla sintassi dei messaggi.
Message Descriptor
React Intl introduce il concetto di Message Descriptor per definire i messaggi predefiniti dell'applicazione, passato a formatMessage. I descrittori contengono tutti i dati necessari per la traduzione e includono queste proprietà:
-
id: Identificatore univoco e stabile -
description: Contesto per i traduttori sull'uso nell'interfaccia -
defaultMessage: Messaggio predefinito (tipicamente in inglese)
type MessageDescriptor = {
id: string
defaultMessage?: string
description?: string | object
}
Puoi estrarre messaggi dichiarati inline dai file sorgente usando la nostra CLI.
Fallback nella formattazione messaggi
Le API di formattazione dei messaggi vanno oltre per fornire fallback nelle situazioni comuni in cui la formattazione fallisce; come minimo, dovrebbe sempre essere restituita una stringa non vuota. Ecco l'algoritmo di fallback per la formattazione dei messaggi:
-
Cerca e formatta il messaggio tradotto corrispondente all'
idpassato a<IntlProvider> -
Fallback alla formattazione del
defaultMessage. -
Fallback al sorgente del messaggio tradotto per l'
id -
Fallback al sorgente del
defaultMessage -
Fallback all'
idletterale del messaggio.
Per "sorgente" s'intende il template originale senza sostituzioni.
Utilizzo
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[]
Questa funzione restituisce una stringa di messaggio formattato. Richiede un MessageDescriptor con almeno la proprietà id e accetta un oggetto values per riempire i segnaposto.
Se un messaggio tradotto con l'id è stato passato a <IntlProvider> tramite messages, verrà formattato. Altrimenti si usa il defaultMessage. Vedi Fallback per la Formattazione Messaggi per dettagli.
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'}) }
con 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>}) }
con formattazione rich text
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>, }) }
Il messaggio è definito con defineMessages per supportare l'estrazione via babel-plugin-formatjs, ma non è obbligatorio senza il plugin Babel.
I messaggi possono essere stringhe semplici senza segnaposto, il tipo più comune.
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
Queste funzioni sono esportate da react-intl e fungono da hook per la nostra CLI e i plugin Babel/TS durante la compilazione dei messaggi predefiniti nei file JavaScript. Restituiscono semplicemente l'oggetto Message Descriptor passato.
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',
})