A full-body photograph of me with large mountains in the background.R. Agustín Morán
  • Blog
  • About me
  • Portafolio
  • Projects
  • Photos
Ricardo Agustín Morán R.
  • LinkedIn
  • GitHub
  • Instagram
Sitemap
  • Home
  • Portafolio
  • Projects
  • About me
  • Photos
  • My mission
  • Experience
  • Education
  • Sitemap.xml
  • Blog
Contact

You can contact me through my social media or via email: agustin.moranr@gmail.com. I'll be delighted to meet you!

©

Ricardo Agustín Morán Rivas

. 2025 🇲🇽
Hecho con cariño.

In this article, you will learn one of the multiple approaches available for changing the language of content on a dynamic page implemented with Strapi and Next.js.

This is not a tutorial from scratch on creating a website with internationalization.

The central objective of this article is to show you how to redirect a user to the correct route if they change the language of a website while on a dynamic route.

Illustrative image showing route redirection.

This applies when using Strapi's UUID data type to query a dynamic page.

Why? 🤷‍♀️

Explanation of the problem.

To illustrate, I have created the following Strapi project:

The project is configured to manage its content in Spanish and English, with Spanish being the default language.

Configuración del Plugin de Internacionalización de Strapi.

Additionally, it includes a Blog Collection Type, which has a Text field for the Blog Title and a UUID field for its Slug.

Imagen del Content Type para nuestros Blogs en Strapi.

Finally, we have an example Blog that in its Spanish version has the title: "Mi Blog Super Épico" and the Slug: mi-blog-super-epico.

Imagen del Blog de ejemplo "Mi Blog Super Épico" en Español.

And in its English version, it has the title: "My Super Epic Blog" and the Slug: my-super-epic-blog.

Imagen del Blog de ejemplo "Mi Blog Super Épico" en inglés.

By enabling internationalized content in Strapi, it adds a filtering property called "locale," which allows the developer to filter content by locale.

In our case, since we only have two languages, the locale property can take the value of "es," "en," or "all," where "all" instructs Strapi to query all configured locations.

Commonly, you might think that you can query information from a record filtering by its UUID field in a different locale. But this is not the case in Strapi.

Taking our Strapi project as an example, if you try to query the title of the Blog "Mi Blog Super Épico" in its English version using its Spanish Slug, the query will not return any information: 🚨

Why is this important? 🥀

Commonly, websites have a single Language Menu or Language Selector, which adds a suffix to the current route with the locale or language selected by the user.

Ejemplo de botón de cambio de idioma en funcionamiento.

The above example shows a website I created using Next.js to integrate it with our Strapi project. You can visit it to see the final version of this example at: i18nBlog. You can also view the complete code in the following repository.

Blog que cambia de idioma en una ruta dinámica.

The Next.js website has a button that, when clicked, switches the page language between Spanish and English. Where, at the route level, we would have the following:

  • Spanish Route: https://example.com/
  • English Route: https://example.com/en

Taking the above and returning to our example. Let's imagine that we access "Mi Blog Super Épico" in Spanish:

https://example.com/blog/mi-blog-super-epico.

If we change the language of our website to English, our route would change to:

https://example.com/en/blog/mi-blog-super-epico.

Do you notice what the problem is? 🤔

Exactly, that we have the locale of our website in English ("en"), but the Slug is still: mi-blog-super-epico (Spanish). And as we saw in our previous GraphQL query, we need the Slug in English to get the information, i.e., my-super-epic-blog.

How do we solve this problem?

A very simple solution would be to redirect to the route with the Slug in the correct locale when it is detected that the user has changed the language of the website.

Illustrative image showing route redirection.

✨Very well, let's implement this solution.✨

In my example, I am using Next.js 14 with the App Router. Again, this article solves a specific case, so I will not delve into how to create a website with internationalization in Next.js. But you can learn here: Next.js Internationalization.

To start, we will add the base to have a dynamic page in Next.js that queries our Strapi API and shows the Title of our Blog "Mi Blog Super Épico."

Learn How to Internationalize Dynamic Routes Using Strapi and Next.js

READING TIME
~ 12 Minutes
PUBLISHED
29 / May / 2024
graphql
query getBlogBySlug {
  blogs(
    locale: "en"
    filters: { 
      slug: { eq: "mi-blog-super-epico" } #use "my-super-epic-blog" insted.
    } 
  ) {
    data {
      attributes {
        title
      }
    }
  }
}

output:

json
{
  "data": {
    "blogs": {
      "data": []
    }
  }
}

Consider the following folder structure:

Folder structure
src/
├── app/
│   └── [locale]/
│       └── blog/
│           └── [slug]/
│               └── page.tsx
├── service/
│   └── getBlogBySlug.ts
└── graphql/
    └── queries/
        └── getBlogBySlug.graphql

The above is the base of our example Blog project. ⬆

The file page.tsx is the dynamic page for our Blog. It receives two parameters, which are the locale in which our website is located, and the Slug of the Blog we are viewing.

typescript
// blog/[slug]/page.tsx
 
import getBlogBySlug from '@/service/getBlogBySlug';
 
export enum LOCALES_LIST {
	ES = 'es',
	EN = 'en',
}
 
export default async function BlogPage({
	params: { locale, slug },
}: {
	params: { locale: LOCALES_LIST; slug: string };
}) {
 
  //query blog data
	const blogResponse = await getBlogBySlug(slug)
   const { title } = blogResponse?.attributes;
 
	return (
		<div>
			<h1>
				{title}
			</h1>
		</div>
	);
}

