Come costruire una Web App moderna AI-Powered e i18n nel 2026

Guida completa per costruire webapp multilingue con Lingui + traduzioni AI. Supporta automaticamente 17 lingue usando Next.js, Claude e T3 Turbo.

Come costruire una Web App moderna AI-Powered e i18n nel 2026
Feng LiuFeng Liu
24 gennaio 2026

Senti, dobbiamo parlare di i18n nel 2026.

La maggior parte dei tutorial ti dirร  di tradurre manualmente le stringhe, assumere traduttori o usare qualche API di Google Translate messa su alla buona. Ma ecco il punto: vivi nell'era di Claude Sonnet 4.5. Perchรฉ traduci come se fossimo nel 2019?

Sto per mostrarti come abbiamo costruito una webapp in produzione che parla fluentemente 17 lingue, utilizzando un'architettura i18n a due parti che ha davvero senso:

  1. Lingui per l'estrazione, la compilazione e la magia a runtime
  2. Un pacchetto i18n personalizzato alimentato da LLM per traduzioni automatizzate e consapevoli del contesto

Il nostro stack? Create T3 Turbo con Next.js, tRPC, Drizzle, Postgres, Tailwind e l'AI SDK. Se non stai usando questo nel 2026, dobbiamo fare un altro tipo di conversazione.

Costruiamo.


Il Problema con l'i18n Tradizionale

I workflow i18n tradizionali assomigliano a questo:

# Extract strings
$ lingui extract

# ??? Somehow get translations ???
# (hire translators, use sketchy services, cry)

# Compile
$ lingui compile

Quel passaggio intermedio? รˆ un incubo. Ti ritrovi a:

  • Pagare $$$ per traduttori umani (lento, costoso)
  • Usare API di traduzione basilari (cieche al contesto, suonano robotiche)
  • Tradurre manualmente (non scala)

Noi facciamo di meglio.


L'Architettura a Due Parti

Ecco il nostro setup:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Next.js App (Lingui Integration)          โ”‚
โ”‚  โ”œโ”€ Extract strings with macros             โ”‚
โ”‚  โ”œโ”€ Trans/t components in your code         โ”‚
โ”‚  โ””โ”€ Runtime i18n with compiled catalogs     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
              โ†“ generates .po files
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  @acme/i18n Package (LLM Translation)     โ”‚
โ”‚  โ”œโ”€ Reads .po files                         โ”‚
โ”‚  โ”œโ”€ Batch translates with Claude/GPT-5      โ”‚
โ”‚  โ”œโ”€ Context-aware, product-specific         โ”‚
โ”‚  โ””โ”€ Writes translated .po files             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
              โ†“ compiles to TypeScript
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Compiled Message Catalogs                  โ”‚
โ”‚  โ””โ”€ Fast, type-safe runtime translations    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Il Pezzo 1 (Lingui) gestisce la developer experience. Il Pezzo 2 (Pacchetto i18n Personalizzato) gestisce la magia della traduzione.

Approfondiamo entrambi.


Technical flow diagram: web UI โ†’ AI translation cloud โ†’ database, three tiers connected by arrows

Parte 1: Configurare Lingui in Next.js

Installazione

Nel tuo monorepo T3 Turbo:

# In apps/nextjs
pnpm add @lingui/core @lingui/react @lingui/macro
pnpm add -D @lingui/cli @lingui/swc-plugin

Configurazione Lingui

Crea apps/nextjs/lingui.config.ts:

import type { LinguiConfig } from "@lingui/conf";

const config: LinguiConfig = {
  locales: [
    "en", "zh_CN", "zh_TW", "ja", "ko",
    "de", "fr", "es", "pt", "ar", "it",
    "ru", "tr", "th", "id", "vi", "hi"
  ],
  sourceLocale: "en",
  fallbackLocales: {
    default: "en"
  },
  catalogs: [
    {
      path: "<rootDir>/src/locales/{locale}/messages",
      include: ["src"],
    },
  ],
};

export default config;

17 lingue pronte all'uso. Perchรฉ no?

Integrazione Next.js

Aggiorna next.config.js per usare il plugin SWC di Lingui:

const linguiConfig = require("./lingui.config");

module.exports = {
  experimental: {
    swcPlugins: [
      [
        "@lingui/swc-plugin",
        {
          // This makes your builds faster
        },
      ],
    ],
  },
  // ... rest of your config
};

Setup Server-Side

Crea src/utils/i18n/appRouterI18n.ts:

