August 11, 2023

A basic application integrating the nexti18next internationalization library with nextjs

NextJs + i18n

Documentación Original: Notion

Demo


📑 Índice General

🤔Requisitos

🌍Internacionalización


Requisitos


  • [ ] Iniciar proyecto
  npx create-next-app@latest --typescript
  # or
  yarn create next-app --typescript
  # or
  pnpm create next-app -- --typescript

Next: 🌍 Internacionalización


Internacionalización

🤭 🤭 Si te has leído la documentación, enhorabuena, no necesitas seguir leyendo 🤭 🤭

📑 Índice


🧐 Background

Nextjs tiene soporte integrado para rutas con i18n desde la versión 10.0.0.

Podemos establecer una lista de localizaciones, la localización por defecto y las localizaciones específicas del dominio y Nextjs se encarga automáticamente de su gestión.

Este soporte está destinado a complementar las soluciones de bibliotecas i18n existentes como, react-i18next por ejemplo.

Índice


⚙️ Configuración

El siguiente fragmento del código debemos introducirlo en el fichero next.config.js en el directorio raíz del proyecto.

Generalmente el nombre del local está compuesto por la lengua, región y script separado por un guión. La región y script son opcionales. Por ejemplo:

  • en-US - inglés de la región de Estados Unidos
  • en - también se puede dejar así

Resultado:

/** @type*/
const nextConfig = {
  reactStrictMode: true,

  i18n: {
    // all of locales suported in our app
    locales: ["en-US", "fr", "es-ES"],
    // set the default languages for our app
    defaultLocale: "es-ES",
    // list of the possible domain
    domains: [
      {
        domain: "example.com",
        defaultLocale: "en-US",
      },
      {
        domain: "example.fr",
        defaultLocale: "fr",
      },
      {
        domain: "example.es",
        defaultLocale: "es-ES",
        // OPTIONAL: varaible to test the locales in http
        http: true,
      },
    ],
  },
};

module.exports = nextConfig;

⚠️ ❗Cada vez que se modifica este fichero hay que reiniciar el server del nextjs ❗⚠️

> Found a change in next.config.js. Restart the server to see the changes in effect.

Ahora si queremos ir http://localhost:3000/fr deberíamos tener la misma página sin cambios, en caso de no introducir los cambios de arriba, obtendríamos 404 | This page could not be found.

👏 ¡Ya tenemos funcionando i18n con nextjs! 👏

Índice


➕ Añadir idiomas - next-i18next

Vamos a utilizar la librería next-i18next creado a partir de react-i18next para Nextjs.

yarn add next-i18next

# or

npm i --save next-i18next

Una vez añadido la librería, debemos realizar añadir su configuración en el fichero next-i18next.config.js en el directorio raíz del proyecto. Este fichero será idéntico al next.config.js:

//next-i18next.config.js
module.exports = {
  i18n: {
    locales: ["en-US", "fr", "es-ES"],
    defaultLocale: "es-ES",
  },
};

⚠️ Si añadimos otro idioma en el futuro habrá que cambiar ambos ficheros con sus respectivos cambios.

Ahora debemos modificar pages/_app.js puesto que vamos a usar componente de orden superior (HOC) para englobar nuestra aplicación. Por lo que el fichero va a quedar de la siguiente manera:

//pages/_app.tsx

import "../styles/globals.css";
import type { AppProps } from "next/app";

import { appWithTranslation } from "next-i18next";

function MyApp({ Component, pageProps }: AppProps) {
  return ;
}

export default appWithTranslation(MyApp);

Ahora vamos a renderizar nuestra página dependiendo de unos ficheros json que contendrán los valores en dichos lenguajes.

Vamos a añadir 3 carpetas, una por cada idioma en nuestra aplicación: en-US, fr y es-ES. Podemos crear estos ficheros en cualquier directorio, en nuestro caso lo vamos hacer en el /public/locale quedandose de la siguiente manera:

en-US/home.json

{
  "welcome_msg": "Welcome to your NextJs-i18n app"
}

fr/home.json

{
  "welcome_msg": "Bienvenue dans notre application NextJs-i18n"
}

es-ES/home.json

{
  "welcome_msg": "Bienvenido a nuestra aplicación de NextJs-i18n"
}