The file getBlogBySlug.graphql contains the GraphQL query to get the Title of our Blog.

graphql
# graphql/queries/getBlogBySlug.graphql
 
query getBlogBySlug($slug: String) {
	blogs(
		locale: "all"
		filters: { slug: { eq: $slug } }
	) {
		data {
			attributes {
				title
			}
		}
	}
}

Lastly, the file getBlogBySlug.ts is the service responsible for handling the GraphQL query to our Strapi API.

typescript
// services/getBlogBySlug.ts
 
import { getClient } from '@/lib/client';
import { GetBlogBySlugQuery } from "@/schema/graphql"
 
export async function getBlogBySlug(slug: string) {
	let response = null;
	try {
		const client = getClient();
		const {
			error,
			data: { blogs },
		} = await client.query({
			query: GetBlogBySlugQuery,
			variables: {
				slug,
			},
		});
 
		if (error || !blogs?.data[0]) {
			throw new Error('Error retrieving blog');
		}
 
		response = blogs.data[0];
	} catch (error) {
		console.error(error);
	}
 
	return response;
}
 
export default getBlogBySlug;

For the example, I am using Apollo Client and Codegen to type and execute the query. But it doesn't matter the client or way in which you make the query to your Strapi API.

Up to this point, we would have the base implementation to query our Blog by Slug. But currently, if the user changes the language of the website while being on the dynamic page of our Blog, the query will fail and we will not obtain its information ☹. This is what interests us so let's solve it.

Redirecting dynamic pages when changing the language of a website.

To correctly redirect to the correct page of our Blog, let's modify our GraphQL query to also obtain the Slugs and the Locale of the Blog in all its locations using the localizations attribute of Strapi:

graphql
#getBlogBySlug.graphql
 
query getBlogsBySlug($slug: String) {
	blogs(
		locale: "all"
		filters: { slug: { eq: $slug } }
	) {
		data {
			attributes {
				title
				localizations {
					data {
						attributes {
							locale
							slug
						}
					}
				}
			}
		}
	}
}

If we use the above query to get the data of "Mi Blog Super Épico" in Spanish, we will get the following response:

json
{
  "data": {
    "blogs": {
      "data": [
        {
          "attributes": {
            "title": "Mi Blog Súper Épico.",
            "localizations": {
              "data": [
                {
                  "attributes": {
                    "locale": "en",
                    "slug": "my-super-epic-blog"
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}

Here is where it gets interesting.

Using these locales and Slugs, we will create a utility function mapSlugsWithLocales which will return an object with the following structure:

typescript
{
  es: "mi-blog-super-epico",
  en: "my-super-epic-blog"
}

Very well, let's implement the mapSlugsWithLocales function.

Folder structure
src/
├── app/
│   └── [locale]/
│       └── blog/
│           └── [slug]/
│               └── page.tsx
├── service/
│   └── getBlogBySlug.ts
├── graphql/
│   └── queries/
│       └── getBlogBySlug.graphql
└── lib/
    └── utils/
        └── mapSlugsWithLocales.ts

Our mapSlugsWithLocales function will receive the localizations of our Blog and the current localization as parameters. Then, using the Reduce method, it will return the output previously described.

typescript
//mapSlugsWithLocales.ts
 
function mapSlugsWithLocales(
	localizations: any[],
	{ currentLocalization }: { currentLocalization: Record<string, string> },
) {
	return localizations.reduce((acc: Record<string, string>, { attributes }) => {
		acc[attributes.locale] = attributes.slug;
		return acc;
	}, currentLocalization);
}
 
export default mapSlugsWithLocales;

Finally, let's use our mapSlugsWithLocales function in our dynamic page page.tsx and with the help of the redirect function from Next.js, let's redirect the user to the correct route when we detect that the Slug is not correct for the language version of our website.

typescript
//blog/[slug]/page.tsx
 
import getBlogBySlug from '@/service/getBlogBySlug';
import mapSlugsWithLocales from '@/lib/utils/mapSlugsWithLocales';
import { redirect } from 'next/navigation';
 
export enum LOCALES_LIST {
	ES = 'es',
	EN = 'en',
}
 
export default async function BlogPage({
	params: { locale, slug },
}: {
	params: { locale: LOCALES_LIST; slug: string };
}) {
	//query blog data
	const blogResponse = await getBlogBySlug(slug);
 
	const { title, localizations } = blogResponse?.attributes;
 
	const localesWithSlugsMap = mapSlugsWithLocales(
		localizations?.data ?? [],
		{ currentLocalization: { [locale]: slug } }, //Current Slug and current Locale
	);
 
	const currentSlugIsInvalid = slug !== localesWithSlugsMap[locale];
 
	//Redirect to the correct route.
	if (currentSlugIsInvalid) {
		redirect(`/blog/${localesWithSlugsMap[locale]}`);
	}
 
	return (
		<div>
			<h1>{title}</h1>
		</div>
	);
}

✨That's it!✨

Now our website correctly redirects our dynamic Blog page when the user changes the website language.

You can implement this functionality on dynamic pages within your website that have versions in different languages.!

Thank you very much if you have made it this far. I hope this article has been helpful to you, and now you know how to manage dynamic pages that use dynamic Slugs in Strapi.