How to Build a 10-Language Next.js Website (App Router Guide 2026)
Building a multilingual website is one of the highest-leverage investments you can make in SEO and conversion. Seventy-six percent of online shoppers prefer purchasing products with information in their native language (CSA Research, 2020), yet fewer than 5% of active websites support more than three languages. We recently shipped Frida Marketing in 10 languages on Next.js 15 App Router — without next-intl, without a translation SaaS, and without 10 separate codebases. Here's exactly how we built it.

Building a multilingual website is one of the highest-leverage investments you can make in SEO and conversion. Seventy-six percent of online shoppers prefer purchasing products with information in their native language (CSA Research, 2020), yet fewer than 5% of active websites support more than three languages. We recently shipped Frida Marketing in 10 languages on Next.js 15 App Router — without next-intl, without a translation SaaS, and without 10 separate codebases. Here's exactly how we built it.
Key Takeaways
- 76% of shoppers prefer buying in their native language; 40% will never buy from sites that don't offer it (CSA Research, 2020)
- English covers just 49.7% of global web content — building multilingual unlocks the other 50.3% (W3Techs, June 2026)
- One
[lang]dynamic segment + one API prefix computation serves 10 languages from a single set of page files- Gemini 2.5 Flash translates a full 3,000-word HTML post per language in under 90 seconds at near-zero cost
Why Building Multilingual Is Cheaper Than You Think
The cost argument against multilingual sites is almost always overstated. Next.js 15 App Router handles the entire routing layer through a single dynamic segment. The content layer — titles, body, metadata — comes from your API per language. And the translation layer can be fully automated.
English represents 49.7% of all website content by language. Spanish and German each hold 6.0%, Japanese 5.0%, and French 4.6% (W3Techs, June 2026). Supporting the top five non-English languages alone covers roughly 70% of indexed global web content. Next.js runs on 2.7% of all websites globally and 17.9% of surveyed developers use it (Stack Overflow Developer Survey, 2024) — which means the tooling and community support for multilingual Next.js is as good as it's ever been.
The real cost isn't code — it's content. Once your routing is in place, adding a language is one API call per published post. We've published 7 posts across 10 languages with zero per-language maintenance overhead. The architecture decision is the investment; the runtime is free.
96% of companies that invested in localization reported positive ROI, with 65% achieving at least a 3x return (DeepL Business Survey, 2024). The business case is solid. The technical barrier is far lower than most developers expect.
How to Structure Your [lang] Dynamic Segment
Next.js App Router turns your filesystem into your router. Create a src/app/[lang]/ directory and every route inside it gets params.lang automatically. That one parameter is the entire foundation of a 10-language site.
`` src/app/ ├── page.tsx ← English homepage (/) ├── blog/[slug]/page.tsx ← English blog posts ├── admin/layout.tsx ← English admin └── [lang]/ ├── page.tsx ← All other language homepages (/fi, /fr, /de…) ├── blog/[slug]/page.tsx ← Language blog posts └── admin/ └── layout.tsx ← Language admin (same file, reads params.lang) ``
English lives at the root. Every other language lives under [lang]. You write one file and let params.lang drive the logic — no language-specific page copies ever.
Define your language configuration in one canonical file:
```typescript // src/lib/languages.ts export const LANGUAGES = [ { code: 'fi', urlPrefix: 'fi', apiPrefix: 'fi_', name: 'Finnish' }, { code: 'fr', urlPrefix: 'fr', apiPrefix: 'fr_', name: 'French' }, { code: 'de', urlPrefix: 'de', apiPrefix: 'de_', name: 'German' }, { code: 'it', urlPrefix: 'it', apiPrefix: 'it_', name: 'Italian' }, { code: 'no', urlPrefix: 'no', apiPrefix: 'no_', name: 'Norwegian' }, { code: 'ch', urlPrefix: 'ch', apiPrefix: 'pt_', name: 'Swiss German' }, // ⚠️ API uses pt_ { code: 'sr', urlPrefix: 'sr', apiPrefix: 'sr_', name: 'Serbian' }, { code: 'es', urlPrefix: 'es', apiPrefix: 'es_', name: 'Spanish' }, { code: 'sw', urlPrefix: 'sv', apiPrefix: 'sw_', name: 'Swedish' }, // ⚠️ URL uses sv ];
export const LANG_CODES = LANGUAGES.map(l => l.code); ```
Two quirks to document immediately: ch (Swiss German) uses pt_ as its API prefix for historical naming reasons, and sw (Swedish) uses sv as its URL prefix to match the ISO standard. These mismatches won't cause bugs if you centralize the config, but they'll confuse every developer who joins the project without this documented.
Connecting Language Routes to Your Backend API
The entire multi-language API integration is one computation:
``typescript const prefix = params.lang ? ${params.lang}_ : ''; const posts = await fetch(${API_URL}/api/${prefix}posts); ``
When params.lang is 'fi', this fetches /api/fi_posts. When it's undefined (English root), it fetches /api/posts. Every page, every data call, every admin operation uses this same pattern. There's no per-language fetch helper, no switch statement, no special case.

