NĂ€in rakennat modernin i18n AI-verkkosovelluksen vuonna 2026
Kattava opas monikielisten web-sovellusten rakentamiseen Linguin ja AI-kÀÀnnösten avulla. Automatisoi tuki 17 kielelle kÀyttÀen Next.js:ÀÀ, Claudea ja T3 Turboa.

Title: Kuule, meidÀn tÀytyy puhua i18n:stÀ vuonna 2026
Excerpt: Useimmat i18n-tutoriaalit ovat jĂ€mĂ€htĂ€neet menneisyyteen. TĂ€ssĂ€ artikkelissa nĂ€ytĂ€n, kuinka rakensimme tuotantotason verkkosovelluksen, joka puhuu sujuvasti 17 kieltĂ€ kĂ€yttĂ€en Next.js:ÀÀ, Lingui:ta ja tekoĂ€lyĂ€ â murto-osalla perinteisistĂ€ kustannuksista.
Content: Kuule, meidÀn tÀytyy puhua i18n:stÀ vuonna 2026.
Useimmat tutoriaalit neuvovat sinua kÀÀntÀmÀÀn merkkijonot kÀsin, palkkaamaan kÀÀntÀjiÀ tai kÀyttÀmÀÀn jotain kömpelöÀ Google Translate API:a. Mutta tÀssÀ on juttu: elÀt Claude Sonnet 4.5 -aikakaudella. Miksi kÀÀnnÀt sovellustasi kuin elettÀisiin vuotta 2019?
Aion nÀyttÀÀ, kuinka rakensimme tuotantotason verkkosovelluksen, joka puhuu sujuvasti 17 kieltÀ. KÀytimme kaksiosaista i18n-arkkitehtuuria, jossa on oikeasti jÀrkeÀ:
- Lingui hoitaa erottelun (extraction), kÀÀntÀmisen (compilation) ja ajonaikaisen taikuuden
- Kustomoitu i18n-paketti, joka hyödyntÀÀ LLM:iÀ automatisoituun, kontekstitietoiseen kÀÀntÀmiseen
MeidÀn tech stack? Create T3 Turbo, jossa on Next.js, tRPC, Drizzle, Postgres, Tailwind ja AI SDK. Jos et kÀytÀ tÀtÀ vuonna 2026, meidÀn tÀytyy kÀydÀ eri keskustelu.
Rakennetaan.
Ongelma perinteisessÀ i18n:ssÀ
Perinteiset i18n-työnkulut nÀyttÀvÀt tÀltÀ:
# Erottele merkkijonot (Extract)
$ lingui extract
# ??? Hanki kÀÀnnökset jotenkin ???
# (palkkaa kÀÀntÀjiÀ, kÀytÀ hÀmÀriÀ palveluita, itke)
# KÀÀnnÀ (Compile)
$ lingui compile
Tuo keskimmÀinen vaihe? Se on painajainen. Olet joko:
- Maksamassa maltaita ihmiskÀÀntÀjistÀ (hidasta, kallista)
- KÀyttÀmÀssÀ perus kÀÀnnös-API:eja (ei ymmÀrrÀ kontekstia, kuulostaa robotilta)
- KÀÀntÀmÀssÀ kÀsin (ei skaalaudu)
Me teemme tÀmÀn paremmin.
Kaksiosainen arkkitehtuuri
TÀssÀ on meidÀn setup:
âââââââââââââââââââââââââââââââââââââââââââââââ
â Next.js App (Lingui-integraatio) â
â ââ Erottele merkkijonot macroilla â
â ââ Trans/t -komponentit koodissasi â
â ââ Ajonaikainen i18n kÀÀnnöskatalogeilla â
âââââââââââââââââââââââââââââââââââââââââââââââ
â generoi .po -tiedostot
âââââââââââââââââââââââââââââââââââââââââââââââ
â @acme/i18n Package (LLM-kÀÀnnös) â
â ââ Lukee .po -tiedostot â
â ââ KÀÀntÀÀ erissĂ€ Claude/GPT-5:llĂ€ â
â ââ Kontekstitietoinen, tuotespesifi â
â ââ Kirjoittaa kÀÀnnetyt .po -tiedostot â
âââââââââââââââââââââââââââââââââââââââââââââââ
â kÀÀntÀÀ TypeScriptiksi
âââââââââââââââââââââââââââââââââââââââââââââââ
â Compiled Message Catalogs â
â ââ Nopeat, tyypitetyt ajonaikaiset kÀÀnnökset â
âââââââââââââââââââââââââââââââââââââââââââââââ
Osa 1 (Lingui) hoitaa kehittÀjÀkokemuksen (DX). Osa 2 (Kustomoitu i18n-paketti) hoitaa kÀÀnnöstaikuuden.
Sukelletaanpa kumpaankin.