import { setupI18n } from "@lingui/core";
import { allMessages } from "./initLingui";

const locales = ["en", "zh_CN", "zh_TW", /* ... */] as const;

const instances = new Map<string, ReturnType<typeof setupI18n>>();

// Pre-create i18n instances for all locales
locales.forEach((locale) => {
  const i18n = setupI18n({
    locale,
    messages: { [locale]: allMessages[locale] },
  });
  instances.set(locale, i18n);
});

export function getI18nInstance(locale: string) {
  return instances.get(locale) ?? instances.get("en")!;
}

Perchรฉ? I Server Components non hanno il React Context. Questo ti offre traduzioni server-side.

Provider Client-Side

Crea src/providers/LinguiClientProvider.tsx:

"use client";

import { I18nProvider } from "@lingui/react";
import { setupI18n } from "@lingui/core";
import { useEffect, useState } from "react";

export function LinguiClientProvider({
  children,
  locale,
  messages
}: {
  children: React.ReactNode;
  locale: string;
  messages: any;
}) {
  const [i18n] = useState(() =>
    setupI18n({
      locale,
      messages: { [locale]: messages },
    })
  );

  useEffect(() => {
    i18n.load(locale, messages);
    i18n.activate(locale);
  }, [locale, messages, i18n]);

  return <I18nProvider i18n={i18n}>{children}</I18nProvider>;
}

Avvolgi la tua app in layout.tsx:

import { LinguiClientProvider } from "@/providers/LinguiClientProvider";
import { getLocale } from "@/utils/i18n/localeDetection";
import { allMessages } from "@/utils/i18n/initLingui";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const locale = getLocale();

  return (
    <html lang={locale}>
      <body>
        <LinguiClientProvider locale={locale} messages={allMessages[locale]}>
          {children}
        </LinguiClientProvider>
      </body>
    </html>
  );
}

Usare le Traduzioni nel Tuo Codice

Nei Server Components:

import { msg } from "@lingui/core/macro";
import { getI18nInstance } from "@/utils/i18n/appRouterI18n";

export async function generateMetadata({ params }) {
  const locale = getLocale();
  const i18n = getI18nInstance(locale);

  return {
    title: i18n._(msg`Pricing Plans | acme`),
    description: i18n._(msg`Choose the perfect plan for you`),
  };
}

Nei Client Components:

"use client";

import { Trans, useLingui } from "@lingui/react/macro";

export function PricingCard() {
  const { t } = useLingui();

  return (
    <div>
      <h1><Trans>Pricing Plans</Trans></h1>
      <p>{t`Ultimate entertainment experience`}</p>

      {/* With variables */}
      <p>{t`${credits} credits remaining`}</p>
    </div>
  );
}

La sintassi macro รจ la CHIAVE. Lingui estrae queste stringhe durante la build.


Parte 2: Il Pacchetto di Traduzione Potenziato dall'AI

Qui รจ dove la cosa si fa interessante.

Struttura del Pacchetto

Crea packages/i18n/:

packages/i18n/
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ translateWithLLM.ts      # Core LLM translation
โ”‚   โ”œโ”€โ”€ enhanceTranslations.ts   # Batch processor
โ”‚   โ””โ”€โ”€ utils.ts                  # Helpers

package.json

{
  "name": "@acme/i18n",
  "version": "0.1.0",
  "dependencies": {
    "@acme/ai": "workspace:*",
    "openai": "^4.77.3",
    "pofile": "^1.1.4",
    "zod": "^3.23.8"
  }
}

Il Motore di Traduzione LLM

Ecco l'ingrediente segreto - translateWithLLM.ts:

import { openai } from "@ai-sdk/openai";
import { generateText } from "ai";
import { z } from "zod";

const translationSchema = z.object({
  translations: z.array(
    z.object({
      msgid: z.string(),
      msgstr: z.string(),
    })
  ),
});

export async function translateWithLLM(
  messages: Array<{ msgid: string; msgstr: string }>,
  targetLocale: string,
  options?: { model?: string }
) {
  const prompt = `You are a professional translator for acme, an AI-powered creative platform.

Translate the following strings from English to ${getLanguageName(targetLocale)}.

CONTEXT:
- acme is a platform for AI chat, image generation, and creative content
- Keep brand names unchanged (acme, Claude, etc.)
- Preserve HTML tags, variables like {count}, and placeholders
- Adapt culturally where appropriate
- Maintain tone: friendly, creative, engaging

STRINGS TO TRANSLATE:
${JSON.stringify(messages, null, 2)}

Return a JSON object with this structure:
{
  "translations": [
    { "msgid": "original", "msgstr": "translation" },
    ...
  ]
}`;

  const result = await generateText({
    model: openai(options?.model ?? "gpt-4o"),
    prompt,
    temperature: 0.3, // Lower = more consistent
  });

  const parsed = translationSchema.parse(JSON.parse(result.text));
  return parsed.translations;
}