We run this pattern across 10 languages and 5 content types — posts, products, pages, authors, and categories — in production. The only edge case we hit: some API endpoints have different structures for English vs. the language-specific versions. In our setup, /api/{lang}_posts/by-url/{slug} exists for every language, but the English equivalent is a completely different controller (/api/resolve-route?url=...). Always check your English API routes separately — they're often structured differently from the language-specific ones, especially on Laravel backends where the English routes were built first.
The admin panel uses the same prefix pattern:
``typescript // src/app/[lang]/admin/posts/page.tsx export default function AdminPostsPage({ params }: { params: { lang?: string } }) { const prefix = params.lang ? ${params.lang}_ : ''; // Fetch from /api/${prefix}posts // Build edit/delete links with /${params.lang}/admin/edit-post/${id} } ``
A single AdminPostsPage component serves all 10 language admin panels. The English admin lives at /admin/posts; Finnish lives at /fi/admin/posts. Same file.
Generating Static Pages for All 10 Languages
Static generation is where multilingual Next.js sites get their performance edge. Use generateStaticParams to pre-render every language variant at build time:
```typescript // src/app/[lang]/page.tsx — static language homepages export async function generateStaticParams() { return LANG_CODES.map(lang => ({ lang })); // Returns: [{lang:'fi'},{lang:'fr'},{lang:'de'},...] }
// src/app/[lang]/blog/[slug]/page.tsx — static blog posts per language export async function generateStaticParams({ params }: { params: { lang: string } }) { const prefix = params.lang ? ${params.lang}_ : ''; const res = await fetch(${API_URL}/api/${prefix}posts); const data = await res.json(); return data.data.map((post: { url: string }) => ({ slug: post.url })); } ```
Static pages appear as ● in next build output. They pre-render to HTML at deploy time, get pushed to Vercel's edge CDN, and load in milliseconds globally. For a 10-language site, this is the difference between a 50ms TTFB and an 800ms origin hit on every request.
One rule to memorize: never combine generateStaticParams with { cache: 'no-store' } fetches in the same page. In Next.js 15 production builds, this throws Error: Page changed from static to dynamic at runtime — a bug that only surfaces in production, not dev. Use ISR instead:
``typescript const res = await fetch(${API_URL}/api/${prefix}posts, { next: { revalidate: 3600 }, // Regenerate stale pages every hour }); export const dynamicParams = true; // Allow new slugs without a full rebuild ``
next-intl pulls 2.3 million weekly npm downloads as of June 2026, making it the dominant library in the space. It's the right choice if you need locale-aware formatting or hardcoded UI string management. For data-driven CMS sites, it's optional — pure App Router routing handles everything you actually need.
SEO for a 10-Language Site: Hreflang and generateMetadata
Hreflang tells Google which language version to serve to which audience. Without it, your 10 language pages compete against each other for the same queries. The App Router implementation is clean:
```typescript // src/lib/hreflang.ts const HREFLANG_CODE: Record
export function getAlternates(routeKey: string, lang?: string) { const BASE = 'https://fridamarketing.com'; const enUrl = ${BASE}/${routeKey};
const languages: Record
for (const [code, hreflang] of Object.entries(HREFLANG_CODE)) { const urlPrefix = code === 'sw' ? 'sv' : code; languages[hreflang] = ${BASE}/${urlPrefix}/${routeKey}; }
return { canonical: lang ? ${BASE}/${lang === 'sw' ? 'sv' : lang}/${routeKey} : enUrl, languages, }; } ```
Use it in generateMetadata on every page:
``typescript export async function generateMetadata({ params }: Props): Promiseblog/${post.url}, params.lang), }; } ``
This generates a complete hreflang set for every page — x-default, en, and all 9 language variants — with zero manual maintenance. No XML sitemap hreflang blocks to keep in sync; Next.js injects them as tags in the rendered .

