跳到正文
cccskills
免费在 GitHub 公开

i18n

Add full internationalization (i18n) to a Next.js project using next-intl. Supports 14+ languages, SEO-friendly locale routing, hreflang sitemaps, and bulk translation. Use when the user asks to "internationalize", "add i18n", "add translations", "multi-language", "localize", "add language support", or "translate my site".

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md9.2 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Internationalize a Next.js Project

Add complete internationalization to a Next.js (App Router) project using next-intl v4. This skill handles routing, translation files, sitemap hreflang, and bulk translation across all locales.

Step 1: Assess the Project

  1. Check the Next.js version (package.json) — must be 13+ with App Router
  2. Check if i18n is already partially set up (look for next-intl, next-i18next, [locale] routes)
  3. Identify all pages/routes that need translation
  4. Identify all user-facing strings (hardcoded text in components)
  5. Ask the user which locales to support (default recommendation: en, es, fr, de, pt, ja, ar, zh, zh-tw, id, vi, ms, ru, hi)

Step 2: Install Dependencies

npm install next-intl

Step 3: Create i18n Configuration Files

Create 4 files under src/i18n/:

src/i18n/config.ts

export const locales = ['en', 'es', 'fr', 'de', 'pt', 'ja', 'ar', 'zh', 'zh-tw', 'id', 'vi', 'ms', 'ru', 'hi'] as const

export type Locale = (typeof locales)[number]
export const defaultLocale: Locale = 'en'

export const localeNames: Record<Locale, string> = {
  en: 'English',
  es: 'Espanol',
  fr: 'Francais',
  de: 'Deutsch',
  pt: 'Portugues',
  ja: '日本語',
  ar: 'العربية',
  zh: '简体中文',
  'zh-tw': '繁體中文',
  id: 'Bahasa Indonesia',
  vi: 'Tieng Viet',
  ms: 'Bahasa Melayu',
  ru: 'Русский',
  hi: 'हिन्दी',
}

export const rtlLocales: Locale[] = ['ar']

src/i18n/routing.ts

import { defineRouting } from 'next-intl/routing'
import { defaultLocale, locales } from './config'

export const routing = defineRouting({
  locales,
  defaultLocale,
  localePrefix: 'as-needed', // English URLs stay clean, other locales get /es/, /fr/, etc.
})

src/i18n/navigation.ts

import { createNavigation } from 'next-intl/navigation'
import { routing } from './routing'

export const { Link, redirect, usePathname, useRouter } = createNavigation(routing)

src/i18n/request.ts

import { getRequestConfig } from 'next-intl/server'
import { routing } from './routing'

export default getRequestConfig(async ({ requestLocale }) => {
  let locale = await requestLocale
  if (!locale || !routing.locales.includes(locale as any)) {
    locale = routing.defaultLocale
  }
  return {
    locale,
    messages: (await import(`../messages/${locale}.json`)).default,
  }
})

Step 4: Create Middleware

Create src/middleware.ts:

import createMiddleware from 'next-intl/middleware'
import { routing } from '@/i18n/routing'

export default createMiddleware({
  ...routing,
  localeDetection: false, // Don't auto-redirect based on Accept-Language
})

export const config = {
  matcher: ['/((?!_next|api|images|fonts|favicon|sitemap|robots).*)'],
}

Key decision: localeDetection: false prevents auto-redirecting users based on browser language. This keeps English URLs stable for SEO. Users can manually switch languages via a language selector.

Step 5: Update next.config

Wrap the existing config with createNextIntlPlugin:

import createNextIntlPlugin from 'next-intl/plugin'
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts')

// ... existing config ...
export default withNextIntl(nextConfig)

Step 6: Add [locale] Dynamic Route

Move all page content under src/app/[locale]/:

  1. Create src/app/[locale]/layout.tsx with:

    • generateStaticParams() returning all locales
    • setRequestLocale(locale) call
    • <NextIntlClientProvider> wrapping children
    • <html lang={locale} dir={rtlLocales.includes(locale) ? 'rtl' : 'ltr'}>
    • Hreflang <link> tags in <head> for all locales + x-default
  2. Move existing pages into src/app/[locale]/

  3. Each page should call setRequestLocale(locale) for static generation

Step 7: Extract Strings into Translation Files

  1. Create src/messages/en.json with all user-facing strings organized by section:

    {
      "common": { "signIn": "Sign In", ... },
      "tools": { "tool-slug": { "title": "...", "description": "..." } },
      "faq": { "tool-slug": [{ "question": "...", "answer": "..." }] }
    }
    
  2. Replace all hardcoded strings in components with useTranslations():

    const t = useTranslations('common')
    return <button>{t('signIn')}</button>
    
  3. For server components, use getTranslations():

    const t = await getTranslations('common')
    

Step 8: Translate to All Locales

For each non-English locale, create src/messages/{locale}.json with the same structure as en.json.

Translation Strategy

Use parallel Codex agents via the codex-tasks skill to save Claude credits:

  1. Launch one Codex task per locale (up to 7 in parallel) using /codex-tasks
  2. Each task reads en.json, translates all strings, writes {locale}.json
  3. Codex prompt should include:
    • The full en.json content (or path to read it)
    • Target language name and locale code
    • Instructions:
      • Translate naturally, not literally
      • Keep technical terms in English (PowerPoint, PDF, API, etc.)
      • Preserve JSON structure exactly (same keys, same nesting)
      • Preserve interpolation variables like {count}, {name} unchanged
      • Write the result to src/messages/{locale}.json
  4. After Codex tasks complete, verify the results using the verification script below — Codex output quality varies and must be checked

Verification

After translation, run a verification script to catch issues:

import json