function getLanguageName(locale: string): string {
  const names: Record<string, string> = {
    zh_CN: "Simplified Chinese",
    zh_TW: "Traditional Chinese",
    ja: "Japanese",
    ko: "Korean",
    de: "German",
    fr: "French",
    es: "Spanish",
    pt: "Portuguese",
    ar: "Arabic",
    // ... etc
  };
  return names[locale] ?? locale;
}

Perchรฉ funziona:

  • Consapevole del contesto: L'LLM sa cos'รจ "acme".
  • Output strutturato: Lo schema Zod assicura un JSON valido.
  • Bassa temperatura: Traduzioni coerenti.
  • Preserva la formattazione: HTML e variabili rimangono intatti.

Processore di Traduzione in Batch

Crea enhanceTranslations.ts:

import fs from "fs";
import path from "path";
import pofile from "pofile";
import { translateWithLLM } from "./translateWithLLM";

const BATCH_SIZE = 30; // Translate 30 strings at a time
const DELAY_MS = 1000; // Rate limiting

export async function enhanceTranslations(
  locale: string,
  catalogPath: string
) {
  const poPath = path.join(catalogPath, locale, "messages.po");
  const po = pofile.parse(fs.readFileSync(poPath, "utf-8"));

  // Find untranslated items
  const untranslated = po.items.filter(
    (item) => item.msgid && (!item.msgstr || item.msgstr[0] === "")
  );

  if (untranslated.length === 0) {
    console.log(`โœ“ ${locale}: All strings translated`);
    return;
  }

  console.log(`Translating ${untranslated.length} strings for ${locale}...`);

  // Process in batches
  for (let i = 0; i < untranslated.length; i += BATCH_SIZE) {
    const batch = untranslated.slice(i, i + BATCH_SIZE);
    const messages = batch.map((item) => ({
      msgid: item.msgid,
      msgstr: item.msgstr?.[0] ?? "",
    }));

    try {
      const translations = await translateWithLLM(messages, locale);

      // Update PO file
      translations.forEach((translation, index) => {
        const item = batch[index];
        if (item) {
          item.msgstr = [translation.msgstr];
        }
      });

      console.log(`  ${i + batch.length}/${untranslated.length} translated`);

      // Save progress
      fs.writeFileSync(poPath, po.toString());

      // Rate limiting
      if (i + BATCH_SIZE < untranslated.length) {
        await new Promise((resolve) => setTimeout(resolve, DELAY_MS));
      }
    } catch (error) {
      console.error(`  Error translating batch: ${error}`);
      // Continue with next batch
    }
  }

  console.log(`โœ“ ${locale}: Translation complete!`);
}

L'elaborazione in batch previene i limiti dei token e risparmia sui costi.

Lo Script di Traduzione

Crea apps/nextjs/script/i18n.ts:

import { enhanceTranslations } from "@acme/i18n";
import { exec } from "child_process";
import { promisify } from "util";

const execAsync = promisify(exec);

const LOCALES = [
  "zh_CN", "zh_TW", "ja", "ko", "de",
  "fr", "es", "pt", "ar", "it", "ru"
];

async function main() {
  // Step 1: Extract strings from code
  console.log("๐Ÿ“ Extracting strings...");
  await execAsync("pnpm run lingui:extract --clean");

  // Step 2: Auto-translate missing strings
  console.log("\n๐Ÿค– Translating with AI...");
  const catalogPath = "./src/locales";

  for (const locale of LOCALES) {
    await enhanceTranslations(locale, catalogPath);
  }

  // Step 3: Compile to TypeScript
  console.log("\nโšก Compiling catalogs...");
  await execAsync("npx lingui compile --typescript");

  console.log("\nโœ… Done! All translations updated.");
}

main().catch(console.error);

Aggiungi a package.json:

{
  "scripts": {
    "i18n": "tsx script/i18n.ts",
    "lingui:extract": "lingui extract",
    "lingui:compile": "lingui compile --typescript"
  }
}

