NextJs + i18n
Documentación Original: Notion
📑 Índice General
Requisitos
- [ ] Leer la documentación oficial
- NextJs: https://nextjs.org/docs
- Nextjs-i18n: https://nextjs.org/docs/advanced-features/i18n-routing
- i18next: https://www.i18next.com/
- next-i18next: https://github.com/isaachinman/next-i18next
- [ ] 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.
⚙️ 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 Unidosen
- 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! 👏
➕ 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 getStaticProps
para 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:
Resultado final:
//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;
🚄 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;