Vai al contenuto principale

API Imperativa

Traduzione Beta Non Ufficiale

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-label e 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.

suggerimento

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 tuo IntlProvider, puoi accedere all'oggetto intl richiamando questo hook nei componenti funzionali React

  • HOC injectIntl: Nei componenti React basati su class, puoi avvolgerli con l'HOC injectIntl e intl sarà disponibile come prop

  • createIntl: In ambienti non React (Node, Vue, Angular, testing... e chi più ne ha più ne metta), puoi creare direttamente un oggetto intl chiamando questa funzione con la stessa configurazione di IntlProvider

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.

Live Editor
intl.formatDate(Date.now(), {
  year: 'numeric',
  month: 'numeric',
  day: 'numeric',
})
Result

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.

Live Editor
intl.formatTime(Date.now()) /* "4:03 PM" */
Result

formatDateTimeRange

supporto browser

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.

Live Editor
intl.formatDateTimeRange(new Date('2020-1-1'), new Date('2020-1-15'))
Result

formatRelativeTime

Supporto browser

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.

Live Editor
intl.formatRelativeTime(0)
Result
Live Editor
intl.formatRelativeTime(-24, 'hour', {style: 'narrow'})
Result

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.

Live Editor
intl.formatNumber(1000, {style: 'currency', currency: 'USD'})
Result

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:

Live Editor
intl.formatNumber(1000, {
  style: 'unit',
  unit: 'kilobyte',
  unitDisplay: 'narrow',
})
Result
Live Editor
intl.formatNumber(1000, {
  unit: 'fahrenheit',
  unitDisplay: 'long',
  style: 'unit',
})
Result

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.

Live Editor
intl.formatPlural(1)
Result
Live Editor
intl.formatPlural(3, {style: 'ordinal'})
Result
Live Editor
intl.formatPlural(4, {style: 'ordinal'})
Result
supporto multilingua

Questa funzione dovrebbe essere usata solo in app che supportano una singola lingua. Per app multilingua usa invece formatMessage.

formatList

supporto browser

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:

Live Editor
intl.formatList(['Me', 'myself', 'I'], {type: 'conjunction'})
Result
Live Editor
intl.formatList(['5 hours', '3 minutes'], {type: 'unit'})
Result

formatDisplayName

supporto browser

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:

Live Editor
intl.formatDisplayName('zh-Hans-SG', {type: 'language'})
Result
Live Editor
// ISO-15924 four letters script code to localized display name
intl.formatDisplayName('Deva', {type: 'script'})
Result
Live Editor
// ISO-4217 currency code to localized display name
intl.formatDisplayName('CNY', {type: 'currency'})
Result
Live Editor
// ISO-3166 two letters region code to localized display name
intl.formatDisplayName('UN', {type: 'region'})
Result

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
}
Estrazione Message Descriptor

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:

  1. Cerca e formatta il messaggio tradotto corrispondente all'id passato a <IntlProvider>

  2. Fallback alla formattazione del defaultMessage.

  3. Fallback al sorgente del messaggio tradotto per l'id

  4. Fallback al sorgente del defaultMessage

  5. Fallback all'id letterale 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.

Live Editor
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'})
}
Result

con ReactElement

Live Editor
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>})
}
Result

con formattazione rich text

Live Editor
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>,
  })
}
Result

Il messaggio è definito con defineMessages per supportare l'estrazione via babel-plugin-formatjs, ma non è obbligatorio senza il plugin Babel.

messaggio semplice

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',
})