SĂ„ bygger du en modern AI-driven webbapp med i18n 2026

Komplett guide till att bygga flersprÄkiga webbappar med Lingui + AI-översÀttning. Stöd 17 sprÄk automatiskt med Next.js, Claude och T3 Turbo.

SĂ„ bygger du en modern AI-driven webbapp med i18n 2026
Feng LiuFeng Liu
24 januari 2026

Hörni, vi mÄste snacka om i18n Är 2026.

De flesta tutorials kommer sÀga Ät dig att översÀtta strÀngar manuellt, anlita översÀttare eller anvÀnda nÄgot svajigt Google Translate-API. Men grejen Àr den: du lever i Claude Sonnet 4.5-eran. Varför översÀtter du som om det vore 2019?

Jag tÀnker visa hur vi byggde en produktions-webapp som talar 17 sprÄk flytande, med hjÀlp av en tvÄdelad i18n-arkitektur som faktiskt Àr logisk:

  1. Lingui för extrahering, kompilering och runtime-magi
  2. Ett skrÀddarsytt i18n-paket drivet av LLM:er för automatiserade, kontextmedvetna översÀttningar

VÄr stack? Create T3 Turbo med Next.js, tRPC, Drizzle, Postgres, Tailwind och AI SDK. Om du inte anvÀnder detta 2026 behöver vi ta ett helt annat snack.

DÄ kör vi.


Problemet med traditionell i18n

Traditionella arbetsflöden för i18n ser ut sÄ hÀr:

# Extrahera strÀngar
$ lingui extract

# ??? FÄ tag pÄ översÀttningar pÄ nÄgot sÀtt ???
# (anlita översÀttare, anvÀnd skumma tjÀnster, grÄt en skvÀtt)

# Kompilera
$ lingui compile

Det dÀr mellansteg? Det Àr en mardröm. Antingen:

  • Betalar du $$$ för mĂ€nskliga översĂ€ttare (lĂ„ngsamt, dyrt)
  • AnvĂ€nder grundlĂ€ggande översĂ€ttnings-API:er (blinda för kontext, lĂ„ter robotaktigt)
  • ÖversĂ€tter manuellt (skalar inte)

Vi gör det bÀttre.


Den tvÄdelade arkitekturen

HÀr Àr vÄr setup:

┌─────────────────────────────────────────────┐
│  Next.js App (Lingui Integration)          │
│  ├─ Extrahera strĂ€ngar med makron           │
│  ├─ Trans/t-komponenter i din kod           │
│  └─ Runtime i18n med kompilerade kataloger  │
└─────────────────────────────────────────────┘
              ↓ genererar .po-filer
┌─────────────────────────────────────────────┐
│  @acme/i18n Package (LLM-översĂ€ttning)    │
│  ├─ LĂ€ser .po-filer                         │
│  ├─ Batch-översĂ€tter med Claude/GPT-5       │
│  ├─ Kontextmedveten, produktspecifik        │
│  └─ Skriver översatta .po-filer             │
└─────────────────────────────────────────────┘
              ↓ kompilerar till TypeScript
┌─────────────────────────────────────────────┐
│  Compiled Message Catalogs                  │
│  └─ Snabb, typsĂ€ker runtime-översĂ€ttning    │
└─────────────────────────────────────────────┘

Del 1 (Lingui) hanterar utvecklarupplevelsen (DX). Del 2 (SkrÀddarsytt i18n-paket) hanterar översÀttningsmagin.

LÄt oss dyka ner i detaljerna.


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

Del 1: SĂ€tta upp Lingui i Next.js

Installation

I ditt T3 Turbo-monorepo:

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

Lingui Config

Skapa 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 sprÄk direkt ur lÄdan. För varför inte?

Next.js Integration

Uppdatera next.config.js för att anvÀnda Linguis SWC-plugin:

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