Eseguire la tua Pipeline i18n

# One command to rule them all
$ pnpm run i18n

๐Ÿ“ Extracting strings...
Catalog statistics for src/locales/{locale}/messages:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Language โ”‚ Total count โ”‚ Missing โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ en       โ”‚         847 โ”‚       0 โ”‚
โ”‚ zh_CN    โ”‚         847 โ”‚     123 โ”‚
โ”‚ ja       โ”‚         847 โ”‚      89 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿค– Translating with AI...
Translating 123 strings for zh_CN...
  30/123 translated
  60/123 translated
  90/123 translated
  123/123 translated
โœ“ zh_CN: Translation complete!

โšก Compiling catalogs...
โœ… Done! All translations updated.

Tutto qui. Aggiungi una nuova stringa nel tuo codice, esegui pnpm i18n, boom - tradotto in 17 lingue.


Before/after split screen: left shows stressed developer with translation papers and $1000 bill

Cambio Lingua (Locale Switching)

Non dimenticare la parte UX. Ecco un selettore di lingua:

"use client";

import { useLocaleSwitcher } from "@/hooks/useLocaleSwitcher";
import { useLocale } from "@/hooks/useLocale";

const LOCALES = {
  en: "English",
  zh_CN: "็ฎ€ไฝ“ไธญๆ–‡",
  zh_TW: "็น้ซ”ไธญๆ–‡",
  ja: "ๆ—ฅๆœฌ่ชž",
  ko: "ํ•œ๊ตญ์–ด",
  // ... etc
};

export function LocaleSelector() {
  const currentLocale = useLocale();
  const { switchLocale } = useLocaleSwitcher();

  return (
    <select
      value={currentLocale}
      onChange={(e) => switchLocale(e.target.value)}
    >
      {Object.entries(LOCALES).map(([code, name]) => (
        <option key={code} value={code}>
          {name}
        </option>
      ))}
    </select>
  );
}

L'implementazione dell'hook:

// hooks/useLocaleSwitcher.tsx
"use client";

import { setUserLocale } from "@/utils/i18n/localeDetection";

export function useLocaleSwitcher() {
  const switchLocale = (locale: string) => {
    setUserLocale(locale);
    window.location.reload(); // Force reload to apply locale
  };

  return { switchLocale };
}

Salva la preferenza in un cookie:

// utils/i18n/localeDetection.ts
import { cookies } from "next/headers";

export function setUserLocale(locale: string) {
  cookies().set("NEXT_LOCALE", locale, {
    maxAge: 365 * 24 * 60 * 60, // 1 year
  });
}

export function getLocale(): string {
  const cookieStore = cookies();
  return cookieStore.get("NEXT_LOCALE")?.value ?? "en";
}

Avanzato: Traduzioni Type-Safe

Vuoi la type safety? Lingui ti copre le spalle:

// Instead of this:
t`Hello ${name}`

// Use msg descriptor:
import { msg } from "@lingui/core/macro";

const greeting = msg`Hello ${name}`;
const translated = i18n._(greeting);

Il tuo IDE autocompleterร  le chiavi di traduzione. Bellissimo.


Considerazioni sulle Performance

1. Compilazione in fase di Build

Lingui compila le traduzioni in JSON minificato. Nessun overhead di parsing a runtime.

// Compiled output (minified):
export const messages = JSON.parse('{"ICt8/V":["่ง†้ข‘"],"..."}');

2. Pre-caricamento dei Cataloghi Server

Carica tutti i cataloghi una volta all'avvio (vedi appRouterI18n.ts sopra). Nessun I/O su file ad ogni richiesta.

3. Dimensione del Bundle Client

Invia al client solo la lingua attiva:

<LinguiClientProvider
  locale={locale}
  messages={allMessages[locale]} // Only one locale
>

4. Ottimizzazione Costi LLM

  • Traduzioni in batch: 30 stringhe per chiamata API
  • Cache delle traduzioni: Non ritradurre stringhe invariate
  • Usa modelli piรน economici: GPT-4o-mini per lingue non critiche

Il nostro costo? ~$2-3 per 800+ stringhe ร— 16 lingue. Spiccioli rispetto ai traduttori umani.


L'Integrazione Full Stack

Vediamo come questo si integra con il resto di T3 Turbo:

tRPC con i18n

// server/api/routers/user.ts
import { createTRPCRouter, publicProcedure } from "../trpc";
import { msg } from "@lingui/core/macro";

