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