I built a multilingual Next.js site that needed English, French, and Swahili content without adding a large translation framework. The project was a corporate site, so the requirements were practical: clean URLs, translated metadata, stable navigation, and a content model that a small team could maintain.
I chose a simple App Router pattern. Locale lives in the route. Middleware redirects users into the right locale. Server components load dictionaries. Components receive strings instead of reaching into global translation state.

The route should tell you the language
I prefer URLs such as /en/about, /fr/about, and /sw/about because they are explicit. They work for sharing, indexing, analytics, and debugging. If a user sends a link, the recipient gets the same language. If search engines crawl the site, each locale has a stable location.
Middleware handles the first visit. If the path has no locale, it checks the user’s language preference and redirects to a supported locale. After that, the URL carries the decision.
const locales = ["en", "fr", "sw"] as const;
type Locale = (typeof locales)[number];
function hasLocale(pathname: string) {
return locales.some((locale) => pathname.startsWith("/" + locale + "/") || pathname === "/" + locale);
}Dictionaries kept the components simple
Each locale gets a dictionary file. Server components load the correct dictionary for the route and pass strings into presentational components. That kept the UI testable and avoided scattering translation lookups through every component.
The tradeoff is discipline. Dictionary keys need naming conventions. Missing strings need fallbacks. Content editors need to understand that a structural change in one locale may need matching updates in the others. The simplicity is worth it only if the content model stays tidy.
What I would keep
I would keep the explicit locale routes, server-side dictionary loading, and small set of supported languages. I would add a stricter build-time check for missing dictionary keys if the site grew. For a corporate site with a manageable amount of content, this approach kept the code understandable and avoided a dependency that would have been larger than the problem.