AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Dev I18n

skill-christopherlouet-claude-base-dev-i18n · by christopherlouet

Internationalization (i18n) and localization (l10n) for web and mobile applications. Libraries next-intl, react-i18next, vue-i18n, formatjs, flutter_localizations, ARB. Trigger when the user wants to add multiple languages, extract strings, handle plurals, date/number formats, or when translation files are detected.

— No reviews yet
0 installs
39 views
0.0% view→install

Install

$ agentstack add skill-christopherlouet-claude-base-dev-i18n

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • ✓ Prompt-injection patterns
  • ✓ Secret / credential exfiltration
  • ✓ Dangerous shell & filesystem operations
  • ✓ Untrusted network calls
  • ✓ Known-malicious package signatures

What it can access

  • ✓ Network access No
  • ✓ Filesystem access No
  • ✓ Shell / process execution No
  • ✓ Environment & secrets No
  • ✓ Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-christopherlouet-claude-base-dev-i18n)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
● 2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Dev I18n? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Internationalization (i18n)

Choosing your lib

Web

| Lib | Stack | Strength | Avoid | |-----|-------|----------|-------| | next-intl | Next.js 13+ App Router | Server Components first, type-safe, localized routes | Pages Router projects (use next-i18next) | | react-i18next | React vanilla / SPA | Mature, large ecosystem, plugins | Heavy for SSR without effort | | formatjs (react-intl) | React | ICU MessageFormat standard | More verbose boilerplate | | vue-i18n | Vue 3 / Nuxt | Native, Composition API, lazy load | Vue-specific | | svelte-i18n / paraglide | Svelte/SvelteKit | Lean, compile-time (paraglide) | Smaller ecosystem |

Mobile

| Lib | Stack | |-----|-------| | flutter_localizations + intl | Flutter official, ARB files | | slang | Flutter alternative, type-safe, code-generation | | react-native-localize + i18next | React Native |

next-intl (Next.js App Router)

Setup

npm install next-intl
messages/
  fr.json
  en.json
app/
  [locale]/
    layout.tsx
    page.tsx
middleware.ts
i18n/
  request.ts
  routing.ts

Config

// i18n/routing.ts
import { defineRouting } from "next-intl/routing";

export const routing = defineRouting({
  locales: ["fr", "en"],
  defaultLocale: "fr",
  localePrefix: "as-needed",  // /en/about, /about (default locale)
});
// middleware.ts
import createMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";

export default createMiddleware(routing);

export const config = {
  matcher: ["/", "/(fr|en)/:path*"],
};

Server Component usage

// app/[locale]/page.tsx
import { getTranslations } from "next-intl/server";

export default async function Page() {
  const t = await getTranslations("home");
  return {t("title")};
}

Client Component usage

"use client";
import { useTranslations } from "next-intl";

export function Greeting() {
  const t = useTranslations("home");
  return {t("welcome", { name: "Alice" })};
}

Plurals (ICU)

{
  "notifications": "{count, plural, =0 {No notifications} one {# notification} other {# notifications}}"
}
t("notifications", { count: 3 });  // "3 notifications"

Date/number formatting

import { useFormatter } from "next-intl";

const format = useFormatter();
format.dateTime(new Date(), { dateStyle: "long" });  // "November 4, 2026"
format.number(1234.5, { style: "currency", currency: "EUR" });  // "€1,234.50"
format.relativeTime(date, now);  // "2 days ago"

react-i18next (SPA)

npm install react-i18next i18next i18next-browser-languagedetector
// i18n/config.ts
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";

import fr from "./locales/fr.json";
import en from "./locales/en.json";

i18n.use(LanguageDetector).use(initReactI18next).init({
  resources: { fr: { translation: fr }, en: { translation: en } },
  fallbackLng: "fr",
  interpolation: { escapeValue: false },
});
import { useTranslation } from "react-i18next";

function Welcome() {
  const { t, i18n } = useTranslation();
  return (
    <>
      {t("welcome")}
       i18n.changeLanguage("en")}>EN
    
  );
}

Flutter (flutter_localizations + intl)

# pubspec.yaml
dependencies:
  flutter_localizations:
    sdk: flutter
  intl: any

flutter:
  generate: true
# l10n.yaml
arb-dir: lib/l10n
template-arb-file: app_fr.arb
output-localization-file: app_localizations.dart
// lib/l10n/app_fr.arb
{
  "@@locale": "fr",
  "welcome": "Bienvenue",
  "notifications": "{count, plural, =0{Aucune notification} one{{count} notification} other{{count} notifications}}",
  "@notifications": {
    "placeholders": { "count": { "type": "int" } }
  }
}
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

MaterialApp(
  localizationsDelegates: AppLocalizations.localizationsDelegates,
  supportedLocales: AppLocalizations.supportedLocales,
);

Text(AppLocalizations.of(context)!.welcome);
Text(AppLocalizations.of(context)!.notifications(count));

Vue 3 (vue-i18n)