module.exports = {
  experimental: {
    swcPlugins: [
      [
        "@lingui/swc-plugin",
        {
          // Detta gör dina byggen snabbare
        },
      ],
    ],
  },
  // ... resten av din config
};

Server-Side Setup

Skapa 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>>();

// För-skapa i18n-instanser för alla sprÄk
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")!;
}

Varför? Server Components har inte React Context. Detta ger dig översÀttningar pÄ serversidan.

Client-Side Provider

Skapa 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>;
}

Wrappa din app i 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>
  );
}

AnvÀnda översÀttningar i din kod

I 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`),
  };
}

I 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>

      {/* Med variabler */}
      <p>{t`${credits} credits remaining`}</p>
    </div>
  );
}

Makro-syntaxen Àr NYCKELN. Lingui extraherar dessa vid byggtid (build time).


Del 2: Det AI-drivna översÀttningspaketet

HÀr börjar det bli riktigt intressant.

Paketstruktur

Skapa packages/i18n/:

packages/i18n/
├── package.json
├── src/
│   ├── translateWithLLM.ts      # KĂ€rnan för LLM-översĂ€ttning
│   ├── enhanceTranslations.ts   # Batch-processor
│   └── utils.ts                  # HjĂ€lpfunktioner

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"
  }
}

LLM-översÀttningsmotorn

HĂ€r Ă€r den hemliga sĂ„sen – 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, // LĂ€gre = mer konsekvent
  });

  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",
    // ... osv
  };
  return names[locale] ?? locale;
}

Varför detta fungerar:

  • Kontextmedveten: LLM:en vet vad "acme" Ă€r.
  • Strukturerad output: Zod-schemat garanterar giltig JSON.
  • LĂ„g temperatur: Konsekventa översĂ€ttningar.
  • Bevarar formatering: HTML och variabler förblir intakta.

Batch-översÀttningsprocessor

Skapa enhanceTranslations.ts:

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

const BATCH_SIZE = 30; // ÖversĂ€tt 30 strĂ€ngar Ă„t gĂ„ngen
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"));

  // Hitta oöversatta objekt
  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}...`);

  // Processa i batcher
  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);

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

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

      // Spara framsteg
      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}`);
      // FortsÀtt med nÀsta batch
    }
  }

  console.log(`✓ ${locale}: Translation complete!`);
}

Batch-processering förhindrar att vi slÄr i taket för tokens och sparar kostnader.

ÖversĂ€ttningsskriptet

Skapa 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() {
  // Steg 1: Extrahera strÀngar frÄn kod
  console.log("📝 Extracting strings...");
  await execAsync("pnpm run lingui:extract --clean");

  // Steg 2: Auto-översÀtt saknade strÀngar
  console.log("\nđŸ€– Translating with AI...");
  const catalogPath = "./src/locales";

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

  // Steg 3: Kompilera till TypeScript
  console.log("\n⚡ Compiling catalogs...");
  await execAsync("npx lingui compile --typescript");

  console.log("\n✅ Done! All translations updated.");
}

main().catch(console.error);

LĂ€gg till i package.json:

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

Köra din i18n-pipeline

# Ett kommando för att styra dem alla
$ 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.

SĂ„ enkelt Ă€r det. LĂ€gg till en ny strĂ€ng i din kod, kör pnpm i18n, boom – översatt till 17 sprĂ„k.


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

Byta sprÄk (Locale Switching)

Glöm inte UX-biten. HÀr Àr en sprÄkvÀljare:

"use client";

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

const LOCALES = {
  en: "English",
  zh_CN: "çź€äœ“äž­æ–‡",
  zh_TW: "çčé«”äž­æ–‡",
  ja: "æ—„æœŹèȘž",
  ko: "한ꔭ얎",
  // ... osv
};

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>
  );
}

Hook-implementationen:

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

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

export function useLocaleSwitcher() {
  const switchLocale = (locale: string) => {
    setUserLocale(locale);
    window.location.reload(); // Tvinga omladdning för att applicera sprÄk
  };

  return { switchLocale };
}

Spara preferensen i en 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 Är
  });
}

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

Avancerat: TypsÀkra översÀttningar

Vill du ha typsÀkerhet? Lingui löser det:

// IstÀllet för detta:
t`Hello ${name}`

// AnvÀnd msg descriptor:
import { msg } from "@lingui/core/macro";

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

Din IDE kommer att autokomplettera översÀttningsnycklar. Vackert.


PrestandaövervÀganden

1. Kompilera vid byggtid (Build Time)

Lingui kompilerar översÀttningar till minifierad JSON. Ingen overhead för parsing vid runtime.

// Kompilerad output (minifierad):
export const messages = JSON.parse('{"ICt8/V":["è§†éą‘"],"..."}');

2. Förladda server-kataloger

Ladda alla kataloger en gÄng vid uppstart (se appRouterI18n.ts ovan). Ingen fil-I/O vid varje request.

3. Klientens bundle-storlek

Skeppa bara det aktiva sprÄket till klienten:

<LinguiClientProvider
  locale={locale}
  messages={allMessages[locale]} // Endast ett sprÄk
>

4. Kostnadsoptimering för LLM

  • Batch-översĂ€ttningar: 30 strĂ€ngar per API-anrop
  • Cacha översĂ€ttningar: ÖversĂ€tt inte om oförĂ€ndrade strĂ€ngar
  • AnvĂ€nd billigare modeller: GPT-4o-mini för icke-kritiska sprĂ„k

VĂ„r kostnad? ~$2-3 för 800+ strĂ€ngar × 16 sprĂ„k. SmĂ„pengar jĂ€mfört med mĂ€nskliga översĂ€ttare.


Integration med hela tech-stacken

LÄt oss se hur detta spelar ihop med resten av T3 Turbo:

tRPC med 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 }) => {
      // Felmeddelanden kan ocksÄ översÀttas!
      if (!ctx.session?.user) {
        throw new TRPCError({
          code: "UNAUTHORIZED",
          message: ctx.i18n._(msg`You must be logged in`),
        });
      }

      // ... prenumerationslogik
    }),
});

Skicka med i18n-instansen via context:

// 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,
  };
};

Databas med Drizzle

Spara anvÀndarens sprÄkpreferens:

// 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"),
  // ... andra fÀlt
});

AI SDK Integration

ÖversĂ€tt AI-svar "on the fly":

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 vi lÀrt oss

1. AnvÀnd alltid makron

// ❌ DĂ„ligt: Runtime-översĂ€ttning (extraheras ej)
const text = t("Hello world");

// ✅ Bra: Makro (extraheras vid byggtid)
const text = t`Hello world`;

2. Kontext Àr allt

LÀgg till kommentarer för översÀttare (eller AI:n):

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

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

Lingui extraherar dessa som anteckningar till översÀttaren.

3. Hantera plural korrekt

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

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

Olika sprÄk har olika regler för plural. Lingui hanterar det.

4. Datum/Nummer-formatering

AnvÀnd Intl API:er:

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. RTL-stöd

För arabiska, hantera textriktning:

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

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

LĂ€gg till i Tailwind config:

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

AnvÀnd riktningsklasser:

<div className="ms-4"> {/* margin-start, fungerar för bÄde LTR/RTL */}

Checklista för deployment

Innan du skeppar:

  • Kör pnpm i18n för att sĂ€kerstĂ€lla att alla översĂ€ttningar Ă€r uppdaterade
  • Testa varje sprĂ„k i produktionslĂ€ge
  • Verifiera att sprĂ„k-cookien sparas korrekt
  • Kolla RTL-layout för arabiska
  • Testa UX för sprĂ„kvĂ€ljaren
  • LĂ€gg till hreflang-taggar för SEO
  • SĂ€tt upp sprĂ„kbaserad routing om det behövs
  • Övervaka kostnader för LLM-översĂ€ttning

Resultatet

Efter att ha implementerat detta system:

  • 17 sprĂ„k stöds direkt ur lĂ„dan
  • ~850 strĂ€ngar översatta automatiskt
  • $2-3 total kostnad för fullstĂ€ndig översĂ€ttning
  • 2 minuters uppdateringscykel nĂ€r nya strĂ€ngar lĂ€ggs till
  • Noll manuellt översĂ€ttningsarbete
  • Kontextmedvetna översĂ€ttningar av hög kvalitet

JÀmför det med:

  • MĂ€nskliga översĂ€ttare: $0.10-0.30 per ord = $1,000+
  • Traditionella tjĂ€nster: Fortfarande dyrt, fortfarande lĂ„ngsamt
  • Manuellt arbete: Skalar inte

Varför detta spelar roll 2026

Hörni, webben Àr global. Om du bara skeppar pÄ engelska Är 2026 lÀmnar du 90% av vÀrlden utanför.

Men traditionell i18n Àr smÀrtsamt. Det hÀr tillvÀgagÄngssÀttet gör det busenkelt:

  1. Skriv kod med Trans/t-makron (tar 2 sekunder)
  2. Kör pnpm i18n (automatiserat)
  3. Skeppa till vÀrlden (profit)

Kombinationen av Linguis utvecklarupplevelse + LLM-drivna översÀttningar Àr en game-changer. Du fÄr:

  • TypsĂ€kra översĂ€ttningar
  • Noll overhead vid runtime
  • Automatisk extrahering
  • Kontextmedvetna AI-översĂ€ttningar
  • SmĂ„pengar per sprĂ„k
  • Skalar oĂ€ndligt

GÄ steget lÀngre

Vill du levla upp? Prova:

Dynamisk innehÄllsöversÀttning

Spara översÀttningar i din databas:

// 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"),
  // ... osv
});

Auto-översÀtt nÀr du sparar:

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

export const blogRouter = createTRPCRouter({
  create: protectedProcedure
    .input(z.object({ title: z.string() }))
    .mutation(async ({ input }) => {
      // ÖversĂ€tt till alla sprĂ„k
      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),
      });
    }),
});

AnvÀndarbidragna översÀttningar

LÄt anvÀndare skicka in bÀttre översÀttningar:

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);

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

A/B-testning av översÀttningar

Testa vilka översÀttningar som konverterar bÀst:

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

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

Koden

Allt detta Àr produktionskod frÄn en riktig app. Hela implementationen finns i vÄrt monorepo:

t3-acme-app/
├── apps/nextjs/
│   ├── lingui.config.ts
│   ├── src/
│   │   ├── locales/           # Kompilerade kataloger
│   │   ├── utils/i18n/        # i18n-verktyg
│   │   └── providers/         # LinguiClientProvider
│   └── script/i18n.ts         # ÖversĂ€ttningsskript
└── packages/i18n/
    └── src/
        ├── translateWithLLM.ts
        ├── enhanceTranslations.ts
        └── utils.ts

Slutord

Att bygga en flersprÄkig AI-app Är 2026 Àr inte svÄrt lÀngre. Verktygen finns hÀr:

  • Lingui för extrahering och runtime
  • Claude/GPT för kontextmedveten översĂ€ttning
  • T3 Turbo för bĂ€sta DX i gamet

Sluta betala tusentals dollar för översÀttningar. Sluta begrÀnsa din app till engelska.

Bygg globalt. Skeppa snabbt. AnvÀnd AI.

SÄ gör vi Är 2026.


FrÄgor? Problem? Hitta mig pÄ Twitter eller kolla in Lingui-dokumentationen och AI SDK-dokumentationen.

GÄ nu och skeppa den dÀr flersprÄkiga appen. VÀrlden vÀntar.

Dela detta

Feng Liu

Feng Liu

shenjian8628@gmail.com

SĂ„ bygger du en modern AI-driven webbapp med i18n 2026 | Feng Liu