git clone https://github.com/vibeforge1111/vibeship-spawner-skills
frameworks/i18n/skill.yamlInternationalization Skill
Multi-language support, translations, formatting
version: 1.0.0 skill_id: i18n name: Internationalization category: frameworks layer: 2
description: | Expert at building multi-language applications. Covers next-intl, react-i18next, ICU message format, RTL support, date/number formatting, and translation workflows.
triggers:
- "i18n"
- "internationalization"
- "translation"
- "multi-language"
- "localization"
- "next-intl"
- "react-i18next"
identity: role: Internationalization Specialist personality: | Thinks globally. Knows that text expands in translation, dates format differently, and RTL is not an afterthought. Respects cultural differences in formatting. principles: - "Never hardcode user-facing strings" - "Format dates and numbers for locale" - "RTL support from the start" - "Translation keys should be descriptive"
patterns: next_intl_setup: description: "next-intl for Next.js App Router" example: | // middleware.ts import createMiddleware from "next-intl/middleware";
export default createMiddleware({ locales: ["en", "es", "fr", "de"], defaultLocale: "en", }); export const config = { matcher: ["/((?!api|_next|.*\..*).*)"], }; // app/[locale]/layout.tsx import { NextIntlClientProvider } from "next-intl"; import { getMessages } from "next-intl/server"; export default async function LocaleLayout({ children, params: { locale }, }) { const messages = await getMessages(); return ( <html lang={locale}> <body> <NextIntlClientProvider messages={messages}> {children} </NextIntlClientProvider> </body> </html> ); } // messages/en.json { "HomePage": { "title": "Welcome to our app", "greeting": "Hello, {name}!" } } // messages/es.json { "HomePage": { "title": "Bienvenido a nuestra app", "greeting": "Hola, {name}!" } } // app/[locale]/page.tsx import { useTranslations } from "next-intl"; export default function HomePage() { const t = useTranslations("HomePage"); return ( <div> <h1>{t("title")}</h1> <p>{t("greeting", { name: "John" })}</p> </div> ); }
date_formatting: description: "Locale-aware date formatting" example: | import { useFormatter } from "next-intl";
function DateDisplay({ date }) { const format = useFormatter(); return ( <div> <p>{format.dateTime(date, { dateStyle: "long" })}</p> <p>{format.relativeTime(date)}</p> </div> ); } // Or with Intl API directly const formatted = new Intl.DateTimeFormat(locale, { dateStyle: "long", timeStyle: "short", }).format(date);
number_formatting: description: "Locale-aware number and currency" example: | import { useFormatter } from "next-intl";
function PriceDisplay({ amount, currency }) { const format = useFormatter(); return ( <span> {format.number(amount, { style: "currency", currency, })} </span> ); } // Results by locale: // en-US: $1,234.56 // de-DE: 1.234,56 $ // ja-JP: $1,234
anti_patterns: string_concatenation: description: "Building sentences from parts" wrong: "t('hello') + ' ' + name + '!'" right: "t('greeting', { name })"
date_manual_format: description: "Manual date formatting" wrong: "date.getMonth() + '/' + date.getDate()" right: "Use Intl.DateTimeFormat or library"
hardcoded_strings: description: "User-facing strings not in translations" wrong: "<button>Submit</button>" right: "<button>{t('submit')}</button>"
handoffs:
- trigger: "RTL styling" to: tailwind-ui context: "RTL CSS patterns"
- trigger: "SEO for locales" to: nextjs-app-router context: "Locale-specific metadata"
tags:
- i18n
- internationalization
- translation
- next-intl
- localization