Osa 1: Lingui:n asennus Next.js:ÀÀn
Asennus
T3 Turbo -monorepossa:
# Hakemistossa apps/nextjs
pnpm add @lingui/core @lingui/react @lingui/macro
pnpm add -D @lingui/cli @lingui/swc-plugin
Lingui Config
Luo 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 kieltÀ heti kÀttelyssÀ. Koska miksi ei?
Next.js Integraatio
PÀivitÀ next.config.js kÀyttÀmÀÀn Linguin SWC-pluginia:
const linguiConfig = require("./lingui.config");
module.exports = {
experimental: {
swcPlugins: [
[
"@lingui/swc-plugin",
{
// TÀmÀ nopeuttaa buildiasi
},
],
],
},
// ... loput konfiguraatiostasi
};
Server-Side Setup
Luo 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>>();
// Luo i18n-instanssit valmiiksi kaikille kielille
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")!;
}
Miksi? Server Componenteilla ei ole React Contextia. TÀmÀ mahdollistaa palvelinpuolen kÀÀnnökset.
Client-Side Provider
Luo 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>;
}
KÀÀri sovelluksesi tiedostossa 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>
);
}
KÀÀnnösten kÀyttö koodissa
Server Componenteissa:
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`),
};
}
Client Componenteissa:
"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>
{/* Muuttujien kanssa */}
<p>{t`${credits} credits remaining`}</p>
</div>
);
}
Macro-syntaksi on AVAIN. Lingui erottelee nÀmÀ build-vaiheessa.
Osa 2: TekoÀlyllÀ toimiva kÀÀnnöspaketti
Nyt homma menee mielenkiintoiseksi.
Paketin rakenne
Luo packages/i18n/:
packages/i18n/
âââ package.json
âââ src/
â âââ translateWithLLM.ts # LLM-kÀÀnnöksen ydin
â âââ enhanceTranslations.ts # ErĂ€ajoprosessori
â âââ utils.ts # Apufunktiot
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-kÀÀnnösmoottori
TĂ€ssĂ€ on salainen ainesosa â 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, // Matala = johdonmukaisempi
});
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",
// ... jne
};
return names[locale] ?? locale;
}
Miksi tÀmÀ toimii:
- Kontekstitietoinen: LLM tietÀÀ mikÀ "acme" on
- Strukturoitu ulostulo: Zod-skeema varmistaa validin JSON:in
- Matala lÀmpötila (temperature): Johdonmukaiset kÀÀnnökset
- SÀilyttÀÀ muotoilun: HTML ja muuttujat pysyvÀt ehjinÀ
ErÀajoprosessori (Batch Processor)
Luo enhanceTranslations.ts:
import fs from "fs";
import path from "path";
import pofile from "pofile";
import { translateWithLLM } from "./translateWithLLM";
const BATCH_SIZE = 30; // KÀÀnnÀ 30 merkkijonoa kerralla
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"));
// Etsi kÀÀntÀmÀttömÀt kohdat
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}...`);
// Prosessoi erissÀ
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);
// PÀivitÀ PO-tiedosto
translations.forEach((translation, index) => {
const item = batch[index];
if (item) {
item.msgstr = [translation.msgstr];
}
});
console.log(` ${i + batch.length}/${untranslated.length} translated`);
// Tallenna edistyminen
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}`);
// Jatka seuraavaan erÀÀn
}
}
console.log(`â ${locale}: Translation complete!`);
}
ErÀajo (Batch processing) estÀÀ token-rajojen ylittymisen ja sÀÀstÀÀ kustannuksia.
KÀÀnnösskripti
Luo 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() {
// Vaihe 1: Erottele merkkijonot koodista
console.log("đ Extracting strings...");
await execAsync("pnpm run lingui:extract --clean");
// Vaihe 2: AutomaattikÀÀnnÀ puuttuvat merkkijonot
console.log("\nđ€ Translating with AI...");
const catalogPath = "./src/locales";
for (const locale of LOCALES) {
await enhanceTranslations(locale, catalogPath);
}
// Vaihe 3: KÀÀnnÀ TypeScriptiksi
console.log("\n⥠Compiling catalogs...");
await execAsync("npx lingui compile --typescript");
console.log("\nâ
Done! All translations updated.");
}
main().catch(console.error);
LisÀÀ package.json -tiedostoon:
{
"scripts": {
"i18n": "tsx script/i18n.ts",
"lingui:extract": "lingui extract",
"lingui:compile": "lingui compile --typescript"
}
}
i18n-putken ajaminen
# Yksi komento hallitsemaan kaikkia
$ 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.
SiinĂ€ se. LisÀÀ uusi merkkijono koodiisi, aja pnpm i18n, boom â kÀÀnnetty 17 kielelle.

Kielen vaihtaminen (Locale Switching)
ĂlĂ€ unohda kĂ€yttĂ€jĂ€kokemusta (UX). TĂ€ssĂ€ on kielenvalitsin:
"use client";
import { useLocaleSwitcher } from "@/hooks/useLocaleSwitcher";
import { useLocale } from "@/hooks/useLocale";
const LOCALES = {
en: "English",
zh_CN: "çźäœäžæ",
zh_TW: "çčé«äžæ",
ja: "æ„æŹèȘ",
ko: "íê”ìŽ",
// ... jne
};
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>
);
}
Hookin toteutus:
// hooks/useLocaleSwitcher.tsx
"use client";
import { setUserLocale } from "@/utils/i18n/localeDetection";
export function useLocaleSwitcher() {
const switchLocale = (locale: string) => {
setUserLocale(locale);
window.location.reload(); // Pakota uudelleenlataus kielen vaihtamiseksi
};
return { switchLocale };
}
Tallenna preferenssi evÀsteeseen:
// 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 vuosi
});
}
export function getLocale(): string {
const cookieStore = cookies();
return cookieStore.get("NEXT_LOCALE")?.value ?? "en";
}
EdistynyttÀ: Tyypitetyt kÀÀnnökset
Haluatko tyyppiturvallisuutta? Lingui hoitaa homman:
// TÀmÀn sijaan:
t`Hello ${name}`
// KÀytÀ msg descriptoria:
import { msg } from "@lingui/core/macro";
const greeting = msg`Hello ${name}`;
const translated = i18n._(greeting);
IDE:si tÀydentÀÀ kÀÀnnösavaimet automaattisesti. Kaunista.
Suorituskykyyn liittyvÀt huomiot
1. KÀÀnnÀ build-vaiheessa (Compile at Build Time)
Lingui kÀÀntÀÀ kÀÀnnökset minifikoiduksi JSON:ksi. Ei ajonaikaista jÀsennyskuormaa.
// KÀÀnnetty output (minifikoitu):
export const messages = JSON.parse('{"ICt8/V":["è§éą"],"..."}');
2. Esilataa palvelinkatalogit
Lataa kaikki katalogit kerran kÀynnistyksen yhteydessÀ (katso appRouterI18n.ts yllÀ). Ei tiedosto-I/O:ta jokaisella pyynnöllÀ.
3. Client Bundle -koko
Toimita asiakkaalle vain aktiivinen kieli:
<LinguiClientProvider
locale={locale}
messages={allMessages[locale]} // Vain yksi kieli
>
4. LLM-kustannusten optimointi
- ErÀkÀÀnnökset: 30 merkkijonoa per API-kutsu
- VĂ€limuisti: ĂlĂ€ kÀÀnnĂ€ muuttumattomia merkkijonoja uudelleen
- KÀytÀ halvempia malleja: GPT-4o-mini vÀhemmÀn kriittisille kielille
MeidÀn kustannukset? ~$2-3 dollaria 800+ merkkijonosta à 16 kieltÀ. SenttejÀ verrattuna ihmiskÀÀntÀjiin.
Koko Tech Stackin integraatio
Katsotaanpa, miten tÀmÀ toimii yhteen muun T3 Turbon kanssa:
tRPC ja 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 }) => {
// Virheet voidaan myös kÀÀntÀÀ!
if (!ctx.session?.user) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: ctx.i18n._(msg`You must be logged in`),
});
}
// ... tilauslogiikka
}),
});
VÀlitÀ i18n-instanssi kontekstin kautta:
// 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,
};
};
Tietokanta ja Drizzle
Tallenna kÀyttÀjÀn kielivalinta:
// 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"),
// ... muut kentÀt
});
AI SDK Integraatio
KÀÀnnÀ tekoÀlyn vastaukset lennosta:
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 };
}
Parhaat kÀytÀnnöt, jotka opimme
1. KÀytÀ aina Macroja
// â Huono: Ajonaikainen kÀÀnnös (ei erotella)
const text = t("Hello world");
// â
HyvÀ: Macro (erotellaan build-vaiheessa)
const text = t`Hello world`;
2. Konteksti on kaikki kaikessa
LisÀÀ kommentteja kÀÀntÀjille:
// i18n: This appears in the pricing table header
<Trans>Monthly</Trans>
// i18n: Button to submit payment form
<button>{t`Subscribe Now`}</button>
Lingui erottelee nÀmÀ kÀÀntÀjÀn muistiinpanoiksi.
3. Hoida monikot oikein
import { Plural } from "@lingui/react/macro";
<Plural
value={count}
one="# credit remaining"
other="# credits remaining"
/>
Eri kielillÀ on eri monikkosÀÀnnöt. Lingui hoitaa tÀmÀn.
4. PÀivÀmÀÀrÀ/Numero -muotoilu
KÀytÀ Intl API:a:
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-tuki
Arabian kielelle, hoida suunta (direction):
export default function RootLayout({ children }) {
const locale = getLocale();
const direction = locale === "ar" ? "rtl" : "ltr";
return (
<html lang={locale} dir={direction}>
<body>{children}</body>
</html>
);
}
LisÀÀ Tailwind-konfiguraatioon:
module.exports = {
plugins: [
require('tailwindcss-rtl'),
],
};
KÀytÀ suuntaan perustuvia luokkia:
<div className="ms-4"> {/* margin-start, toimii sekÀ LTR/RTL */}
Deployment-tarkistuslista
Ennen kuin julkaiset:
- Aja
pnpm i18nvarmistaaksesi, ettÀ kaikki kÀÀnnökset ovat ajan tasalla - Testaa jokainen kieli tuotantotilassa
- Varmista kieli-evÀsteen sÀilyminen
- Tarkista RTL-asettelu arabian kielelle
- Testaa kielenvalitsimen UX
- LisÀÀ hreflang-tagit SEO:ta varten
- Aseta kieleen perustuva reititys tarvittaessa
- Seuraa LLM-kÀÀnnöskustannuksia
Tulokset
TÀmÀn jÀrjestelmÀn kÀyttöönoton jÀlkeen:
- 17 kieltÀ tuettuna heti kÀttelyssÀ
- ~850 merkkijonoa kÀÀnnetty automaattisesti
- $2-3 kokonaiskustannus tÀydelle kÀÀnnökselle
- 2 minuutin pÀivityssykli uusia merkkijonoja lisÀtessÀ
- Nolla manuaalista kÀÀnnöstyötÀ
- Kontekstitietoiset, korkealaatuiset kÀÀnnökset
Verrataanpa tÀtÀ:
- IhmiskÀÀntÀjÀt: $0.10-0.30 per sana = $1,000+
- Perinteiset palvelut: YhÀ kalliita, yhÀ hitaita
- Manuaalinen työ: Ei skaalaudu
Miksi tÀllÀ on vÀliÀ vuonna 2026
Kuule, verkko on globaali. Jos toimitat sovelluksesi vain englanniksi vuonna 2026, jÀtÀt 90% maailmasta ulkopuolelle.
Mutta perinteinen i18n on tuskallista. TÀmÀ lÀhestymistapa tekee siitÀ triviaalia:
- Kirjoita koodia Trans/t -macroilla (vie 2 sekuntia)
- Aja
pnpm i18n(automatisoitu) - Toimita maailmalle (tuottoa)
YhdistelmÀ Linguin kehittÀjÀkokemusta + LLM-pohjaisia kÀÀnnöksiÀ on pelin muuttaja. Saat:
- Tyypitetyt kÀÀnnökset
- Nollakuormituksen ajonaikana
- Automaattisen erottelun
- Kontekstitietoiset AI-kÀÀnnökset
- SenttejÀ per kieli
- Skaalautuu loputtomiin
Menossa pidemmÀlle
Haluatko viedÀ homman seuraavalle tasolle? Kokeile:
Dynaamisen sisÀllön kÀÀntÀminen
Tallenna kÀÀnnökset tietokantaasi:
// 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"),
// ... jne
});
AutomaattikÀÀnnÀ tallennuksen yhteydessÀ:
import { translateWithLLM } from "@acme/i18n";
export const blogRouter = createTRPCRouter({
create: protectedProcedure
.input(z.object({ title: z.string() }))
.mutation(async ({ input }) => {
// KÀÀnnÀ kaikille kielille
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),
});
}),
});
KÀyttÀjien tarjoamat kÀÀnnökset
Anna kÀyttÀjien ehdottaa parempia kÀÀnnöksiÀ:
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);
// Ilmoita yllÀpitÀjille
await sendEmail({
to: "i18n@acme.com",
subject: `New translation suggestion for ${input.locale}`,
body: `"${input.msgid}" â "${input.suggestion}"`,
});
}),
});
KÀÀnnösten A/B-testaus
Testaa mitkÀ kÀÀnnökset konvertoivat paremmin:
const variant = await abTest.getVariant("pricing-cta", locale);
const ctaText = variant === "A"
? t`Start Your Free Trial`
: t`Try acme Free`;
Koodi
Kaikki tÀmÀ on tuotantokoodia oikeasta sovelluksesta. TÀysi toteutus löytyy monorepostamme:
t3-acme-app/
âââ apps/nextjs/
â âââ lingui.config.ts
â âââ src/
â â âââ locales/ # KÀÀnnetyt katalogit
â â âââ utils/i18n/ # i18n apufunktiot
â â âââ providers/ # LinguiClientProvider
â âââ script/i18n.ts # KÀÀnnösskripti
âââ packages/i18n/
âââ src/
âââ translateWithLLM.ts
âââ enhanceTranslations.ts
âââ utils.ts
Lopuksi
Monikielisen AI-sovelluksen rakentaminen vuonna 2026 ei ole enÀÀ vaikeaa. Työkalut ovat tÀÀllÀ:
- Lingui erotteluun ja ajonaikaiseen toimintaan
- Claude/GPT kontekstitietoiseen kÀÀntÀmiseen
- T3 Turbo parhaaseen mahdolliseen kehittÀjÀkokemukseen
Lopeta tuhansien maksaminen kÀÀnnöksistÀ. Lopeta sovelluksesi rajoittaminen vain englantiin.
Rakenna globaalisti. Toimita nopeasti. KÀytÀ tekoÀlyÀ.
NĂ€in me teemme sen vuonna 2026.
KysymyksiÀ? Ongelmia? LöydÀt minut TwitteristÀ tai tsekkaa Lingui docs ja AI SDK docs.
Nyt mene ja julkaise se monikielinen sovellus. Maailma odottaa.
Jaa tÀmÀ

Feng Liu
shenjian8628@gmail.com