Guía de Actualización (v2 -> v3)
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Cambios importantes en la API
-
Se ha eliminado
addLocaleData. Consulta Migrar a las APIs nativas de Intl para más detalles. -
Se ha eliminado
ReactIntlLocaleData. Consulta Migrar a las APIs nativas de Intl para más detalles. -
Se ha eliminado
intlShape. Consulta Soporte para TypeScript para más detalles. -
Cambio del
textComponentpredeterminado enIntlProvideraReact.Fragment. Para mantener el comportamiento anterior, puedes establecer explícitamentetextComponentcomospan.
<IntlProvider textComponent="span" />
-
FormattedRelativese ha renombrado aFormattedRelativeTimey su API ha cambiado significativamente. Consulta FormattedRelativeTime para más detalles. -
formatRelativese ha renombrado aformatRelativeTimey su API ha cambiado significativamente. Consulta FormattedRelativeTime para más detalles. -
Cambios en la sintaxis de formato de mensajes. Consulta Cambios en la Sintaxis de Formato de Mensajes para más detalles.
-
IntlProviderya no hereda delIntlProvidersuperior.
Usa React 16.3 o superior
React Intl v3 es compatible con la nueva API de contexto, solucionando todo tipo de problemas de actualización del árbol 🎉
Además, aprovecha los nuevos métodos del ciclo de vida (y elimina los obsoletos).
También soporta el nuevo React.forwardRef(), permitiendo a los usuarios acceder directamente a las referencias usando la prop ref estándar (ver más abajo para más información).
Migrar from withRef to forwardRef
Con la actualización a React >= 16.3, obtuvimos la opción de usar la nueva función React.forwardRef() y por ello marcamos como obsoleta la opción withRef para el HOC injectIntl en favor de forwardRef.
Cuando forwardRef está establecido en true, ahora puedes simplemente ignorar que el HOC está presente.
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} />
}
}
Nuevo hook useIntl como alternativa al HOC injectIntl
Esta versión v3 también soporta la última API de hooks de React para usuarios con React >= 16.8. Ahora puedes usar el hook useIntl como alternativa al HOC injectIntl en componentes funcionales. Ambos métodos te permiten acceder a la instancia intl, aquí una comparación rápida:
// 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
}
Para mantener la superficie de la API limpia y simple, solo proporcionamos el hook useIntl en el paquete. Si lo prefieres, el usuario puede envolver este hook integrado para crear hooks personalizados como useFormatMessage fácilmente. Visita el sitio oficial de React para una introducción general a los hooks de React.
Migrar al uso de APIs nativas de Intl
React Intl v3 ya no incluye datos CLDR y en su lugar depende de la API nativa de Intl. Específicamente, las nuevas APIs que utilizamos son:
-
Intl.PluralRules: Puede aplicarse un polyfill usando este paquete.
-
Intl.RelativeTimeFormat: Puede aplicarse un polyfill usando este paquete.
Este cambio busca preparar React Intl para el futuro, ya que estas APIs son estables y se están implementando en navegadores modernos. Esto también significa que ya no empaquetamos ni consumimos CLDRs en este paquete.
Si anteriormente usabas addLocaleData para dar soporte a navegadores antiguos, te recomendamos lo siguiente:
-
Si das soporte a navegadores que no tienen Intl.PluralRules (ej. IE11 y Safari 12-), incluye este polyfill en tu build.
-
Si das soporte a navegadores que no tienen Intl.RelativeTimeFormat (ej. IE11, Edge, Safari 13-), incluye este polyfill en tu build junto con los datos CLDR individuales para cada locale que soportes.
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
Al usar React Intl en Node.js, tu binario de node debe:
- Compilarse con
full-icusiguiendo estas instrucciones
O
- Usar el paquete npm
full-icu
Soporte para TypeScript
react-intl ha sido reescrito en TypeScript y por lo tanto tiene soporte nativo para TypeScript. Por ello, también hemos eliminado la dependencia de prop-types y exponemos IntlShape como una interfaz en su lugar.
Todos los tipos deberían estar disponibles desde el archivo index de nivel superior sin necesidad de importar desde subarchivos específicos. Por ejemplo:
import {IntlShape} from 'react-intl' // Correct
import {IntlShape} from 'react-intl/lib/types' // Incorrect
Si falta soporte para alguna interfaz de nivel superior, por favor avísanos y/o enviar un PR sería muy apreciado :)
Es posible que necesites hacer algunos cambios en tu código si dependías del paquete @types/react-intl (ahora obsoleto). El ejemplo más común es InjectedIntlProps, que debe reemplazarse por WrappedComponentProps.
FormattedRelativeTime
Cuando introdujimos FormattedRelative, la especificación de Intl.RelativeTimeFormat aún era inestable. Ahora ha alcanzado la etapa 3 y múltiples navegadores la han implementado. Sin embargo, su API es diferente a la de FormattedRelative, así que hemos ajustado su API para que coincida con la especificación, lo que significa que no es compatible con versiones anteriores.
- Todas las
units(comoday-short) se convierten en una combinación deunitystyle:
<FormattedRelative units="second-short"/>
// will be
<FormattedRelativeTime unit="second" style="short"/>
stylese convierte ennumeric(que es el valor predeterminado):
<FormattedRelative style="numeric"/>
// will be
<FormattedRelativeTime />
<FormattedRelative style="best fit"/>
// will be
<FormattedRelativeTime numeric="auto"/>
- El tipo de
valueya no esDate, sinodeltaen launitespecificada:
<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" />
updateIntervalpasa a serupdateIntervalInSecondsy solo aceptará el delta de tiempo en segundos. El comportamiento de actualización sigue siendo el mismo, ej:
<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`
initialNowha sido eliminado.
De manera similar, la contraparte funcional de este componente, formatRelative, ha sido renombrada a formatRelativeTime y sus parámetros se han modificado para reflejar las props de este componente.
- Implementar el comportamiento de
FormattedRelative
Puedes usar @formatjs/intl-utils para lograr un comportamiento similar al anterior de esta manera:
import {selectUnit} from '@formatjs/intl-utils'
const {value, unit} = selectUnit(Date.now() - 48 * 3600 * 1000)
// render
;<FormattedRelativeTime value={value} unit={unit} />
Mejoras en el formato de rich text para FormattedMessage y formatMessage
En v2, para aplicar formato de rich text (incrustar un ReactElement), tenías que hacer esto:
<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>,
}}
/>
Ahora puedes hacer:
<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>,
}}
/>
Este cambio resuelve varios problemas:
-
Se perdía información contextual al aplicar estilos a parte del string: En el ejemplo anterior,
linkera efectivamente un marcador de posición de caja negra para un traductor. Podía ser una persona, un animal o una marca de tiempo. Transmitir información contextual mediantedescriptiony variablesplaceholdera menudo no era suficiente ya que la variable podía volverse suficientemente compleja. -
Esto iguala las funcionalidades con otras librerías de traducción, como fluent de Mozilla (usando Overlays).
Si anteriormente en casos donde pasabas un ReactElement como marcador de posición, recomendamos encarecidamente que reconsideres la estructura para declarar tanto texto como sea posible:
Antes
<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>
}}
/>
Despué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 y sus librerías subyacentes (intl-messageformat-parser, intl-messageformat, @formatjs/intl-relativetimeformat, intl-format-cache, intl-utils) exportan artefactos ESM. Esto significa que debes configurar tu cadena de herramientas de compilación para transpilar esas librerías.
Jest
Añade transformIgnorePatterns para incluir siempre esas librerías, ej:
{
transformIgnorePatterns: [
'/node_modules/(?!intl-messageformat|intl-messageformat-parser).+\\.js$',
],
}
webpack
Si usas babel-loader, añade esas librerías en include, ej:
include: [
path.join(__dirname, "node_modules/react-intl"),
path.join(__dirname, "node_modules/intl-messageformat"),
path.join(__dirname, "node_modules/intl-messageformat-parser"),
],
Crear intl sin usar Provider
Hemos añadido una nueva API llamada createIntl que permite crear un objeto IntlShape sin usar Provider. Esto te permite formatear elementos fuera del ciclo de vida de React mientras reutilizas el mismo objeto intl. Por ejemplo:
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>
Esto es especialmente beneficioso en SSR donde puedes reutilizar el mismo objeto intl entre solicitudes.
Cambios en la sintaxis del formato de mensajes
Hemos reescrito nuestro analizador sintáctico para ser más fiel al Formato de mensajes ICU, con el fin de soportar potencialmente skeleton. Hasta ahora, los cambios incompatibles son:
El carácter de escape ha cambiado a apóstrofo (')
Anteriormente usábamos la barra invertida (\) como carácter de escape en la sintaxis de formato de mensajes ICU. Esto generaba problemas con proveedores de traducción estrictos que soportan otras implementaciones como ICU4J/ICU4C. Gracias a @pyrocat101 hemos cambiado este comportamiento para cumplir con la especificación. Esto significa:
// Before
<FormattedMessage defaultMessage="\\{foo\\}" /> //prints out "{foo}"
// After
<FormattedMessage defaultMessage="'{foo}'" /> //prints out "{foo}"
Recomendamos leer la especificación para entender mejor cómo funciona el entrecomillado/escapado aquí en la sección Quoting/Escaping.
Cambio en la sintaxis de argumentos para marcadores de posición
Los argumentos de marcadores de posición ya no pueden contener - (ej: this is a {placeholder-var} es inválido, pero this is a {placeholder_var} es válido).
Pruebas
Hemos eliminado IntlProvider.getChildContext para pruebas. Ahora puedes usar createIntl para crear un objeto intl independiente fuera de React y usarlo con fines de prueba. Consulta Pruebas con React Intl para más detalles.