Next.js Vietnamese Localization: Routing and ICU Messages
← Blog
🌏 Localization7 min read

Next.js Vietnamese Localization: Routing and ICU Messages

💡 To localize a Next.js or React app into Vietnamese: structure routes under app/[lang]/, add a proxy middleware for Accept-Language detection, load per-locale JSON messages, and configure Intl with the vi-VN locale. The key surprise: Vietnamese has one plural category in ICU MessageFormat, so plural messages simplify to a single branch.

Key takeaways

  • Use app/[lang]/ with generateStaticParams returning your supported locales (including vi) for fully static Vietnamese routes.
  • next-intl's defineRouting and createMiddleware handle locale detection, redirects, and locale-aware navigation out of the box.
  • Vietnamese (CLDR locale vi) has one plural category: other. ICU plural blocks still work, but you never need a separate "one" form in Vietnamese strings.
  • Use the JavaScript Intl API with locale vi-VN: dates render as DD/MM/YYYY, thousands separator is a period, and VND currency appends ₫ after the number with no decimal places.
  • Budget for layout testing: Vietnamese strings run 10-20% longer than English equivalents. Test button labels, tab names, and mobile navigation before launch.

What does localizing a Next.js app into Vietnamese actually require?

Localization of a Next.js app covers three distinct layers: routing (which URL serves which language), message loading (which strings appear), and locale formatting (how dates, numbers, and currency render). All three must be correct before your Vietnamese version is production-ready.

The routing layer is a Next.js concern. The message layer is handled by a library such as next-intl, next-i18next, or Lingui. The formatting layer uses the JavaScript Intl API or a higher-level wrapper. Each has a clear setup path for Vietnamese, and the three work independently, so you can adopt them incrementally.

Which locale library should you choose for a Vietnamese-ready app?

Three libraries dominate the Next.js i18n ecosystem. The right choice depends on your team's constraints and how much of the i18n surface you need to own.

  • next-intl: The current community recommendation for the App Router. Provides defineRouting, createMiddleware, createNavigation, ICU MessageFormat support, and typed translation keys. Works with both Server and Client Components. Recommended for new App Router projects.
  • next-i18next: Widely used in the Pages Router era. Still works with App Router through a compatibility layer, but the setup is heavier. A familiar choice if your team already uses react-i18next.
  • Next.js built-in dictionary pattern: The official Next.js docs show a minimal approach using getDictionary() with no third-party library. Sufficient for small apps with simple strings, but provides no ICU message formatting or pluralization.

For Vietnamese specifically, next-intl's ICU support matters because it handles plural awareness per locale at runtime, giving correct behavior for Vietnamese's single plural category without extra configuration.

How do ICU messages behave when Vietnamese has no plural forms?

English has two CLDR plural categories: one (singular) and other (all other counts). Vietnamese has one: other. All numbers map to other in Vietnamese, because Vietnamese uses noun classifiers and context rather than morphological plural marking.

In practice, this means Vietnamese never needs a dedicated singular message string. A pluralized English message such as:

