Đặt câu hỏi và nhận tóm tắt tài liệu bằng cách tham chiếu trang này và nhà cung cấp AI bạn chọn
Nội dung của trang này đã được dịch bằng AI.
Xem phiên bản mới nhất của nội dung gốc bằng tiếng AnhNếu bạn có ý tưởng để cải thiện tài liệu này, vui lòng đóng góp bằng cách gửi pull request trên GitHub.
Liên kết GitHub tới tài liệuSao chép Markdown của tài liệu vào bộ nhớ tạm
next-intl VS @intlayer/next-intl | Cùng API, Bundle Khác Nhau
@intlayer/next-intl là một compat adapter: nó cung cấp API của next-intl (useTranslations, getTranslations, useLocale, t.rich(), ICU plurals, NextIntlClientProvider...) và phục vụ nó từ các từ điển được biên dịch bởi Intlayer. Mã ứng dụng không thay đổi. Bundle thì có.
Bài viết này so sánh hai cái trên cùng một ứng dụng Next.js, được xây dựng một lần với next-intl và một lần với adapter. Các con số này đến từ Benchmark Bloom, một bộ mã nguồn mở ghi lại những gì trình duyệt thực sự tải xuống. Nếu bạn muốn so sánh next-intl vs Intlayer dưới dạng các thư viện, hãy đọc next-intl vs Intlayer. Cái này là về những gì adapter thay đổi khi bạn giữ nguyên các component của mình.
tl;dr: Trên cùng một ứng dụng Next.js, việc thay thếnext-intlbằng@intlayer/next-intlđã giảm JavaScript trên mỗi trang từ 153.6 KB xuống 147.5 KB gzip, thành phần trung bình từ 21.8 KB xuống 8.1 KB, rò rỉ chuỗi trang nước ngoài từ ~90% xuống 0%, và hydration từ 14.7 ms xuống 12.8 ms, mà không cần chỉnh sửa thành phần nào. Trên TanStack Start, equivalentuse-intl(@intlayer/use-intl) đã giảm các thành phần từ 76-87 KB xuống 9-11 KB và chuyển đổi locale từ 7-21 ms xuống 4-9 ms. Adapter tiêu tốn 8.0 KB runtime so với 14.7 KB chonext-intlvà 5.5 KB chonext-intlayergốc. Navigation và middleware được triển khai lại trên cấu hình định tuyến của Intlayer;pathnamesđược địa phương hóa là tính năng duy nhất không được chuyển qua.
@intlayer/next-intl là gì
next-intl là một runtime: getRequestConfig tải một messages/{locale}.json cho mỗi request, NextIntlClientProvider gửi nó đến client, và useTranslations("about") đọc các keys từ object đó tại thời điểm render. Mọi tối ưu hóa (namespaces, pick(messages, [...]) cho mỗi page, lazy loading) đều phải được bạn viết.
@intlayer/next-intl giữ lại phần đầu và phần cuối của chuỗi đó và thay thế phần giữa. Các components của bạn vẫn gọi useTranslations("about"); những gì họ nhận được đến từ một dictionary Intlayer được biên dịch tại build time, được phạm vi hóa cho component đó, chỉ trong locale hoạt động.
Ba cơ chế làm cho nó hoạt động:
- Import aliasing.
createNextIntlPlugin()từ@intlayer/next-intl/pluginbao bọcwithIntlayervà thêm các alias Webpack / Turbopack đểnext-intl,next-intl/server,next-intl/navigationvànext-intl/middlewaređược phân giải thành@intlayer/next-intl. Không có import nào trong codebase của bạn bị đổi tên. - JSON as source of truth. Plugin
syncJSONđọcmessages/{locale}.jsonhiện tại của bạn, tách các khóa cấp cao nhất thành một dictionary cho mỗi namespace, và ghi lại các bản dịch vào cùng các tệp khi CLI hoặc CMS cập nhật chúng. Quy trình làm việc của các dịch giả của bạn không bị thay đổi. - Call-site binding. Lần pass tối ưu hóa Intlayer (Babel hoặc SWC) viết lại
useTranslations("about")thành một lệnh gọi nhận trực tiếp dictionaryabout. Component không còn tiếp cận một cây message toàn cục; nó tiếp cận nội dung của riêng nó.
Sao chép mã vào clipboard
Sao chép mã vào clipboard
Đó là lý do tại sao các cột component-size và page-leakage bên dưới thay đổi: một trang chỉ lấy các từ điển của các component mà nó render, và chỉ ở locale đang được phục vụ.
Những gì adapter giữ lại, bỏ qua và không thay thế
Mở bảng trong một cửa sổ bật lên để xem toàn bộ nội dung dữ liệu một cách rõ ràng
next-intl API | Với @intlayer/next-intl |
|---|---|
useTranslations("ns") / getTranslations("ns") | ✅ Được giữ lại. Được ràng buộc với từ điển ns tại thời điểm build. Các khóa được gõ dựa trên nội dung của bạn. |
getTranslations({ locale, namespace }) | ✅ Được giữ lại |
t("key", { name }), t.rich(), t.markup(), t.raw() | ✅ Được giữ lại. ICU plurals, select, selectordinal, #, {ts, date, long} chạy qua Intlayer's ICU resolver |
useLocale() / getLocale() / setRequestLocale() / setLocale | ✅ Được giữ lại |
useFormatter() | ✅ Được giữ lại. dateTime, number, relativeTime, list, dateTimeRange kết nối với native Intl |
NextIntlClientProvider | ✅ Được giữ lại. Các props messages, timeZone và now được chấp nhận nhưng bị bỏ qua (một cảnh báo cho dev sẽ thông báo cho bạn) |
getMessages() | ✅ Được giữ lại để tương thích; không còn cần thiết |
getRequestConfig() trong src/i18n.ts | ⚠️ Không cần thiết. Các từ điển được biên dịch tại thời điểm build; không có việc tải message cho từng request |
defineRouting() | ✅ Được giữ lại. Các trường bị bỏ qua (locales, defaultLocale, localePrefix) được đọc từ intlayer.config.ts |
createNavigation(), Link, redirect, usePathname, useRouter | ✅ Được giữ lại. Được triển khai lại trên cấu hình định tuyến của Intlayer; tham số routing được chấp nhận nhưng bị bỏ qua |
pathnames (tên route được bản địa hóa) | ❌ Được chấp nhận để gõ, không được nội suy. Giữ tên đường dẫn đơn giản hoặc chuyển ánh xạ đó sang rewrite của Intlayer |
createMiddleware() | ✅ Được giữ lại. Trả về proxy của Intlayer; đặt cookie NEXT_LOCALE để useLocale() và bộ chuyển đổi của bạn tiếp tục hoạt động |
NEXT_LOCALE cookie | ✅ Được đọc theo mặc định (trừ khi bạn tự cấu hình routing.storage) |
Bare useTranslations() với không có namespace | ⚠️ Hoạt động, nhưng call site không được ràng buộc: nó được giải quyết thông qua registry runtime. Hãy truyền một namespace để có được bundle gains |
Benchmark
Những gì được đo lường
Suite Benchmark Bloom xây dựng cùng một ứng dụng với mỗi setup: 10 trang (home, about, blog, careers, contact, FAQ, pricing, products, settings, team), 10 locale (en, fr, es, de, it, pt, zh, ja, ko, ru), các component giống hệt nhau và nội dung giống hệt nhau. Các trang được đo lường trong en và fr.
next-intl được xây dựng với bốn chiến lược tải, từ cách thiết lập ngây thơ (messages/{locale}.json tải toàn bộ) đến cách tối ưu nhất (một namespace cho mỗi route + pick() cho mỗi trang). Adapter được xây dựng trên các component giống như cách thiết lập ngây thơ, chỉ thay đổi next.config.ts và intlayer.config.ts. Nó không có biến thể "scoped": compiler scopes content cho mỗi component, vì vậy các hàng static và dynamic của nó đã được scoped.
Đối với mỗi build, suite ghi lại:
- Lib size: gzip size của một component trống chỉ import thư viện i18n. Chi phí cố định của runtime.
- Page JS: gzip JavaScript được tải xuống cho mỗi trang, trung bình trên tất cả các trang và locale.
- Locale leak %: tỷ lệ các chuỗi dịch được tìm thấy trong JS đã tải xuống mà người dùng không xem.
- Page leak %: tỷ lệ các chuỗi dịch được tìm thấy trong JS đã tải xuống mà người dùng không ở trên trang đó.
- Component avg: kích thước gzip trung bình của mỗi component được biên dịch riêng lẻ. Cho thấy bao nhiêu runtime i18n và catalog mà một component đơn lẻ kéo theo.
- E2E reactivity: thời gian tính theo giờ tường giữa việc chọn một locale mới và
html[lang]cập nhật trong DOM (Playwright, 5 lần lặp). - Hydration: thời lượng pha hydration của React.
Các số liệu dưới đây đến từ lần chạy ngày 2026-09-12 vớinext-intl/use-intl4.14.2 và@intlayer/*9.5.1. Ứng dụng kiểm tra được thiết kế có ý định nhỏ (một vài chục chuỗi cho mỗi locale), vì vậy tỷ lệ phần trăm rò rỉ mô tả một mô hình: chúng tăng theo nội dung của bạn trong khi chi phí runtime vẫn cố định.
Kết quả trên Next.js
Mở bảng trong một cửa sổ bật lên để xem toàn bộ nội dung dữ liệu một cách rõ ràng
| Setup | Strategy | Lib size (gz) | Page JS avg (gz) | Locale leak | Page leak | Component avg (gz) | E2E reactivity | Hydration |
|---|---|---|---|---|---|---|---|---|
| base (no i18n) | - | 0.0 KB | 141.0 KB | 0.0% | 0.0% | 0.9 KB | 13.4 ms | 11.8 ms |
next-intl | static | 14.7 KB | 153.6 KB | 4.2% | 89.8% | 21.8 KB | 16.0 ms | 14.7 ms |
next-intl | dynamic | 14.7 KB | 153.6 KB | 9.7% | 89.9% | 21.8 KB | 15.6 ms | 14.8 ms |
next-intl | scoped-static | 14.7 KB | 153.6 KB | 0.0% | 0.0% | 80.1 KB | 17.9 ms | 17.4 ms |
next-intl | scoped-dynamic | 14.7 KB | 153.6 KB | 0.0% | 0.0% | 22.9 KB | 17.8 ms | 16.8 ms |
@intlayer/next-intl | static | 8.0 KB | 147.5 KB | 0.0% | 0.0% | 8.1 KB | 14.5 ms | 12.8 ms |
@intlayer/next-intl | dynamic | 8.0 KB | 148.7 KB | 0.0% | 0.0% | 8.1 KB | 11.7 ms | 12.8 ms |
next-intlayer (native) | static | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 8.5 KB | 15.5 ms | 16.9 ms |
next-intlayer (native) | dynamic | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 6.9 KB | 15.3 ms | 15.9 ms |
Cách đọc nó
- Cùng các component, ít hơn 6 KB trên mỗi trang. Phiên bản build của adapter trên ứng dụng naive đạt 147.5 KB, thấp hơn mọi cấu hình
next-intlbao gồm cấu hình được tối ưu hóa hoàn toàn (153.6 KB). Bản thân runtime là sự khác biệt: 8.0 KB so với 14.7 KB, được trả trên mỗi trang. - Rò rỉ xuống 0% mà không cần chạm vào component. Cài đặt
next-intlngây thơ vận chuyển ~90% các chuỗi trang nước ngoài trên mỗi trang. Để đạt 0% vớinext-intlcó nghĩa là các cài đặtscoped-*: một namespace cho mỗi route, vàpick(messages, [...])trên mỗi trang. Adapter đạt 0% từ mã ngây thơ vì quá trình tối ưu hóa liên kết mỗiuseTranslations("ns")với từ điển riêng của nó. - Components co lại 2.7x. Một component biên dịch độc lập trung bình 21.8 KB với
next-intl(nó đạt đến provider và cây message) và 8.1 KB với adapter. Trong cài đặtscoped-staticcủanext-intl, con số đó tăng lên thành 80 KB, vì tệp namespace của mỗi route trở nên có thể tiếp cận từ trang chọn nó. - Hydration nhanh hơn 2 ms (12.8 vs 14.7 ms): không có message object để deserialize từ RSC payload trước khi React có thể hydrate.
- Adapter không phải là native runtime.
next-intlayernằm ở 141.3 KB, +0.3 KB so với base app, với 5.5 KB runtime. Adapter mang API surface củanext-intl(useFormatter,t.rich, ICU resolver) trên top của core của Intlayer, do đó 8.0 KB và +6 KB trên mỗi page. Đó là cây cầu, không phải đích đến.
Kết quả trên TanStack Start (use-intl)
use-intl là core framework-agnostic của next-intl. Adapter của nó, @intlayer/use-intl, tuân theo cùng design với Vite plugin (@intlayer/use-intl/plugin).
Mở bảng trong một cửa sổ bật lên để xem toàn bộ nội dung dữ liệu một cách rõ ràng
| Thiết lập | Chiến lược | Kích thước Lib (gz) | Page JS trung bình (gz) | Rò rỉ Locale | Rò rỉ Page | Component trung bình (gz) | E2E reactivity | Hydration |
|---|---|---|---|---|---|---|---|---|
| base (không i18n) | - | 0.0 KB | 111.0 KB | 0.0% | 0.0% | 0.7 KB | 8.1 ms | 21.6 ms |
use-intl | static | 14.1 KB | 179.8 KB | 50.0% | 89.8% | 76.0 KB | 6.7 ms | 15.3 ms |
use-intl | dynamic | 14.1 KB | 119.4 KB | 0.0% | 89.8% | 75.9 KB | 7.0 ms | 15.4 ms |
use-intl | scoped-static | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 20.9 ms | 24.8 ms |
use-intl | scoped-dynamic | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 13.3 ms | 25.9 ms |
@intlayer/use-intl | static | 7.3 KB | 135.8 KB | 49.7% | 0.0% | 10.9 KB | 4.2 ms | 10.5 ms |
@intlayer/use-intl | dynamic | 7.3 KB | 129.7 KB | 0.0% | 0.0% | 9.3 KB | 8.7 ms | 16.1 ms |
intlayer (native) | static | 5.0 KB | 125.8 KB | 50.0% | 0.0% | 8.1 KB | 3.2 ms | 11.5 ms |
intlayer (native) | dynamic | 5.0 KB | 118.6 KB | 0.0% | 0.0% | 6.3 KB | 3.6 ms | 14.1 ms |
Cách đọc bảng
- Bytes trên mỗi trang tương đương với
use-intlđược tối ưu hóa.@intlayer/use-intlở chế độdynamic(129.7 KB) nằm trong 1 KB củause-intl'sscoped-dynamic(128.7 KB), và cao hơn 10 KB so vớiuse-intl'sdynamicđơn giản (119.4 KB). Hàngdynamicđơn giản đó vẫn rò rỉ 90% chuỗi từ các trang khác; byte count thấp vì nội dung ứng dụng kiểm tra nhỏ. Adapter's 0% là những gì giữ nguyên khi nội dung phát triển. - Các component nhỏ hơn 7-9 lần. Các component
use-intltrung bình 76-87 KB trong mọi chiến lược, bởi vìuseTranslationsđược liên kết với toàn bộ đối tượng message của provider. Adapter trung bình 9-11 KB. - Chuyển đổi locale nhanh hơn. Các setup
use-intlđược tối ưu hóa mất 13-21 ms để cập nhậthtml[lang]; adapter mất 4-9 ms. Ít component hơn được re-render, và không có gì được lấy lại từ một message tree. staticgiữ lại mọi locale. Hàngstaticcủa adapter cho thấy 49.7% rò rỉ locale, giống như native Intlayer ở chế độstatic: tất cả locale được bundled, chỉ các từ điển của trang được bundled. Một dòng config (importMode: 'dynamic') sẽ loại bỏ nó.
Tại sao các con số thay đổi
Không có gì trong component thay đổi, vì vậy các lợi ích đều đến từ những gì useTranslations được liên kết tới.
Với next-intl, ràng buộc là provider. NextIntlClientProvider nhận toàn bộ object messages cho locale; mỗi useTranslations("about") đọc từ nó. Bundler thấy một component import một hook đọc một context, và không thể biết rằng chỉ có branch about được sử dụng. Các routes dưới đây đều chia sẻ cùng một object message, vì vậy cột page-leak đọc ~90% cho đến khi bạn tự chia nhỏ file.
Sao chép mã vào clipboard
Với @intlayer/next-intl, binding là dictionary. syncJSON chuyển messages/en.json thành một dictionary cho mỗi key cấp cao; compiler giải quyết component nào gọi useTranslations("about") và gửi nó about trực tiếp, trong locale hiện hoạt, dưới dạng một import mà bundler có thể trace và split.
Sao chép mã vào clipboard
src/i18n.ts và props messages biến mất. Mọi thứ khác giống hệt.
Migration trong ba bước
Cài đặt
bashSao chép mãSao chép mã vào clipboard
Lệnh phát hiện
next-intlvà cài đặtintlayer,next-intlayer,@intlayer/next-intlvà@intlayer/sync-json-plugin. Giữnext-intlđược cài đặt: nó là một peer dependency của adapter và cung cấp các types.Chỉ định Intlayer đến messages của bạn
intlayer.config.tsSao chép mãSao chép mã vào clipboard
messages/{locale}.jsonvẫn ở vị trí của nó. Mỗi khóa cấp cao nhất trở thành một dictionary;useTranslations("about")ánh xạ tới dictionaryabout.Bao bọc next.config.ts
next.config.tsSao chép mãSao chép mã vào clipboard
createNextIntlPlugin()kết hợpwithIntlayer(xem nội dung, biên dịch từ điển, bước tối ưu hóa) và các aliasnext-intl→@intlayer/next-intlcho Webpack và Turbopack. Build, và các số trong bảng trên là của bạn.
Những gì bạn có thể xóa sau đó
Mở bảng trong một cửa sổ bật lên để xem toàn bộ nội dung dữ liệu một cách rõ ràng
| Tệp / pattern | Lý do |
|---|---|
getRequestConfig in src/i18n.ts | Không tải tin nhắn theo yêu cầu. Chỉ giữ tệp nếu nó cũng xuất createNavigation helpers |
messages={...} on NextIntlClientProvider | Adapter đọc output được biên dịch; prop bị bỏ qua và ghi log cảnh báo trong development |
await getMessages() in layouts | Lý do tương tự |
Per-page pick(messages, [...]) | Compiler thực hiện picking, per component |
Những gì bạn nhận được ngoài bytes
- Typed keys.
useTranslations("about")được gõ chống lại compiledaboutdictionary.t("does.not.exist")là lỗi TypeScript, không phải fallback runtime. npx intlayer testlàm CI thất bại khi một locale thiếu một khóa.npx intlayer filldịch các khóa bị thiếu bằng nhà cung cấp của bạn lựa chọn (OpenAI, Anthropic, Mistral, Gemini...) sử dụng khóa của riêng bạn, và ghi kết quả trở lạimessages/{locale}.json.- Visual Editor và CMS làm việc trên các từ điển giống nhau, vì vậy những người không phải là nhà phát triển có thể chỉnh sửa
messages/fr.jsonthông qua một giao diện người dùng và tệp được cập nhật. - Di chuyển từng phần sang
.content.ts. Bất kỳ component nào cũng có thể chuyển từuseTranslations("about")sanguseIntlayer("about")với một tệp nội dung đồng vị trí, từng cái một. Các từ điển JSON và.content.tstồn tại cùng nhau và hợp nhất.
Những giới hạn cần biết trước khi bạn bắt đầu
- Routing config chuyển sang
intlayer.config.ts.createNavigation(routing)vàcreateMiddleware(routing)giữ nguyên signature của chúng nhưng bỏ qua argument: locales, default locale và prefix strategy đến từ Intlayer'sroutingconfig. Nếu bạn sử dụngnext-intl's localizedpathnames(/about→/a-propos), adapter không interpolate chúng; Intlayer'srouting.rewritebao gồm trường hợp đó nhưng đó là một thay đổi riêng biệt. - Namespace-less
useTranslations()không được bind. Optimize pass cần một namespace tĩnh để biết dictionary nào cần import. Một bare call vẫn hoạt động thông qua một runtime registry tham chiếu đến mọi dictionary, đó chính xác là sự rò rỉ mà bạn đang cố gắng loại bỏ. Truyền namespace. - Adapter không miễn phí. 8.0 KB runtime so với 5.5 KB cho
next-intlayer, và +6-7 KB cho mỗi trang so với native build. Nó chi trả cho bề mặt API củanext-intl. Nếu bạn đến điểm mà mọi component đã được chuyển sanguseIntlayer, hãy bỏ adapter. messages,timeZone,nowtrên provider bị bỏ qua. Các formatter được hỗ trợ bởiIntlnative và chỉ locale ảnh hưởng đến output của chúng; nếu bạn dựa vào một time zone bắt buộc hoặc mộtnowcố định cho các ngày ổn định hydration, hãy xử lý nó tại call site.
Khi nào nên sử dụng cái nào?
- Tiếp tục với
next-intlnếu ứng dụng của bạn nhỏ, bundle của bạn không phải là mối quan tâm, và team của bạn thoải mái sở hữu namespaces vàpick()cho mỗi trang. - Sử dụng
@intlayer/next-intlnếu bạn đang dùngnext-intlvà muốn có những lợi ích về bundle, rò rỉ và hydration, typed keys cũng như CLI / CMS tooling mà không cần viết lại toàn bộ. Đây là điểm vào được khuyến nghị cho bất kỳ codebasenext-intlhiện có nào. - Đi native (
next-intlayer) cho các dự án mới, hoặc khi adapter đã hoàn thành công việc của nó. Đây là lựa chọn nhẹ nhất trong ba cái (5.5 KB, +0.3 KB per page) và mở khóa các server components đồng bộ, các file.content.tsper-component và bộ tính năng đầy đủ.
Các so sánh liên quan
- next-intl vs Intlayer (các thư viện, cùng benchmark)
- i18next vs @intlayer/i18next (cùng chuỗi adapter)
- Lingui vs @intlayer/lingui (cùng chuỗi adapter)
- vue-i18n vs @intlayer/vue-i18n (cùng chuỗi adapter)
- Hướng dẫn di chuyển: next-intl sang Intlayer
- Tham khảo adapter tương thích: next-intl
Kết luận
@intlayer/next-intl làm một việc: nó thay đổi những gì useTranslations được liên kết đến, từ một provider chứa mọi message đến một dictionary được biên dịch cho component đó. Trên cùng một ứng dụng Next.js mà trị giá 6 KB mỗi trang, các component nhỏ hơn 2.7x, 0% leakage và 2 ms hydration, trước khi bất kỳ ai mở một file component. Navigation và middleware giữ API của họ trên cấu hình routing của Intlayer, và runtime next-intlayer native vẫn còn nhẹ hơn nữa.
Tất cả dữ liệu thô, các test apps và scripts có trong repository Benchmark Bloom. Chạy nó của bạn.
Tham khảo tài liệu 'Tại sao Intlayer?' để biết thêm chi tiết.
Bình luận
Chưa có bình luận nào. Hãy là người đầu tiên chia sẻ suy nghĩ của bạn.
