Loading...
Loading...
> Multi-language support for global audiences using next-intl.
Fabrk includes full internationalization support using next-intl. Ship your SaaS to users worldwide with translations for English, Spanish, French, German, Portuguese, and Japanese out of the box. Add more languages easily by creating new message files.
DESC: next-intl is already installed. If starting fresh:
1$npm install next-intl
DESC: i18n configuration in src/i18n/config.ts
1// src/i18n/config.ts2export const locales = ["en", "es", "fr", "de", "pt", "ja"] as const;3export type Locale = (typeof locales)[number];4export const defaultLocale: Locale = "en";56export const localeNames: Record<Locale, string> = {7 en: "English",8 es: "Español",9 fr: "Français",10 de: "Deutsch",11 pt: "Português",12 ja: "日本語",13};1415export const localeFlags: Record<Locale, string> = {16 en: "🇺🇸",17 es: "🇪🇸",18 fr: "🇫🇷",19 de: "🇩🇪",20 pt: "🇧🇷",21 ja: "🇯🇵",22};
DESC: Server-side message loading
1// src/i18n/request.ts2import { getRequestConfig } from "next-intl/server";34export default getRequestConfig(async ({ locale }) => ({5 messages: (await import(`./messages/${locale}.json`)).default,6}));
Translations organized by namespace
1// src/i18n/messages/en.json2{3 "common": {4 "loading": "Loading...",5 "error": "Error",6 "success": "Success",7 "save": "Save",8 "cancel": "Cancel",9 "delete": "Delete",10 "edit": "Edit",11 "create": "Create",12 "search": "Search"13 },14 "auth": {15 "signIn": "Sign In",16 "signOut": "Sign Out",17 "signUp": "Sign Up",18 "email": "Email",19 "password": "Password",20 "forgotPassword": "Forgot Password?",21 "continueWithGoogle": "Continue with Google"22 },23 "nav": {24 "home": "Home",25 "dashboard": "Dashboard",26 "settings": "Settings",27 "billing": "Billing"28 },29 "dashboard": {30 "welcome": "Welcome back",31 "overview": "Overview",32 "recentActivity": "Recent Activity"33 }34}
Access translations in React Server Components
1// src/app/dashboard/page.tsx2import { getTranslations } from "next-intl/server";34export default async function DashboardPage() {5 const t = await getTranslations("dashboard");67 return (8 <div>9 <h1>{t("welcome")}</h1>10 <h2>{t("overview")}</h2>11 </div>12 );13}
Access translations in client-side components
1"use client";23import { useTranslations } from "next-intl";45export function WelcomeMessage() {6 const t = useTranslations("dashboard");78 return (9 <div>10 <h1>{t("welcome")}</h1>11 </div>12 );13}
Let users change their language
1// src/components/i18n/locale-switcher.tsx2"use client";34import { useLocale } from "next-intl";5import { useRouter, usePathname } from "next/navigation";6import {7 DropdownMenu,8 DropdownMenuContent,9 DropdownMenuItem,10 DropdownMenuTrigger,11} from "@/components/ui/dropdown-menu";12import { Button } from "@/components/ui/button";13import { Globe } from "lucide-react";14import { locales, localeNames, localeFlags, type Locale } from "@/i18n/config";1516export function LocaleSwitcher() {17 const locale = useLocale() as Locale;18 const router = useRouter();19 const pathname = usePathname();2021 function handleLocaleChange(newLocale: Locale) {22 const segments = pathname.split("/");23 if (locales.includes(segments[1] as Locale)) {24 segments[1] = newLocale;25 } else {26 segments.splice(1, 0, newLocale);27 }28 router.push(segments.join("/") || "/");29 }3031 return (32 <DropdownMenu>33 <DropdownMenuTrigger asChild>34 <Button variant="ghost" size="sm" className="font-mono text-xs">35 <Globe className="mr-1 h-4 w-4" />36 {localeFlags[locale]} {locale.toUpperCase()}37 </Button>38 </DropdownMenuTrigger>39 <DropdownMenuContent align="end">40 {locales.map((loc) => (41 <DropdownMenuItem42 key={loc}43 onClick={() => handleLocaleChange(loc)}44 className={loc === locale ? "bg-primary/10" : ""}45 >46 <span className="mr-2">{localeFlags[loc]}</span>47 {localeNames[loc]}48 </DropdownMenuItem>49 ))}50 </DropdownMenuContent>51 </DropdownMenu>52 );53}
Easy to add support for more languages
1$// 1. Copy an existing message file2$cp src/i18n/messages/en.json src/i18n/messages/ko.json34$// 2. Translate the strings in ko.json56$// 3. Update config.ts7$export const locales = ["en", "es", "fr", "de", "pt", "ja", "ko"] as const;89$export const localeNames: Record<Locale, string> = {10$ // ...existing11$ ko: "한국어",12$};1314$export const localeFlags: Record<Locale, string> = {15$ // ...existing16$ ko: "🇰🇷",17$};
Messages are organized into logical namespaces:
commonShared strings (loading, save, cancel, etc.)authAuthentication (sign in, sign up, password)navNavigation labels (dashboard, settings)dashboardDashboard-specific stringssettingsSettings page stringsbillingBilling and subscription stringserrorsError messagesTranslation Tips
"greeting": "Hello, {name}!"
// Usage: t("greeting", { name: "John" })"items": "{count, plural, =0 {No items} one {# item} other {# items}}"
// Usage: t("items", { count: 5 })