According to multilingual SEO research, businesses investing in localization see 300–500% more organic traffic from international markets (MotionPoint, 2025). The hreflang layer is what captures that traffic. Without it, Google either ignores the language signals or surfaces the wrong version for each locale.
Automating Translation: Gemini API in Your Publish Pipeline
Manual translation at 10 languages is the wrong model for any team without a dedicated localization budget. Human translation runs $0.15–$0.30 per word — a 2,500-word post costs $375–$750 per language, or $3,375–$6,750 for all 9 (Seatongue, 2025). Machine translation post-editing cuts that by 50–70%. But for structured HTML content from a CMS, fully automated API translation with a precise prompt is better still.
Here's the core translation call we use with Gemini 2.5 Flash:
``javascript async function translatePost(content, targetLang) { const prompt = Translate the following JSON to ${targetLang}. Rules: - Translate all text values: title, intro, body, meta_title, meta_description - Preserve ALL HTML tags, attributes, and structure in body exactly as-is - Generate a URL-safe localized slug and return it as "url" - Keep brand names, code examples, and proper nouns in English - Return valid JSON only: { title, intro, body, meta_title, meta_description, url }
Content: ${JSON.stringify(content)}`;
const response = await gemini.generateContent(prompt); return JSON.parse(response.text()); } ```
We've run this in production for 7 published posts, each translated to 9 languages automatically. Quality is publication-ready for Finnish, French, German, Spanish, and Italian without manual review. Norwegian and Swedish need occasional spot-checks. Serbian needs a proofreader pass — Gemini is less reliable with Cyrillic in complex HTML contexts. For a content-heavy site, this approach cuts translation overhead by over 90% compared to even machine translation post-editing workflows.
Timing matters. A 3,000-word HTML post takes Gemini 2.5 Flash approximately 60–90 seconds per language. That's 9–13 minutes for all 9 translations. Always set a 150-second abort timeout on each API call and implement exponential backoff — the model occasionally hangs under high demand:
``javascript const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 150000); try { const res = await fetch(GEMINI_URL, { signal: controller.signal, ...opts }); clearTimeout(timeoutId); return res; } catch (err) { clearTimeout(timeoutId); if (err.name === 'AbortError') { // Retry with exponential backoff await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 5000)); } } ``
84% of marketers say content localization has helped grow their revenue, and businesses that invested in translation were 1.5x more likely to see a revenue increase (Redokun, 2025). The automation removes the last real blocker — the operational cost of running translations at scale.
---
Building a multilingual Next.js site? We design and build Next.js websites for global audiences — full i18n routing, hreflang SEO, and automated translation pipelines included. See our Next.js development services or get in touch to discuss your project.
---
Frequently Asked Questions
Do I need next-intl to build a multilingual Next.js site?
No. Next.js 15 App Router handles multilingual routing natively through a [lang] dynamic segment. Libraries like next-intl add value for locale-aware formatting (dates, currencies, pluralization) and hardcoded UI string management. If your content comes from a CMS or API, you don't need them for routing, static generation, or SEO.
How do I map URL language prefixes to API endpoints in Next.js?
Compute a prefix at runtime: const prefix = params.lang ? \${params.lang}_\ : '';. Use it in every fetch call — for example, /api/${prefix}posts. This pattern handles all 10 languages from one shared page file. No language-specific pages, no switch statements, no special cases.
What's the best way to auto-translate a Next.js site to multiple languages?
Use Gemini 2.5 Flash or GPT-4 in your publish pipeline. Send the English content as JSON with a system prompt to translate all text values while preserving HTML tags and generating a localized URL slug. Add a 150-second timeout and exponential backoff. Machine translation post-editing costs 50–70% less than full human translation (Seatongue, 2025), and full API automation reduces that further.
How do I add hreflang tags to a Next.js 15 App Router site?
Build a getAlternates(routeKey, lang) function that returns a Next.js alternates object. Map internal URL codes to BCP 47 lang codes — sw → sv for Swedish, ch → de-CH for Swiss German — and include x-default pointing to the English canonical. Return it from alternates.languages inside each page's generateMetadata export.
How many languages should my website support?
Start with 2–3 that match your top traffic sources, then expand. English covers only 49.7% of global web content (W3Techs, June 2026), so any second language opens a new addressable audience with near-zero marginal cost once your routing and translation pipeline are in place. Businesses investing in localization report 96% positive ROI, with 65% achieving at least a 3x return (DeepL, 2024).
Conclusion
A 10-language Next.js site isn't 10x the work. It's one [lang] dynamic segment, one API prefix computation, one hreflang helper, and a translation script — multiplied across your publishing pipeline. We built this for Frida Marketing and it runs in production across 10 languages with no per-language maintenance overhead and no localization SaaS subscription.
The routing is a morning's work. The hreflang is an afternoon's. The translation pipeline is a weekend project. And the payoff — reaching the 50.3% of the web that isn't English — is permanent.
Start with the [lang] segment today. Add hreflang this week. Wire up the Gemini translation API this month.

Written by
Andrija IlićMore articles
Blog →
How AI Search Changes SEO (And What to Do About It)
Fifty-eight and a half percent of Google searches in the US now end without a single click. That number comes from SparkToro's 2024 study of tens of millions of panelists, and it means the majority of queries today are answered before anyone reaches your website. Add AI Overviews peaking at 24.61% of all queries in July 2025 ([Semrush](https://www.semrush.com/blog/semrush-ai-overviews-study/), 2025), ChatGPT reaching 700 million weekly active users by September 2025, and Perplexity processing 780 million queries in a single month — and the picture becomes clear. Search has fundamentally changed. The question is what to do about it.
Read more →
React Native vs Flutter: Which Should You Choose in 2026?
Flutter quietly overtook React Native in developer adoption last year. The Stack Overflow Developer Survey 2024 found Flutter used by 9.4% of all developers, versus 8.4% for React Native, and the gap widens among learners (11.1% vs 6.7%). Yet React Native still posts roughly six times more job listings on LinkedIn. That's the core tension in this comparison, and it matters a lot depending on what you're building and why.
Read more →
Multilingual SEO Strategy 2026: How to Rank in 10 Languages
Most businesses are competing for 25.9% of the internet.
Read more →