18ways blog
Paano magdagdag ng multilingual i18n at lokalisasyon sa isang umiiral na Next.js app nang hindi sinisira ang SEO o muling nagtatayo mula sa simula.
I18n (internasyonal na pag-aangkop), l10n (lokalisasyon), suporta sa maraming wika… kahit paano mo pa ito sabihin, darating din ang panahon na kailangan mong i-update ang iyong Next.js app para suportahan ang higit sa isang wika.
Ang hamon ay hindi ang pagsasalin ng isang pangungusap. Ito ay ang pagdaragdag ng maraming wika sa isang tunay na Next.js app nang hindi nasisira ang SEO, hindi ginugulo ang iyong codebase, at hindi ka gumagawa ng problemang pang-maintenance para sa sarili mo.
Kung gusto mong dumiretso sa code, tingnan ang 18ways-next na mga halimbawa sa GitHub.
Bago ang lahat, kailangan muna nating i-install ang mga package natin. Ise-setup natin ang 18ways libs, pero gumagana rin nang maayos ang mga tool tulad ng i18next kung simple lang ang project mo at static lang ang laman nito.
npm install @18ways/next @18ways/reactGumawa ng iyong config file:
// 18ways.config.js
module.exports = {
apiKey: 'pk_dummy_demo_token',
baseLocale: 'en-GB',
router: 'app', // 'app', or 'path' depending on which Next.js router you are using
};I-wrap ang iyong Next.js config:
// next.config.js
const { withWays } = require('@18ways/next/config');
const nextConfig = {
/*
* your normal Next.js config here
*/
};
module.exports = withWays(nextConfig);Magdagdag ng root proxy para ang / ay makapag-redirect sa tamang locale:
// proxy.js
export { default, config } from '@18ways/next/proxy';Pagkatapos, magdagdag ng isang isinalokal na layout:
// app/layout.jsx
import './styles.css';
import { WaysRoot } from '@18ways/next/server';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className="next-demo-body">
<WaysRoot>{children}</WaysRoot>
</body>
</html>
);
}Tapos na tayo! Mayroon ka na ngayong:
Maaari mo na ngayong isalin ang iyong unang pahina. Ang mga tool tulad ng i18next ay mangangailangan na ilabas mo ang lahat ng iyong teksto sa mga translation key, at saka i-reference ang mga ito sa iyong code. Kung eksakto kung ano ang magiging hitsura nito ay depende sa library na pinili mo.
Kung gumagamit ka ng 18ways, maaari mo lang balutin ang tekstong gusto mong isalin sa isang <T> na bahagi:
// src/components/MyExampleComponent.jsx
import { useT, T } from '@18ways/react';
export default function MyExampleComponent() {
const t = useT();
return (
<div>
<T>Hello world!</T>
<img
src="https://example.com/image.png"
alt={t('Example image')}
/>
</div>
);
}Malamang na gusto mong payagan ang mga user na palitan ang kanilang napiling wika:
// src/components/Footer.jsx
import { T, LanguageSwitcher } from '@18ways/react';
export default function Footer() {
return (
<footer>
<T>My footer content</T>
<LanguageSwitcher />
</footer>
);
}Kapag naayos na ang mga tubo at kable ng tubig, may ilang paraan kung paano kadalasang nagkakamali ang mga proyektong ito.
Ang server-side rendering (SSR) ang nagbibigay-daan sa iyong Next.js app na mag-pre-render ng HTML sa server. Napakahalaga nito para sa SEO, at para hindi makakita ang user ng mga biglang paglitaw ng maling content o maling wika.
Kung gumagamit ka ng library tulad ng i18next, kailangan mong maging maingat na ang mga salin mo ay nai-load at napupunan habang isinasagawa ang server-side render. Maaari mo itong subukan sa pamamagitan ng pagsuri sa view-source: ng iyong page, hal. view-source:http://localhost:3000/. Dapat mo rin itong i-check sa production, para matiyak na gumagana rin ito sa iyong production build.
Kung ginagamit mo ang 18ways, huwag kang mag-alala tungkol dito. Aayusin na ito para sa’yo.
Kung gumagamit ka ng 18ways, hindi mo na kailangang alalahanin ito. Hindi kailangan ng 18ways ng mga translation key, puwede mong iwan ang text mo sa lugar nito gaya ng normal.
Maraming i18n system ang nangangailangan na hatiin mo ang code mo sa mga translation key. Mahalagang maging maingat sa kung paano mo pinapangalanan ang mga key na ito.
Malabo ang masasamang key:
// bad-keys.en-GB.js
module.exports = {
title: 'Continue',
button: 'Pay now',
label: 'Home',
};Ang mga key na iyon ay halos walang sinasabi sa mga tagasalin at developer tungkol sa kung saan lumalabas ang text.
Mas mabubuting susi ay may kasamang konteksto:
// better-keys.en-GB.js
module.exports = {
'checkout.payment.primaryButton': 'Pay now',
'checkout.payment.stepTitle': 'Complete your payment',
'account.sidebar.homeLink': 'Home',
};Iwasan din ang pagbuo ng mga key nang dynamic:
// bad-dynamic-keys.js
const key = `checkout.${status}.${buttonType}`;
const translatedText = t(key);Masisira nito ang mga tool ng IDE na sumusubok gawing mas matitiis ang mga translation key. Magiging napakahirap din nitong hanapin at linisin ang mga lumang translation key.
// better-dynamic-keys.js
const keyMap = {
success: {
primary: t('checkout.success.primary'),
default: t('checkout.success.default'),
},
error: {
primary: t('checkout.error.primary'),
default: t('checkout.error.default'),
},
};
const translatedText = keyMap[status][buttonType];Mas hindi ito DRY, pero ito ang pinakamahusay na paraan para pigilan ang mga translation key na maging hindi na kayang pamahalaan.
Mas maganda pa ang gumamit ng tool tulad ng 18ways, dahil hindi mo na kailanman kailangan ng translation keys:
// app/[lang]/checkout/page.jsx
<T>Pay now</T>Ang pagtukoy sa locale ay nangangahulugang pagpapasya kung aling wika ang dapat makita ng isang user bago siya tahasang lumipat.
Karaniwan, kinasasangkutan nito ang ilang kombinasyon ng:
Accept-Language ng browserSa plain Next.js middleware, madalas ganito ang hitsura nito:
// middleware.js
/**
* You don't need any of this if you're using 18ways
*/
import { NextResponse } from 'next/server';
const acceptedLocales = ['en-GB', 'fr-FR'];
export function middleware(request) {
const savedLocale =
request.cookies.get('preferred-locale')?.value;
const browserLocale =
request.headers
.get('accept-language')
?.split(',')[0] || 'en-GB';
const locale = acceptedLocales.includes(savedLocale)
? savedLocale
: acceptedLocales.includes(browserLocale)
? browserLocale
: 'en-GB';
if (request.nextUrl.pathname === '/') {
return NextResponse.redirect(
new URL(`/${locale}`, request.url)
);
}
return NextResponse.next();
}May ilang library na may mga helper para gawing mas madali ito, sa iba’t ibang antas. Sa 18ways, ang paunang pag-detect at ang layer ng pagre-redirect ay pinapangasiwaan na para sa iyo.
Iba’t ibang locale ang magkakaibang nagfo-format ng mga petsa, numero, at pera.
Halimbawa:
04/05/2026 ay maaaring mangahulugang ika-4 ng Mayo o Abril 5€1,999.00 at 1.999,00 € ay parehong wasto, depende sa lokalidadSa raw JavaScript, ikaw mismo ang bahala niyan gamit ang Intl:
// formatting-dates-and-currency.js
const myLocale = getCurrentLocale(); // depends on your lib
const someTimestamp = new Date('2026-04-13T09:00:00Z');
const dateLabel = new Intl.DateTimeFormat('fr-FR', {
dateStyle: 'long',
}).format(someTimestamp);
const someMoney = {
amount: 1999,
currency: 'EUR',
};
const moneyLabel = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: someMoney.currency,
}).format(someMoney.amount);
const translatedText = t(
'my.translation.key',
{ dateLabel, moneyLabel }
);Kung ginagamit mo ang 18ways, ito na ang bahala para sa iyo:
const someTimestamp = new Date('2026-04-13T09:00:00Z');
const someMoney = {
amount: 1999,
currency: 'EUR',
};
<T>My text with {{ someTimestamp }} and {{ someMoney }}</T>Huwag bumuo ng isinaling UI sa pamamagitan ng pagsasama-sama ng mga piraso:
// bad-string-joining.jsx
const clickHereText = t('click.here');
const toGetStartedText = t('to.get.started')
<p><a href="#">{clickHereText}</a> {toGetStartedText}.</p>Magkakaroon ng problema ito sa maraming wika.
Maaaring sabihin mo na “👉👉I-click dito👈👈 para magsimula” sa Ingles, pero sa Pranses, mas natural na sabihing “Pour commencer, 👉👉cliquez ici👈👈”. Sa ilang wika gaya ng Hapones, kakailanganin pa nga ang mga salita bago at pagkatapos, tulad ng “始めるには👉👉こちら👈👈をクリックしてください”.
Maaaring magbago ang estruktura ng pangungusap, kaya ang paghahati-hati nito sa mga bahagi ay lalong nagpapahirap sa paggawa ng magandang salin.
Mas mahusay ang pagsasalin ng buong mga pangungusap dahil natural na naibabalik ng mga tagasalin ang ayos ng mga salita at nakikita ang kahulugan bilang isang buo at magkakaugnay na yunit.
Kung nagta-translate ka ng JSX na ganito:
// rich-text-message.jsx
<p>
Something <strong>that we want to be bold</strong>
</p>kakailanganin mong kumonsulta sa iyong i18n library kung paano ito pamamahalaan, dahil iba-iba ang paghawak dito ng iba’t ibang mga library.
Kung gumagamit ka ng 18ways, puwede mo lang isalin ang buong JSX block gaya ng normal.
// rich-text-message.jsx
<p>
<T>Something <strong>that we want to be bold</strong></T>
</p>Hinahayaan ka ng mga variable na panatilihing buo ang pangungusap habang naglalagay pa rin ng mga value sa runtime.
Karamihan sa mga i18n library ay may suporta rito sa isang anyo o iba pa. Sa 18ways:
// app/[lang]/page.jsx
<T>Hello {{ name: 'Ada' }}</T>Ang pattern na iyon ay gumagana nang maayos para sa:
Nananatiling nababasa ang pangungusap, at nananatiling tahasan ang halaga.
Kung kailangan mo ring maisalin ang mismong variable, tiyaking ibalot ito sa t(...):
const animal = t('dog');
<T>Favourite animal: {{ animal }}</T>Nagkakaiba ang mga patakaran sa plural ayon sa wika. Ang Ingles ay may isang anyong plural (1 taon, 2 taon, atbp.). Ang Polish ay may maraming anyong plural (1 taon, 2 taon, 5 taon). Ang Hapon ay walang anyong plural sa lahat (1 年, 2 年, 3 年).
Karamihan sa mga i18n library ay nagpapahintulot sa iyo na mag-specify ng syntax na kahawig ng ICU para hawakan ang mga plural:
// app/[lang]/page.jsx
<T>
{{
unreadCount,
format:
'plural, =0{No unread messages} =1{One unread message} other{{unreadCount} unread messages}',
}}
</T>Gagana ito sa 18 paraan sa itaas, pero sa karamihan ng mga kaso, puwede mo ring gawin lang ito:
// app/[lang]/page.jsx
<T>{{ unreadCount }} unread messages</T>Ang 18ways ang bahala sa mga pangmaramihang anyo para sa iyo!
Ang pagdaragdag ng maraming wika sa isang Next.js app ay hindi kailangang maging isang proyekto ng muling pagsulat.
Kung hindi ka gumagamit ng solusyon tulad ng 18ways — tiyaking tama ang iyong routing at SSR, pagkatapos ay unti-unting ilipat ang iyong copy sa mga translation key, at siguraduhing iwasan ang mga karaniwang i18n na patibong.
Kung gumagamit ka ng 18ways, ito na ang bahalang gumawa ng lahat para sa iyo, at kailangan mo lang simulan ang pagbalot ng text mo sa mga <T> block!