{count, plural, one {# result} other {# results}}

becomes, in your Vietnamese message file:

{count, plural, other {# kết quả}}

Vietnamese speakers say "1 kết quả", "2 kết quả" using the same form, relying on the number itself for quantity. The =0 and =1 exact-match selectors are still available when you need specific zero-state messages or edge cases.

Date and time formatting uses ICU skeletons or predefined styles in next-intl. For Vietnamese, pass locale: 'vi' or locale: 'vi-VN'. The DateTimeFormat API returns DD/MM/YYYY for short dates in the Vietnamese locale.

Vietnamese date, number, and currency: how to configure Intl

The JavaScript Intl API with locale vi-VN handles Vietnamese formatting correctly without custom code. These are the patterns to wire up in your app:

  • Dates: new Intl.DateTimeFormat('vi-VN').format(date) returns DD/MM/YYYY. Use { dateStyle: 'short' } for explicit date-only output.
  • Numbers: new Intl.NumberFormat('vi-VN').format(1500000) returns 1.500.000. Period is the thousands separator; comma is the decimal separator.
  • Currency (VND): new Intl.NumberFormat('vi-VN', { style: 'currency', currency: 'VND' }).format(250000) returns 250.000 ₫. VND has no decimal subdivision.
  • Collation: Vietnamese alphabetical order differs from ASCII. Use new Intl.Collator('vi').compare for sorting dropdowns or search results in correct Vietnamese order. The Vietnamese alphabet has 29 base letters.

In next-intl, pass the locale string through the framework's formatting helpers; they call the correct Intl constructor internally. The CLDR data backing the vi locale is maintained by the Unicode Consortium's CLDR project and updated with each CLDR release.

Vietnamese strings run longer: layout and font rendering checklist

Vietnamese strings typically run 10-20% longer than their English equivalents in natural prose. UI labels, navigation items, and button text are most likely to overflow. Run through this checklist before launching your Vietnamese version:

  • Test all button labels and CTA text at mobile widths (375px and 390px viewports).
  • Check navigation tabs and breadcrumbs: Vietnamese compound nouns often expand where English uses short verbs.
  • Verify font support: Vietnamese uses combining diacritics (six tone marks plus hook and breve modifications on base letters). System fonts that correctly render UTF-8 handle this, but web font subsets built for Latin characters only may drop Vietnamese glyphs. Test with characters such as ộ, ề, ầ, ẫ, ắ.
  • Set lang="vi" on the root <html> element. Line-breaking and hyphenation rules are language-aware in modern browsers.
  • Use overflow-wrap: break-word as a safety net: Vietnamese text wraps at spaces between syllable-words, and long technical terms can overflow narrow containers.

In-house vs. specialist: the honest line for a Vietnamese-facing product

The engineering work, routing setup, and ICU message wiring are fully in-house tasks. Any developer with a day to spare can configure next-intl with a vi locale and connect a JSON message file. Machine translation plus spot-checking works well for large volumes of structured UI strings: form labels, error messages, and help documentation.

Where a native Vietnamese specialist saves real money: marketing copy, product UI tone, and domain-specific terminology. Vietnamese has formal and informal register distinctions that affect how professional a product feels to users. Sino-Vietnamese terms (vocabulary borrowed from Classical Chinese) signal expertise in finance, medicine, and law. A general machine translation engine defaults to plain Vietnamese equivalents that specialists find imprecise.

The practical split for a SaaS product entering Vietnam: use MT for the long tail of help content, and hire a human specialist for the 50-100 strings a user sees every session. For B2B products where a procurement team evaluates your Vietnamese version, have a native reviewer audit the full UI before launch.

For professional English-to-Vietnamese translation covering website and SaaS UI, the Vietnamese website localization service on this site handles both translation and technical review.

FAQ

Can I use the Next.js Pages Router i18n for Vietnamese?

Yes. The Pages Router has a built-in i18n key in next.config.js with locales and defaultLocale. Set locales: ['en', 'vi', 'zh', 'fr'] and defaultLocale: 'en'. Next.js automatically prefixes routes with the locale segment. The App Router equivalent uses a manual app/[lang]/ directory and proxy middleware, giving more control over static export behavior.

Does Vietnamese need special Unicode normalization?

Vietnamese diacritics can be encoded two ways: precomposed (NFC, a single code point per accented character) and decomposed (NFD, base character plus combining mark). Both render identically in a browser but fail string equality checks. Normalize to NFC in your message files and any string comparisons. In JavaScript: str.normalize('NFC'). This prevents invisible comparison bugs where two visually identical Vietnamese strings do not match.

Should I use 'vi' or 'vi-VN' as the locale code?

Use vi as your primary language tag. Per W3C BCP 47 guidance, keep the tag as short as possible unless you need to distinguish regional variants. For most products, vi covers all Vietnamese users. Use vi-VN only if a third-party library requires the full locale code for its data lookup.

How does Vietnamese affect the hreflang setup in a Next.js app?

The hreflang value for Vietnamese pages is vi. In next-intl, navigation helpers such as Link and redirect automatically prepend the locale to the URL. To emit hreflang tags, add them in generateMetadata() or in the root layout head. Each page should carry a self-referential alternate and alternates for every other language version. See Google Search Central for the full bidirectional linking requirement.

Is Vietnamese right-to-left or does it need special text direction handling?

Vietnamese uses the Latin script and is strictly left-to-right. No dir="rtl" or bidi CSS is needed. The only layout concern is line wrapping in narrow containers, since Vietnamese text is composed of syllable-words separated by spaces. Use overflow-wrap: break-word to handle edge cases. There is no bidi algorithm involvement for Vietnamese text.

Official Sources

Written by Dao Huy (Lucas), Vietnamese translator & localization specialist (EN · ZH · FR → Vietnamese). See translation services →

Get QuoteWhatsApp