export const userRouter = createTRPCRouter({
  subscribe: publicProcedure
    .mutation(async ({ ctx }) => {
      // Errors can be translated too!
      if (!ctx.session?.user) {
        throw new TRPCError({
          code: "UNAUTHORIZED",
          message: ctx.i18n._(msg`You must be logged in`),
        });
      }

      // ... subscription logic
    }),
});

Passa l'istanza i18n tramite il contesto:

// server/api/trpc.ts
import { getI18nInstance } from "@/utils/i18n/appRouterI18n";

export const createTRPCContext = async (opts: CreateNextContextOptions) => {
  const locale = getLocale();
  const i18n = getI18nInstance(locale);

  return {
    session: await getServerAuthSession(),
    i18n,
    locale,
  };
};

Database con Drizzle

Salva la preferenza della lingua dell'utente:

// packages/db/schema/user.ts
import { pgTable, text, varchar } from "drizzle-orm/pg-core";

export const users = pgTable("user", {
  id: varchar("id", { length: 255 }).primaryKey(),
  locale: varchar("locale", { length: 10 }).default("en"),
  // ... other fields
});

Integrazione AI SDK

Traduci le risposte dell'AI al volo:

import { openai } from "@ai-sdk/openai";
import { generateText } from "ai";
import { useLingui } from "@lingui/react/macro";

export function useAIChat() {
  const { i18n } = useLingui();

  const chat = async (prompt: string) => {
    const systemPrompt = i18n._(msg`You are a helpful AI assistant for acme.`);

    return generateText({
      model: openai("gpt-4"),
      messages: [
        { role: "system", content: systemPrompt },
        { role: "user", content: prompt },
      ],
    });
  };

  return { chat };
}

Best Practices che Abbiamo Imparato

1. Usa Sempre le Macro

// โŒ Bad: Runtime translation (not extracted)
const text = t("Hello world");

// โœ… Good: Macro (extracted at build time)
const text = t`Hello world`;

2. Il Contesto รจ Tutto

Aggiungi commenti per i traduttori:

// i18n: This appears in the pricing table header
<Trans>Monthly</Trans>

// i18n: Button to submit payment form
<button>{t`Subscribe Now`}</button>

Lingui estrae questi come note per il traduttore.

3. Gestisci Correttamente i Plurali

import { Plural } from "@lingui/react/macro";

<Plural
  value={count}
  one="# credit remaining"
  other="# credits remaining"
/>

Lingue diverse hanno regole diverse per i plurali. Lingui se ne occupa.

4. Formattazione Data/Numero

Usa le API Intl:

const date = new Intl.DateTimeFormat(locale, {
  dateStyle: "long",
}).format(new Date());

const price = new Intl.NumberFormat(locale, {
  style: "currency",
  currency: "USD",
}).format(29.99);

5. Supporto RTL

Per l'Arabo, gestisci la direzione:

export default function RootLayout({ children }) {
  const locale = getLocale();
  const direction = locale === "ar" ? "rtl" : "ltr";

  return (
    <html lang={locale} dir={direction}>
      <body>{children}</body>
    </html>
  );
}

Aggiungi alla config di Tailwind:

module.exports = {
  plugins: [
    require('tailwindcss-rtl'),
  ],
};

Usa classi direzionali:

<div className="ms-4"> {/* margin-start, works for both LTR/RTL */}

Checklist di Deployment

Prima di shippare:

  • Esegui pnpm i18n per assicurarti che tutte le traduzioni siano aggiornate
  • Testa ogni lingua in modalitร  produzione
  • Verifica la persistenza del cookie della lingua
  • Controlla il layout RTL per l'Arabo
  • Testa la UX del selettore di lingua
  • Aggiungi i tag hreflang per la SEO
  • Imposta il routing basato sulla lingua se necessario
  • Monitora i costi di traduzione LLM

I Risultati

Dopo aver implementato questo sistema:

  • 17 lingue supportate pronte all'uso
  • ~850 stringhe tradotte automaticamente
  • $2-3 costo totale per la traduzione completa
  • Ciclo di aggiornamento di 2 minuti quando si aggiungono nuove stringhe
  • Zero lavoro di traduzione manuale
  • Traduzioni di alta qualitร , consapevoli del contesto

Confrontalo con:

  • Traduttori umani: $0.10-0.30 per parola = $1,000+
  • Servizi tradizionali: Ancora costosi, ancora lenti
  • Lavoro manuale: Non scala

Perchรฉ Questo รจ Importante nel 2026