locales = ['es', 'fr', 'de', 'pt', 'ja', 'ar', 'zh', 'zh-tw', 'id', 'vi', 'ms', 'ru', 'hi']
english_words = ['the ', 'and ', 'you ', 'your ', 'our ', 'this ', 'that ', 'with ', 'from ', 'will ']

with open('src/messages/en.json') as f:
    en = json.load(f)

for loc in locales:
    with open(f'src/messages/{loc}.json') as f:
        data = json.load(f)

    # Check: missing sections
    missing = [s for s in en if s not in data]

    # Check: residual English content
    eng_count = 0
    def check(d):
        nonlocal eng_count  # won't work in inline script; use list trick
        if isinstance(d, dict):
            for v in d.values(): check(v)
        elif isinstance(d, str):
            if sum(1 for w in english_words if w in d.lower()) >= 3:
                eng_count += 1
    check(data)

    status = 'OK' if not missing and eng_count == 0 else 'ISSUES'
    print(f'{loc}: {status} (missing={len(missing)}, english={eng_count})')

Step 9: Update Sitemap with Hreflang

Update src/app/sitemap.ts to include hreflang alternates:

import { MetadataRoute } from 'next'
import { locales } from '@/i18n/config'

const baseUrl = 'https://www.example.com'

function buildAlternates(path: string): Record<string, string> {
  const alternates: Record<string, string> = {}
  for (const locale of locales) {
    const prefix = locale === 'en' ? '' : `/${locale}`
    alternates[locale] = `${baseUrl}${prefix}${path}`
  }
  return alternates
}

export default function sitemap(): MetadataRoute.Sitemap {
  return pages.map((path) => ({
    url: `${baseUrl}${path}`,
    lastModified: new Date(),
    alternates: { languages: buildAlternates(path) },
  }))
}

Important: Generate one canonical URL per page with hreflang alternates, NOT one URL per locale. This prevents duplicate content in search results.

Step 10: Add Language Selector (Optional)

Add a language switcher component that uses useRouter and usePathname from @/i18n/navigation to switch locales while preserving the current path.

Step 11: Verify

  1. Build the project: npm run build — check that all static pages generate correctly
  2. Test English URLs have no prefix: https://example.com/tools
  3. Test locale URLs have prefix: https://example.com/es/tools
  4. Verify sitemap has hreflang alternates
  5. Check RTL rendering for Arabic
  6. Run the translation verification script from Step 8

Common Pitfalls

  • public/sitemap.xml conflicts with dynamic src/app/sitemap.ts in dev mode — delete the static one or rename it
  • Middleware matcher must exclude _next, api, sitemap, robots, and static asset paths
  • localePrefix: 'as-needed' is critical — it keeps default locale URLs clean for SEO continuity
  • localeDetection: false prevents unwanted redirects that break SEO and confuse users
  • Large translation files (5000+ lines per locale) can make git pushes fail — use git config http.postBuffer 524288000
  • Verify translations thoroughly — automated translation often produces mixed-language output; always verify with the English word detection script after Codex tasks complete

Locale Count Reference

  • 14 locales x N pages = 14N static pages at build time
  • Each locale JSON file is typically 2-5x the size of en.json (CJK characters, verbose languages)
  • Build time increases linearly with locale count

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Design, plan, and analyze A/B tests with statistical rigor. Use when the user asks about A/B testing, split testing, experiment design, statistical significance, sample size calculation, test duration, multivariate testing, or conversion experiments. Trigger phrases include "A/B test", "split test", "experiment", "statistical significance", "sample size", "test duration", "which version wins", "conversion experiment", "hypothesis test", "variant testing".

中文简介正在准备中。当前显示原文说明。

OpenClaudia/openclaudia-skills7222026年10月21日 更新

Build and manage an affiliate marketing program. Use when the user says "affiliate program", "affiliate marketing", "affiliate partners", "referral commissions", "affiliate network", "partner program", "affiliate tracking", or asks about creating, managing, or growing an affiliate or partner program.

中文简介正在准备中。当前显示原文说明。

OpenClaudia/openclaudia-skills7222026年10月21日 更新

Manages Ahrefs API usage in Python using `ahrefs-python` library. Use when working with SEO / marketing related tasks or with data including backlinks, keywords, domain ratings, organic traffic, site audits, rank tracking, and brand monitoring. Covers `ahrefs-python` usage including AhrefsClient / AsyncAhrefsClient, typed request/response models, error handling, and all API sections.

中文简介正在准备中。当前显示原文说明。

OpenClaudia/openclaudia-skills7222026年10月21日 更新

Generate an AI Citations Report (GEO) for a domain — which AI-search prompts cite the site across Google AI Overview and ChatGPT, plus organic-traffic context and per-article citation coverage. Use when the user asks for an 'AI citations report', 'GEO citations report', or 'which AI prompts cite <domain>'.

中文简介正在准备中。当前显示原文说明。

OpenClaudia/openclaudia-skills7222026年10月21日 更新

Research and enrich B2B leads using the Apollo.io API. Use when the user says "find leads", "prospect research", "company enrichment", "find decision makers", "B2B leads", "lead research", "enrich contacts", "find VP of marketing at", or asks about finding people at specific companies or in specific roles.

中文简介正在准备中。当前显示原文说明。

OpenClaudia/openclaudia-skills7222026年10月21日 更新

Audit a domain's backlink profile using the SemRush API. Use when the user says "audit backlinks", "check my backlinks", "backlink analysis", "link profile", "toxic links", "disavow", "link building opportunities", "referring domains", "anchor text", or asks about a site's link authority.

中文简介正在准备中。当前显示原文说明。

OpenClaudia/openclaudia-skills7222026年10月21日 更新

查看 OpenClaudia 的全部技能

报告此技能的问题