Ir para o conteúdo principal

Guia de Atualização (v2 -> v3)

Tradução Beta Não Oficial

Esta página foi traduzida por PageTurner AI (beta). Não é oficialmente endossada pelo projeto. Encontrou um erro? Reportar problema →

Alterações de API que Quebram Compatibilidade

  • addLocaleData foi removido. Veja Migrar para APIs Intl nativas para detalhes.

  • ReactIntlLocaleData foi removido. Veja Migrar para APIs Intl nativas para detalhes.

  • intlShape foi removido. Veja Suporte TypeScript para detalhes.

  • O padrão de textComponent no IntlProvider foi alterado para React.Fragment. Para manter o comportamento anterior, defina explicitamente textComponent como span.

<IntlProvider textComponent="span" />
  • FormattedRelative foi renomeado para FormattedRelativeTime com mudanças significativas na API. Veja FormattedRelativeTime para detalhes.

  • formatRelative foi renomeado para formatRelativeTime com mudanças significativas na API. Veja FormattedRelativeTime para detalhes.

  • Mudanças na sintaxe de Message Format. Veja Alterações na Sintaxe de Message Format para detalhes.

  • IntlProvider não herda mais do IntlProvider upstream.

Utilize React 16.3 ou superior

React Intl v3 suporta a nova API de contexto, resolvendo problemas de atualização de árvore 🎉 Além disso, utiliza os novos lifecycle hooks (eliminando os obsoletos). Também suporta React.forwardRef(), permitindo acesso direto a refs via prop ref (veja abaixo).

Migre de withRef para forwardRef

Com a atualização para React >= 16.3, obtivemos a opção de usar o novo recurso React.forwardRef() e, por causa disso, descontinuamos o uso da opção withRef para o HOC injectIntl em favor de forwardRef. Quando forwardRef é true, você pode ignorar completamente a presença do 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} />
}
}

Novo hook useIntl como alternativa ao HOC injectIntl

Esta versão v3 suporta hooks do React (>= 16.8). Agora você pode usar o hook useIntl como alternativa ao HOC injectIntl em componentes funcionais. Ambos acessam a instância intl:

// 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 manter a API limpa, fornecemos apenas useIntl. Se preferir, encapsule este hook para criar soluções como useFormatMessage. Consulte a introdução oficial a hooks.

Migre para APIs Intl nativas

React Intl v3 não inclui dados CLDR, dependendo de APIs Intl nativas:

Essa mudança torna o React Intl mais futuro, pois estas APIs são estáveis em navegadores modernos. Também eliminamos o empacotamento de CLDRs.

Se anteriormente você usava addLocaleData para dar suporte a navegadores antigos, recomendamos o seguinte:

  1. Se você suporta navegadores sem Intl.PluralRules (ex: IE11 e Safari 12-), inclua este polyfill na sua build.

  2. Se você suporta navegadores sem Intl.RelativeTimeFormat (ex: IE11, Edge, Safari 13-), inclua este polyfill na sua build junto com dados CLDR individuais para cada localidade suportada.

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

Ao usar React Intl no Node.js, seu binário node deve:

OU

Suporte ao TypeScript

O react-intl foi reescrito em TypeScript e agora possui suporte nativo. Portanto, removemos a dependência prop-types e expomos IntlShape como uma interface.

Todos os tipos devem estar disponíveis no arquivo index de nível superior, sem necessidade de importar de subarquivos específicos. Por exemplo:

import {IntlShape} from 'react-intl' // Correct
import {IntlShape} from 'react-intl/lib/types' // Incorrect

Se faltar qualquer suporte de interface de nível superior, informe-nos e/ou envie um PR - será muito apreciado :)

informação

Talvez você precise ajustar seu código se dependia do pacote @types/react-intl (agora obsoleto). O exemplo mais comum é substituir InjectedIntlProps por WrappedComponentProps.

FormattedRelativeTime

Quando introduzimos FormattedRelative, a especificação do Intl.RelativeTimeFormat ainda era instável. Agora ela está no estágio 3 e vários navegadores a implementaram. Porém sua API difere da FormattedRelative, então ajustamos para seguir a especificação, quebrando a compatibilidade com versões anteriores.

  1. Todas as units (como day-short) tornam-se uma combinação de unit e style:
<FormattedRelative units="second-short"/>
// will be
<FormattedRelativeTime unit="second" style="short"/>
  1. style torna-se numeric (valor padrão):
<FormattedRelative style="numeric"/>
// will be
<FormattedRelativeTime />

<FormattedRelative style="best fit"/>
// will be
<FormattedRelativeTime numeric="auto"/>
  1. O tipo de value muda de Date para delta na unit especificada:
<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" />
  1. updateInterval torna-se updateIntervalInSeconds e aceitará apenas delta de tempo em segundos. O comportamento de atualização permanece:
<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`
  1. initialNow foi removido.

Similarmente, a contraparte funcional formatRelative foi renomeada para formatRelativeTime com parâmetros ajustados para refletir as props deste componente.

  1. Implementando comportamento do FormattedRelative

Você pode usar @formatjs/intl-utils para aproximar o comportamento anterior assim:

import {selectUnit} from '@formatjs/intl-utils'
const {value, unit} = selectUnit(Date.now() - 48 * 3600 * 1000)
// render
;<FormattedRelativeTime value={value} unit={unit} />

Formatação de rich text aprimorada em FormattedMessage e formatMessage

Na v2, para formatação de rich text (incorporar ReactElement), você precisava fazer:

<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>,
}}
/>

Agora você pode fazer:

<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>,
}}
/>

Esta mudança resolve vários problemas:

  1. Perda de informação contextual ao estilizar partes do texto: No exemplo acima, link era um placeholder caixa-preta para tradutores. Poderia ser pessoa, animal ou timestamp. Transmitir contexto via description e placeholder não bastava quando variáveis ficavam complexas.

  2. Traz paridade de recursos com outras bibliotecas como fluent da Mozilla (usando Overlays).

Caso anteriormente você tenha passado um ReactElement como placeholder, recomendamos fortemente repensar a estrutura para declarar o máximo de texto possível:

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>
}}
/>

Depois

<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 e suas bibliotecas subjacentes (intl-messageformat-parser, intl-messageformat, @formatjs/intl-relativetimeformat, intl-format-cache, intl-utils) exportam artefatos ESM. Isso significa que você deve configurar sua toolchain de build para transpilar essas bibliotecas.

Jest

Adicione transformIgnorePatterns para sempre incluir essas bibliotecas, por exemplo:

{
transformIgnorePatterns: [
'/node_modules/(?!intl-messageformat|intl-messageformat-parser).+\\.js$',
],
}

webpack

Se estiver usando babel-loader, adicione essas bibliotecas em include, por exemplo:

include: [
path.join(__dirname, "node_modules/react-intl"),
path.join(__dirname, "node_modules/intl-messageformat"),
path.join(__dirname, "node_modules/intl-messageformat-parser"),
],

Criando intl sem usar Provider

Adicionamos uma nova API chamada createIntl que permite criar um objeto IntlShape sem usar Provider. Isso permite formatar elementos fora do ciclo de vida do React enquanto reutiliza o mesmo objeto intl. Por exemplo:

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>

Isso é especialmente benéfico em SSR, onde você pode reutilizar o mesmo objeto intl entre requisições.

Alterações na Sintaxe de Formatação de Mensagens

Reescrevemos nosso parser para ser mais fiel ao ICU Message Format, visando potencial suporte a skeleton. As alterações incompatíveis com versões anteriores são:

O caractere de escape mudou para apóstrofe (')

Anteriormente, nossa sintaxe usava backslash (\) como escape. Isso causava problemas com ferramentas de tradução compatíveis com ICU4J/ICU4C. Graças a @pyrocat101, alinhamos o comportamento à especificação:

// Before
<FormattedMessage defaultMessage="\\{foo\\}" /> //prints out "{foo}"

// After
<FormattedMessage defaultMessage="'{foo}'" /> //prints out "{foo}"

Recomendamos ler a especificação sobre citação/escape aqui na seção Quoting/Escaping.

Mudança na sintaxe de argumentos de placeholder

Argumentos de placeholder não podem mais conter - (ex: this is a {placeholder-var} é inválido, mas this is a {placeholder_var} é válido).

Testes

Removemos IntlProvider.getChildContext para testes. Agora você pode usar createIntl para criar um objeto intl autônomo fora do React para propósitos de teste. Veja Testando com React Intl para detalhes.