⚠️ ⚠️ Además del fichero es-ES/home.json la librería de next-i18next por defecto espera que haya una estructura de carpeta igual a esta:

.
└── public
 └── locales
     ├── en
     |   └── common.json
     └── de
         └── common.json

Por lo que nuestra estructura de ficheros sería la siguiente:

.
└── public
    └── locales
        ├── en-US
        |   └── common.json
        |   └── home.json
        └── fr
        |   └── common.json
        |   └── home.json
        └── en-ES
            └── common.json
            └── home.json

Una vez que tenemos estos ficheros creados vamos a utilizarlo en nuestra página de home que se encuentra en /pages/index.tsx . creados Vamos a utilizar unas de las ventajas que nos ofrece Nextjs que es getStaticPropspara obtener los datos previo a la renderización de la página.

Primero añadimos la función getStaticProps

//pages/index.tsx

import { GetStaticProps } from "next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";

export const getStaticProps: GetStaticProps = async (context) => {
  const { locale } = context;

  //locale can be string or undefined, so we do a check before returning the props
  if (locale) {
    return {
      props: {
        //fetch the file for our home page depending of the locale
        ...(await serverSideTranslations(locale, ["home"])),
      },
    };
  }
  //in case the locale is undefined, we can just serve our default languege file
  return {
    props: {
      ...(await serverSideTranslations("es-ES", ["home"])),
    },
  };
};

Todavía nuestra página no está haciendo uso del fichero de idiomas, para ello debemos añadir el siguiente hook useTranslation() de next-i18next de la siguiente manera:

//pages/index.tsx

import type { NextPage } from "next";
import Head from "next/head";
import styles from "../styles/Home.module.css";
import { useTranslation } from "next-i18next";

const Home: NextPage = () => {
  const { t } = useTranslation();

  return (
    
Create Next App
); }; export default Home;

Ahora si, podemos ir a las siguientes rutas y veremos como cambia el mensaje de bienvenida:

  //pages/index.tsx
  import type { NextPage } from "next";
  import Head from "next/head";
  import { useTranslation } from "next-i18next";
  import { GetStaticProps } from "next";
  import styles from "../styles/Home.module.css";
  import { serverSideTranslations } from "next-i18next/serverSideTranslations";

  export const getStaticProps: GetStaticProps = async (context) => {
    const { locale } = context;

    //locale can be string or undefined, so we do a check before returning the props
    if (locale) {
      return {
        props: {
          ...(await serverSideTranslations(locale, ["home"])),
        },
      };
    }
    //in case the locale is undefined, we can just serve our default langueges
    return {
      props: {
        ...(await serverSideTranslations("es-ES", ["home"])),
      },
    };
  };

  const Home: NextPage = () => {
    const { t } = useTranslation();

    return (
      
Create Next App
); }; export default Home;

https://media1.giphy.com/media/CuMiNoTRz2bYc/200.gif

Índice


🚄 Transición entre locales

La transición se puede realizar a través de next/link o next/router de la siguiente manera:

//pages/index.tsx

//skipped imports
import Link from "next/link";

// skipped getStaticProps code

const Home: NextPage = () => {
  const { t } = useTranslation();

  return (
    <>
      {/*skipped code */}
      

Static *

* ); }; export default Home;

Con next/router tendríamos:

//pages/index.tsx

//skipped imports
import { useRouter } from "next/router";

// skipped getStaticProps code

const Home: NextPage = () => {
  const { t } = useTranslation();
  const router = useRouter();

  return (
    <>
      {/*skipped code */}
      

With next/router

{ router.push("/", "/", { locale: "fr" }); }} > to /fr
); }; export default Home;

¿Y si necesitamos rutas dinámicas? Pues también lo tenemos cubierto

//pages/index.tsx

//skipped imports
import { useRouter } from "next/router";

// skipped getStaticProps code

const Home: NextPage = () => {
  const { t } = useTranslation();
  const router = useRouter();

  const dynamicLink = (locale: string) => {
    router.push({ pathname, query }, asPath, { locale: locale });
  };

  return (
    <>
      {/*skipped code */}
      

Dynamic route with router.push()

{ // This variable can come from the api of another page, etc. // and we could go to the locale of that page dynamicLink("fr"); }} > to /fr
); }; export default Home;