Senti, il web รจ globale. Se nel 2026 shippi solo in inglese, stai lasciando indietro il 90% del mondo.

Ma l'i18n tradizionale รจ doloroso. Questo approccio lo rende banale:

  1. Scrivi codice con le macro Trans/t (ci vogliono 2 secondi)
  2. Esegui pnpm i18n (automatizzato)
  3. Shippa al mondo (profitto)

La combinazione della developer experience di Lingui + traduzioni potenziate da LLM cambia le regole del gioco. Ottieni:

  • Traduzioni type-safe
  • Overhead a runtime pari a zero
  • Estrazione automatica
  • Traduzioni AI consapevoli del contesto
  • Spiccioli per lingua
  • Scala all'infinito

Andare Oltre

Vuoi salire di livello? Prova:

Traduzione di Contenuti Dinamici

Salva le traduzioni nel tuo database:

// packages/db/schema/content.ts
export const blogPosts = pgTable("blog_post", {
  id: varchar("id", { length: 255 }).primaryKey(),
  titleEn: text("title_en"),
  titleZhCn: text("title_zh_cn"),
  titleJa: text("title_ja"),
  // ... etc
});

Traduci automaticamente al salvataggio:

import { translateWithLLM } from "@acme/i18n";

export const blogRouter = createTRPCRouter({
  create: protectedProcedure
    .input(z.object({ title: z.string() }))
    .mutation(async ({ input }) => {
      // Translate to all languages
      const translations = await Promise.all(
        LOCALES.map(async (locale) => {
          const result = await translateWithLLM(
            [{ msgid: input.title, msgstr: "" }],
            locale
          );
          return [locale, result[0].msgstr];
        })
      );

      await db.insert(blogPosts).values({
        id: generateId(),
        titleEn: input.title,
        ...Object.fromEntries(translations),
      });
    }),
});

Traduzioni Fornite dagli Utenti

Lascia che gli utenti inviino traduzioni migliori:

export const i18nRouter = createTRPCRouter({
  suggestTranslation: publicProcedure
    .input(z.object({
      msgid: z.string(),
      locale: z.string(),
      suggestion: z.string(),
    }))
    .mutation(async ({ input }) => {
      await db.insert(translationSuggestions).values(input);

      // Notify maintainers
      await sendEmail({
        to: "i18n@acme.com",
        subject: `New translation suggestion for ${input.locale}`,
        body: `"${input.msgid}" โ†’ "${input.suggestion}"`,
      });
    }),
});

A/B Testing delle Traduzioni

Testa quali traduzioni convertono meglio:

const variant = await abTest.getVariant("pricing-cta", locale);

const ctaText = variant === "A"
  ? t`Start Your Free Trial`
  : t`Try acme Free`;

Il Codice

Tutto questo รจ codice di produzione di un'app reale. L'implementazione completa รจ nel nostro monorepo:

t3-acme-app/
โ”œโ”€โ”€ apps/nextjs/
โ”‚   โ”œโ”€โ”€ lingui.config.ts
โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”œโ”€โ”€ locales/           # Compiled catalogs
โ”‚   โ”‚   โ”œโ”€โ”€ utils/i18n/        # i18n utilities
โ”‚   โ”‚   โ””โ”€โ”€ providers/         # LinguiClientProvider
โ”‚   โ””โ”€โ”€ script/i18n.ts         # Translation script
โ””โ”€โ”€ packages/i18n/
    โ””โ”€โ”€ src/
        โ”œโ”€โ”€ translateWithLLM.ts
        โ”œโ”€โ”€ enhanceTranslations.ts
        โ””โ”€โ”€ utils.ts

Pensieri Finali

Costruire un'app AI multilingue nel 2026 non รจ piรน difficile. Gli strumenti sono qui:

  • Lingui per estrazione e runtime
  • Claude/GPT per traduzioni consapevoli del contesto
  • T3 Turbo per la migliore DX in circolazione

Smetti di pagare migliaia di dollari per le traduzioni. Smetti di limitare la tua app all'inglese.

Costruisci globalmente. Shippa velocemente. Usa l'AI.

รˆ cosรฌ che lo facciamo nel 2026.


Domande? Problemi? Trovami su Twitter o controlla la documentazione di Lingui e la documentazione dell'AI SDK.

Ora vai e shippa quell'app multilingue. Il mondo sta aspettando.

Condividi questo

Feng Liu

Feng Liu

shenjian8628@gmail.com