Este es mi primer tutorial, la intención de este es facilitar la vida y ahorrar el tiempo de quien necesite implementar estas tecnologías en un mismo proyecto, a mí me costó un poco implementarlo y quiero compartirlo con quien lo pueda necesitar.
Espero les sirva...
1. Creamos un nuevo proyecto como indica la documentación.
Lo voy a hacer con Typescript pero con Javascript los pasos son los mismos.
npx create-next-app@latest --typescript
Escribes el nombre de tu proyecto y esperas a que se instalen las dependencias, luego ejecutas VSCode o tu editor de código en la carpeta del proyecto.
Ahora en la terminal escribes
npm run dev
Esto iniciará el servidor de desarrollo en el puerto 3000.
2. Instalamos y configuramos next-i18next
Como indica el repositorio del proyecto ejecutamos
yarn add next-i18next
# Si usas NPM ejecuta:
npm i --save next-i18next
Ahora creamos dos carpetas con archivos json para probar las traducciones:
└─ public
└ locales
├─en
| └─ common.json
└─es
└─ common.json
Después creamos un archivo next-i18next.config.js
en la raíz del proyecto con el siguiente contenido:
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'es'],
},
};
Luego en el archivo next.config.js
importamos la configuración:
/** @type {import('next').NextConfig} */
const { i18n } = require("./next-i18next.config");
const nextConfig = {
reactStrictMode: true,
i18n,
};
module.exports = nextConfig;
Finalmente editamos el archivo pages/_app.tsx
para ejecutar la aplicación con traducciones, el cambio se hace en la última línea.
import '../styles/globals.css';
import type { AppProps } from 'next/app';
import { appWithTranslation } from 'next-i18next';
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default appWithTranslation(MyApp);
3. Agregamos y probamos las traducciones.
En los archivos public/locales/en/common.json
y public/locales/es/common.json
agregamos las traducciones de esta manera:
public/locales/en/common.json
{
"welcome": "Welcome to the world of Next.js!",
"content": "This is the blog index page"
}
public/locales/en/common.json
{
"welcome": "Bienvenido al mundo de Next.js!",
"content": "Esta es la página de inicio del blog"
}
Ahora, este paso es importante, para que las traducciones funcionen correctamente, deben ser llamadas desde el servidor en todos los componentes a nivel de página para que este realice la "hidratación" de manera correcta, en este caso editamos nuestro archivo pages/index.tsx
para pasar las traducciones, para poder llamar cada traducción de manera individual usamos
const { t } = useTranslation('common');
El archivo en este caso queda así:
import type { NextPage, NextPageContext } from 'next';
import Head from 'next/head';
import Image from 'next/image';
import styles from '../styles/Home.module.css';
const Home: NextPage = () => {
const { t } = useTranslation('common');
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1 className={styles.title}>{t('welcome')}</h1>
<p className={styles.description}>{t('content')}</p>
</main>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</div>
);
};
export default Home;
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useTranslation } from 'next-i18next';
export async function getStaticProps({ locale }: NextPageContext) {
return {
props: {
...(await serverSideTranslations(locale || 'en', ['common'])),
},
};
}
El resultado es la página traducida con el idioma detectado en el sistema.
Si queremos probar manualmente el cambio de idioma hay que cambiar la configuración, además agregaré otra línea que servirá para subir nuestro proyecto a Vercel, el archivo queda así:
const path = require('path');
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'es'],
localeDetection: false,
localePath: path.resolve('./public/locales'), // for deployment on Vercel
},
};
También crearemos un boton para hacer el cambio de idioma de manera manual, el archivo pages/index.tsx
quedaría así:
import type { NextPage, NextPageContext } from 'next';
import Head from 'next/head';
import Image from 'next/image';
import styles from '../styles/Home.module.css';
import { useRouter } from 'next/router';
const Home: NextPage = () => {
const router = useRouter();
const { pathname, asPath, query } = router;
const { t } = useTranslation('common');
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1 className={styles.title}>{t('welcome')}</h1>
{/* Language Change Button */}
<button
type="button"
onClick={() => {
router.push({ pathname, query }, asPath, {
locale: router.locale === 'es' ? 'en' : 'es',
});
}}
>
{router.locale === 'es' ? 'English' : 'Español'}
</button>
<p className={styles.description}>{t('content')}</p>
</main>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</div>
);
};
export default Home;
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useTranslation } from 'next-i18next';
export async function getStaticProps({ locale }: NextPageContext) {
return {
props: {
...(await serverSideTranslations(locale || 'en', ['common'])),
},
};
}
De esta forma ya tenemos nuestra aplicación trabajando perfectamente con dos idiomas basados en la ruta
4. Configuramos la autenticación con next-auth
Primero instalamos el paquete:
npm i --save next-auth
Para propósitos de prueba usaremos credenciales (email y contraseña), además haremos una validación sencilla, el hacerla de manera correcta depende de lo que quieras en tu proyecto, si lo comentas puedo hacer un tutorial explicando cómo hacer la autenticación con diferentes proveedores y usando una página de inicio customizable. Dicho eso seguimos...
Creamos un archivo pages/api/auth/[...nextauth].ts
Con el siguiente contenido:
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
export default NextAuth({
providers: [
CredentialsProvider({
name: 'credentials',
credentials: {
email: {
label: 'Email',
type: 'email',
placeholder: 'email@example.com',
},
password: {
label: 'Password',
type: 'password',
placeholder: '********',
},
},
async authorize(credentials) {
if (credentials && credentials.email && credentials.password) {
if (
credentials.email === 'test@test.com' &&
credentials.password === 'test'
) {
return {
email: credentials.email,
image: 'https://i.pravatar.cc/500',
name: 'Test User',
};
}
}
return null;
},
}),
],
callbacks: {
jwt: async ({ token }) => {
return token;
},
session: ({ session, token }) => {
if (token) {
session.id = token.id;
}
return session;
},
},
secret: 'my-secret',
jwt: {
secret: 'my-secret',
maxAge: 60 * 60 * 24 * 30,
},
});
Luego aditamos el archivo pages/index.tsx
para tener un botón de autenticación o de logout además del cambio de idioma.
import type { NextPage, NextPageContext } from 'next';
import Head from 'next/head';
import Image from 'next/image';
import styles from '../styles/Home.module.css';
import { useRouter } from 'next/router';
import { useSession, signIn, signOut } from 'next-auth/react';
const Home: NextPage = () => {
const router = useRouter();
const { pathname, asPath, query } = router;
const { t } = useTranslation('common');
const { data: session } = useSession();
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1 className={styles.title}>{t('welcome')}</h1>
{/* Language Change Button */}
<button
type="button"
onClick={() => {
router.push({ pathname, query }, asPath, {
locale: router.locale === 'es' ? 'en' : 'es',
});
}}
>
{router.locale === 'es' ? 'English' : 'Español'}
</button>
{/* Authentication Button */}
{session ? (
<>
<h4>
{t('welcome')} {JSON.stringify(session.user)}
</h4>
<button
type="button"
onClick={() => {
signOut();
}}
>
{t('signOut')}
</button>
</>
) : (
<button
type="button"
onClick={() => {
signIn();
}}
>
{t('signIn')}
</button>
)}
<p className={styles.description}>{t('content')}</p>
</main>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</div>
);
};
export default Home;
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useTranslation } from 'next-i18next';
export async function getStaticProps({ locale }: NextPageContext) {
return {
props: {
...(await serverSideTranslations(locale || 'en', ['common'])),
},
};
}
Ahora lo más importante, seguramente la razón por la que vienes a este post, y personalmente lo que más me costó solucionar, solo hay que hacer un pequeño cambio en el archivo pages/_app.tsx
para que el SessionProvider no choque con el Provider de Traducción:
import '../styles/globals.css';
import type { AppProps } from 'next/app';
import { appWithTranslation } from 'next-i18next';
import { SessionProvider } from 'next-auth/react';
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
const AppWithI18n = appWithTranslation(MyApp);
const AppWithAuth = (props: AppProps) => (
<SessionProvider session={props.pageProps.session}>
<AppWithI18n {...props} />
</SessionProvider>
);
export default AppWithAuth;
¡Y listo!, ahora podemos cambiar el idioma de nuestra página y tener la autenticación en la misma aplicación de NextJS.
Espero haberte ayudado, si tienes alguna observación o duda déjala en los comentarios para ayudar a más desarrolladores.
Top comments (0)