rmoral

Next.js multi idioma

Con la versión 10 de Next.js han introducido el soporte a internacionalización. Se puede configurar un listado de idiomas, el idioma por defecto y, en caso de requerirlo, un dominio específico para cada uno. Next.js hará la detección del idioma del usuario y gestionará las rutas automáticamente.

La gestión de idioma por Next.js debe ser complementada con la gestión de las traducciones, utilizando alguna biblioteca Open Source existente o llevando a cabo el desarrollo nosotros mismos. En la documentación de Next.js recomiendan soluciones como react-intl, react-i18next, lingui o rosetta.

En este artículo, os voy a explicar cómo he llevado a cabo la implementación de Polyglot.js, una herramienta desarrollada y mantenida por Airbnb.

Lo primero que deberemos hacer es instalar la dependencia:

$ npm install node-polyglot -S

Internacionalización de rutas con Next.js 10 o superior

La configuración de internacionalización en Next.js debe hacerse en el archivo next.config.js, en este ejemplo vamos a utilizar la opción de Sub-path Routing con locales sin región, es decir, las url's de las páginas de cada idioma tendrán /es o /en al inicio del path.

A continuación la configuración de next.config.js:

// next.config.js
module.exports = {
  i18n: {
    /* Listado de locales que vamos a soportar */
    locales: ['en', 'es'],
    /* Locale seleccionado por defecto */
    defaultLocale: 'es'
  }
}

Traducción de textos con Polyglot.js en Next.js

Para integrar Polyglot.js con Next.js he creado un contexto, el cual recibirá el locale actual en el que está corriendo la aplicación. Este contexto contendrá la instancia de Polyglot con la configuración seleccionada.

// src/I18n.js
import {createContext, useContext, useMemo} from 'react'
import Polyglot from 'node-polyglot'

// Creamos las traducciones para cada idioma.
// Es recomendable mover esto a otros archivos.
const literals = {
  en: {hello: 'hello'},
  es: {hello: 'hola'}
}

// Definimos los idiomas a los que damos soporte
const locales = {
  en: 'en',
  es: 'es'
}

// Creamos el contexto
const I18nContext = createContext()

/**
 * Componente que encapsula la aplicación dentro del Provider.
 * Este componente será el responsable de actualizar el idioma activado en la aplicación
 */
function I18n({children, locale}) {
  // Cada vez que el locale cambie, obtendremos una instancia de i18n actualizada.
  const i18n = useMemo(
    () =>
      new Polyglot({
        locale,
        phrases: literals[locale]
      }),
    [locale]
  )

  return <I18nContext.Provider value={i18n}>{children}</I18nContext.Provider>
}

// Creamos un Hook para utilizar el contexto en los componentes que necesitemos
const useTranslate = () => useContext(I18nContext)

export {locales, useTranslate}
export default I18n

Lo siguiente que debemos hacer consiste en utilizar este contexto en la aplicación. Yo he optado por utilizar el componente I18n directamente en el componente _app.js, enviándole la prop locale que se puede obtener directamente del Hook useRouter.

// src/pages/_app.js
import {useRouter} from 'next/router'
import I18n from '../i18n'

function MyApp({Component, pageProps}) {
  const {locale} = useRouter()

  return (
    <I18n locale={locale}>
      <Component {...pageProps} />
    </I18n>
  )
}

export default MyApp

Localización de textos en componentes y páginas

Para utilizar las traducciones que hemos ido añadiendo en el objeto literals utilizaremos el valor del contexto, que corresponde con la instancia de Polyglot. Para ello, utilizaremos el Hook useTranslate.

// src/pages/index.js
import {useTranslate} from '../i18n'

const Home = () => {
  const i18n = useTranslate()
  return <p>{i18n.t('hello')}</p>
}

export default Home