npm install vue-i18n@9
// i18n.ts
import { createI18n } from "vue-i18n";
import fr from "./locales/fr.json";
import en from "./locales/en.json";

export const i18n = createI18n({
  legacy: false,
  locale: "fr",
  fallbackLocale: "en",
  messages: { fr, en },
});

  {{ t('welcome') }}

import { useI18n } from "vue-i18n";
const { t } = useI18n();

Best practices

File structure

Organize by namespace (not by screen):

messages/
  fr/
    common.json       # Buttons, generic messages
    errors.json       # Error messages
    auth.json         # Auth screens (shared)
    dashboard.json    # Dashboard section
  en/
    ...

Bad: 1 file per screen (duplication of shared messages).

Translation keys

{
  "dashboard": {
    "header": {
      "title": "Tableau de bord",
      "subtitle": "Vue d'ensemble"
    },
    "metrics": {
      "users": "Utilisateurs actifs",
      "revenue": "Revenu"
    }
  }
}

Conventions:

  • kebab-case or camelCase depending on the lib (camelCase for JS)
  • Hierarchical: group by feature
  • Descriptive: dashboard.metrics.users not label1
  • Typed placeholders: {count, plural, ...}, {name}

ICU MessageFormat

Universal standard for plurals, gender, select:

{count, plural,
  =0 {No items}
  one {One item}
  other {# items}
}

{gender, select,
  male {He}
  female {She}
  other {They}
}

Supported by: next-intl, formatjs, flutter intl.

Locale negotiation

// Priority order
1. User preference (stored in DB or cookie)
2. URL path (/fr/..., /en/...)
3. Accept-Language header
4. Fallback locale

String extraction

Tools to extract strings from code into translation files:

| Stack | Tool | |-------|------| | next-intl | @formatjs/cli with extract | | react-i18next | i18next-parser | | Flutter | flutter gen-l10n | | formatjs | formatjs extract |

# i18next-parser example
npx i18next-parser 'src/**/*.{ts,tsx}' --output 'public/locales/$LOCALE/$NAMESPACE.json'

Common pitfalls

| Pitfall | Prevention | |---------|------------| | String concatenation | NEVER. Use placeholders: t("hello", { name }) | | Hardcoded strings in code | Automatic extractor + lint rule (i18next/no-literal-string) | | Plurals with manual conditions | {count === 1 ? "item": "items"} doesn't work in all languages (Arabic, Russian: 6 forms) → ICU plural | | Fixed word order | Sentences change order between languages → interpolate, don't split | | Hardcoded formats | Use Intl.DateTimeFormat, Intl.NumberFormat, not date.toLocaleString() without options | | RTL forgotten | Test with Arabic/Hebrew: dir="rtl", text-align: start instead of left | | Variable length | "OK" in English → "D'accord" in French (2x longer). Flexible layout. |

RTL examples

/* Instead of: */
.card { padding-left: 16px; text-align: left; }

/* Write: */
.card { padding-inline-start: 16px; text-align: start; }

Typical workflow

1. Extract

npx i18next-parser 'src/**/*.tsx' -o 'messages/$LOCALE.json'

2. Translate

Hand off to translators via:

  • Lokalise, Crowdin, Phrase (SaaS, collaboration)
  • JSON/ARB files in git (small projects)
  • DeepL / LLM for draft, native human review mandatory

3. Validate

# Check that all locales have the same keys
npx i18next-resources-for-ts --check

# Or custom script
node scripts/check-i18n.js

4. Integrate

CI: fail if a key is missing in a locale.

Multi-language SEO

// next-intl
export async function generateMetadata({ params: { locale } }) {
  return {
    alternates: {
      canonical: `/${locale}`,
      languages: { fr: "/fr", en: "/en" },
    },
  };
}

Add hreflang in `` and sitemap.xml.

Complement with the foundation

  • Rule .claude/rules/accessibility.md: lang="fr", dir="rtl" for a11y
  • Skill growth-localization: localization strategy (markets, pricing per country)

Expected output

  1. Structure: namespaces (not by screen), descriptive hierarchical keys
  2. Plurals in ICU MessageFormat (never manual conditions)
  3. Dates/numbers via Intl or wrapper lib (never hardcoded)
  4. Extractor configured (i18next-parser, formatjs, flutter gen-l10n)
  5. CI check: validate that all locales have the same keys
  6. RTL tested if RTL language targeted (CSS logical properties)

Rules

IMPORTANT: NEVER concatenate strings to build sentences. Use placeholders.

IMPORTANT: NEVER count === 1 ? "item": "items". Use ICU plurals.

IMPORTANT: NEVER hardcode formatted dates/numbers. Use Intl.DateTimeFormat or wrapper lib.

YOU MUST extract all user-visible strings (not "Error" in code).

YOU MUST add a CI check that validates translation completeness between locales.

NEVER commit LLM translations without native speaker human review (variable quality on nuances).

NEVER use padding-left / margin-right / text-align: left in an RTL-supported app. Use logical properties.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.