Đặ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
i18next VS Intlayer | Đo kiểm đa ngôn ngữ (i18n) cho React & Next.js
i18next là framework i18n phổ biến nhất trong hệ sinh thái JavaScript. Thông qua react-i18next và next-i18next, nó đang vận hành phần lớn các ứng dụng React và Next.js. Intlayer là một giải pháp thay thế hiện đại dựa trên trình biên dịch và cô lập phạm vi theo từng component.
Bài viết này so sánh hai giải pháp dựa trên số liệu đo lường thực tế thay vì danh sách tính năng đơn thuần. Các số liệu được trích xuất từ Benchmark Bloom - một bộ đo lường nguồn mở xây dựng cùng một ứng dụng mẫu trên từng thư viện và ghi nhận chính xác những gì trình duyệt tải về.
Tóm tắt cốt lõi (tl;dr):i18nextlà runtime nặng nhất trong thử nghiệm benchmark: làm tăng thêm +77 KB gzip mỗi trang trên Next.js ở cấu hình cơ bản, và vẫn tăng +22 KB sau khi đã tối ưu hóa toàn diện namespace và lazy-loading. Ngược lại, Intlayer chỉ thêm vỏn vẹn +0.3 KB. Mọi cấu hìnhi18nextngoại trừ cấu hình phân tách phạm vi hoàn toàn (scoped) đều rò rỉ khoảng 90% chuỗi ký tự của các trang khác; trong khi Intlayer đạt 0% rò rỉ theo mặc định. Thao tác chuyển đổi ngôn ngữ với backend tải lười (lazy load) mất 123-185 ms vớireact-i18next, so với chỉ 3-4 ms ở Intlayer. Bộ chuyển đổi tương thích@intlayer/next-i18nextgiữ nguyên API củai18nextnhưng hạ kích thước trang từ 218.5 KB xuống còn 150.7 KB.
Tóm tắt nhanh
- i18next / react-i18next / next-i18next - Lâu đời, hệ sinh thái plugin đồ sộ và độc lập với framework. Hỗ trợ namespaces, bộ nhận diện ngôn ngữ, các dạng backend, ICU qua plugin và thẻ
<Trans>cho nội dung có cấu trúc. Dữ liệu bản dịch được tập trung tạilocales/{lng}/{ns}.json. Rất mạnh mẽ nhưng mọi tối ưu (chia nhỏ namespace, tải theo từng trang, an toàn kiểu dữ liệu) đều đòi hỏi bạn phải tự cấu hình và bảo trì thủ công. - Intlayer - Mô hình nội dung lấy component làm trung tâm. Các tệp từ điển
.content.tsnằm ngay cạnh component mà chúng phục vụ, trình biên dịch tại thời điểm build sẽ tự động thực hiện tree-shaking và lazy loading theo từng component và ngôn ngữ, tự động tạo kiểu TypeScript nghiêm ngặt từ nội dung và chặn quá trình build nếu thiếu bản dịch. Tích hợp sẵn middleware, bộ hỗ trợ SEO, Visual Editor / CMS và biên dịch hỗ trợ bởi AI.
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
Các huy hiệu cập nhật tự động. Số liệu thực tế có thể biến động theo thời gian.
So sánh tính năng chi tiết
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ính năng | Intlayer (react-intlayer / next-intlayer) | i18next (react-i18next / next-i18next) |
|---|---|---|
| Bản dịch đặt ngay cạnh component | ✅ Có, tệp .content.ts đặt cùng thư mục với component | ❌ Không, tập trung ở locales/{lng}/{ns}.json |
| Tích hợp TypeScript | ✅ Tự động tạo kiểu dữ liệu nghiêm ngặt từ nội dung | ⚠️ Cơ bản; các khóa nghiêm ngặt cần mở rộng CustomTypeOptions và gán kiểu thủ công |
| Phát hiện bản dịch còn thiếu | ✅ Báo lỗi TypeScript + lỗi/cảnh báo trong thời gian build | ⚠️ Fallback ở runtime (saveMissing, hiển thị lại chuỗi khóa) |
| Nội dung đa dạng (JSX / Markdown / Component) | ✅ Hỗ trợ trực tiếp tự nhiên | ⚠️ Sử dụng thẻ đánh chỉ mục <Trans> |
| Hỗ trợ định dạng ICU | ⚠️ Đang hoàn thiện | ⚠️ Cần bổ sung plugin (i18next-icu) |
| Xử lý số nhiều (Pluralization) | ✅ Định dạng dựa trên liệt kê (Enum) tường minh | ✅ Hậu tố _one / _other (dựa trên Intl.PluralRules) |
| Định dạng (ngày, số, tiền tệ) | ✅ useNumber, useDate, ... (tận dụng Intl gốc) | ⚠️ Sử dụng formatter nội suy hoặc gọi Intl.* thủ công |
| Định tuyến bản địa hóa và middleware | ✅ Tích hợp sẵn proxy/middleware, getMultilingualUrls | ⚠️ Không có sẵn trong core; cần tự tạo middleware hoặc dùng thư viện ngoài |
| Bộ hỗ trợ SEO (hreflang, sitemap, robots) | ✅ Cung cấp đầy đủ | ❌ Phải thiết lập thủ công |
| Component Server đồng bộ (RSC) | ✅ useIntlayer từ next-intlayer/server dùng được ở mọi Server Component con | ⚠️ Gọi getFixedT ở cấp page rồi truyền t qua Props xuống các component |
| Tree-shaking (chỉ đóng gói nội dung dùng) | ✅ Trình biên dịch tự động xử lý theo component và ngôn ngữ | ⚠️ Thủ công: chia namespace + liệt kê ns cho từng trang + cấu hình backend |
| Tải lười (Lazy loading) | ✅ importMode: 'dynamic' (chỉ một dòng cấu hình) | ✅ Thông qua các plugin backend (i18next-resources-to-backend, v.v.) |
| Dọn dẹp nội dung thừa (Purge) | ✅ Loại bỏ các từ điển không dùng đến khi build | ❌ Không hỗ trợ mặc định |
| Kiểm thử bản dịch còn thiếu (CLI / CI) | ✅ npx intlayer content test | ⚠️ Cần công cụ ngoài như i18next-parser |
| Biên dịch bằng AI | ✅ Tích hợp sẵn, dùng API key của riêng bạn | ❌ Không có (Locize là dịch vụ tính phí riêng) |
| Visual Editor / CMS | ✅ Visual Editor miễn phí + CMS tùy chọn | ❌ Không có (phụ thuộc vào Locize hoặc nền tảng ngoài) |
| Máy chủ MCP & Kỹ năng Agent | ✅ Hỗ trợ | ❌ Không hỗ trợ |
| Hệ sinh thái & Cộng đồng | ⚠️ Mới hơn nhưng tốc độ phát triển rất nhanh | ✅ Lớn nhất và lâu đời nhất |
Đo kiểm Benchmark
Những gì đã được đo lường?
Bộ công cụ Benchmark Bloom xây dựng cùng một ứng dụng giống hệt nhau cho từng thư viện: 10 trang (home, about, blog, careers, contact, FAQ, pricing, products, settings, team), 10 ngôn ngữ (en, fr, es, de, it, pt, zh, ja, ko, ru), cùng component và cùng nội dung văn bản. Dữ liệu được đo trên hai ngôn ngữ en và fr. Mỗi thư viện được đánh giá qua tối đa bốn chiến lược tải:
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
| Chiến lược | Mô tả | Trường hợp sử dụng tiêu biểu |
|---|---|---|
| static | Gom tất cả ngôn ngữ và trang vào một gói duy nhất (resources nhúng thẳng vào init()) | Làm bản thử nghiệm nhanh, code sinh bởi AI |
| dynamic | Chỉ tải ngôn ngữ hiện tại qua backend, nhưng tải toàn bộ các namespace cùng lúc | Đa số các ứng dụng thông thường |
| scoped-static | Mỗi route một namespace riêng, nhưng đóng gói tất cả vào bundle ban đầu | Rất hiếm khi sử dụng |
| scoped-dynamic | Namespace theo route + tải lười qua backend. Chỉ tải trang hiện tại và ngôn ngữ hiện tại | Ứng dụng có giới hạn nghiêm ngặt về hiệu năng |
Intlayer không cần biến thể "scoped": Trình biên dịch tự động giới hạn phạm vi nội dung ở mức độ từng component, vì vậy hai dòng static và dynamic của Intlayer đã được tối ưu hóa sẵn.
Các chỉ số đo lường:
- Lib size: Dung lượng gzip của một component rỗng chỉ import thư viện i18n (chi phí cố định của runtime).
- Page JS: Dung lượng gzip JavaScript trung bình tải về mỗi trang (tính chung trên mọi trang và ngôn ngữ).
- Locale leak %: Tỷ lệ chuỗi dịch trong JS tải về thuộc về các ngôn ngữ mà người dùng không xem.
- Page leak %: Tỷ lệ chuỗi dịch trong JS tải về thuộc về các trang mà người dùng không mở.
- Component avg: Kích thước gzip trung bình của từng component khi được biên dịch cô lập.
- E2E reactivity: Thời gian thực tế tính từ khi người dùng chọn ngôn ngữ mới đến khi thẻ
html[lang]trong DOM được cập nhật xong (Playwright, trung bình 5 lần lặp). - Hydration: Thời gian thực hiện quá trình hydrate của React.
Số liệu bên dưới được ghi nhận vào ngày 2026-09-12 vớinext-i18next16.3.0,react-i18next17.0.13 vàintlayer9.5.1. Ứng dụng kiểm thử được thiết kế gọn nhẹ, nên các tỷ lệ rò rỉ thể hiện mô hình kiến trúc: Rò rỉ sẽ tăng theo lượng văn bản thực tế, trong khi chi phí runtime giữ nguyên.
Kết quả trên Next.js (next-i18next)
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
| Thư viện | Chiến lược | Dung lượng Lib (gz) | JS TB mỗi trang (gz) | Rò rỉ ngôn ngữ | Rò rỉ trang | Kích thước TB Comp (gz) | Phản hồi E2E | Thời gian Hydrate |
|---|---|---|---|---|---|---|---|---|
| base (không i18n) | - | 0.0 KB | 141.0 KB | 0.0% | 0.0% | 0.9 KB | 13.4 ms | 11.8 ms |
next-i18next | static | 19.7 KB | 218.5 KB | 0.0% | 89.8% | 78.5 KB | 16.4 ms | 15.6 ms |
next-i18next | dynamic | 19.7 KB | 169.5 KB | 50.0% | 89.8% | 26.1 KB | 15.4 ms | 27.7 ms |
next-i18next | scoped-static | 19.7 KB | 220.1 KB | 0.0% | 89.8% | 78.9 KB | 16.4 ms | 14.7 ms |
next-i18next | scoped-dynamic | 19.7 KB | 163.4 KB | 0.0% | 0.0% | 27.1 KB | 15.9 ms | 15.1 ms |
next-intlayer | static | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 8.5 KB | 15.5 ms | 16.9 ms |
next-intlayer | dynamic | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 6.9 KB | 15.3 ms | 15.9 ms |
@intlayer/next-i18next (tương thích) | static | 9.4 KB | 150.7 KB | 0.0% | 0.0% | 9.7 KB | 10.7 ms | 11.3 ms |
@intlayer/next-i18next (tương thích) | dynamic | 9.4 KB | 150.7 KB | 0.0% | 0.0% | 9.7 KB | 11.9 ms | 10.6 ms |
Phân tích kết quả
- Gánh nặng runtime: Phần lõi của
i18nextkết hợp cùngreact-i18nextlà runtime nặng nhất được ghi nhận: chiếm 19.7 KB gzip cho một component rỗng, so với chỉ 5.5 KB củanext-intlayer. - Thiết lập cơ bản rất tốn kém: Đặt
resourcestrực tiếp vàoinit()tạo ra kích thước 218.5 KB mỗi trang (+77.5 KB so với ứng dụng gốc không i18n). Từng trang bắt buộc phải tải về toàn bộ các namespace. - Tối ưu hóa thủ công đòi hỏi nhiều công sức: Chuyển sang dùng backend (
dynamic) giảm được 49 KB nhưng vẫn rò rỉ 90% chuỗi thuộc các trang khác, và một nửa chuỗi tải về thuộc ngôn ngữ không dùng đến. Phải bổ sung cấu hình chia nhỏ namespace cho từng route (scoped-dynamic) mới triệt tiêu được rò rỉ ở mức 163.4 KB, nhưng vẫn nặng hơn +22.4 KB mỗi trang so với Intlayer (141.3 KB) vốn không cần cấu hình phức tạp. - Kích thước từng component: Một component gọi
useTranslation()được đóng gói trong khoảng 26 đến 79 KB; trong khi component tương đương dùnguseIntlayer()chỉ nặng 6.9 KB. - Độ trễ khi hydrate: Thời gian hydrate tăng vọt lên 27.7 ms ở cấu hình
dynamicdo thực thể i18next cần khởi tạo và nạp backend tại client trước khi React có thể hoàn tất quá trình hydrate.
Kết quả trên TanStack Start (react-i18next)
Cùng ứng dụng kiểm thử trên nền tảng TanStack Start sử dụng trực tiếp react-i18next nhằm loại bỏ các yếu tố can thiệp riêng của 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
| Thư viện | Chiến lược | Dung lượng Lib (gz) | JS TB mỗi trang (gz) | Rò rỉ ngôn ngữ | Rò rỉ trang | Kích thước TB Comp (gz) | Phản hồi E2E | Thời gian Hydrate |
|---|---|---|---|---|---|---|---|---|
| base (không i18n) | - | 0.0 KB | 111.0 KB | 0.0% | 0.0% | 0.7 KB | 8.1 ms | 21.6 ms |
react-i18next | static | 18.4 KB | 180.3 KB | 50.0% | 89.8% | 24.3 KB | 12.9 ms | 85.1 ms |
react-i18next | dynamic | 18.4 KB | 136.4 KB | 23.1% | 89.8% | 24.8 KB | 123.1 ms | 32.9 ms |
react-i18next | scoped-static | 18.4 KB | 184.2 KB | 50.7% | 89.8% | 25.3 KB | 185.1 ms | 25.2 ms |
react-i18next | scoped-dynamic | 18.4 KB | 127.2 KB | 0.0% | 0.0% | 26.7 KB | 17.6 ms | 11.3 ms |
intlayer | static | 5.0 KB | 125.8 KB | 50.0% | 0.0% | 8.1 KB | 3.2 ms | 11.5 ms |
intlayer | dynamic | 5.0 KB | 118.6 KB | 0.0% | 0.0% | 6.3 KB | 3.6 ms | 14.1 ms |
Phân tích kết quả
- Ứng dụng
react-i18nextcơ bản tải về nhiều hơn +69 KB mỗi trang so với bản không có i18n, và quá trình hydrate kéo dài tới 85 ms (gấp 4 lần chuẩn) do toàn bộ cây dữ liệu dịch thuật phải được xử lý và đăng ký trên client trước lần render đầu tiên. - Độ trễ mạng khi đổi ngôn ngữ: Khi nạp tài nguyên theo yêu cầu qua backend, việc chuyển đổi ngôn ngữ phải chờ phản hồi mạng trước khi thẻ
html[lang]kịp cập nhật: 123 ms ởdynamicvà 185 ms ởscoped-static. Trong khi đó, Intlayer cập nhật DOM chỉ trong 3-4 ms ở cả hai chế độ mà không bao giờ bị nghẽn mạng. - Cấu hình tối ưu cao
scoped-dynamicđạt mức 127.2 KB, vẫn nặng hơn +8.6 KB so với cấu hìnhdynamiccủa Intlayer, dù đã phải tốn rất nhiều công sức tạo ánh xạ route-namespace, backend và các biên Suspense. - Chế độ
staticcủa Intlayer đã mặc định đạt 0% rò rỉ trang vì nó chỉ đóng gói các từ điển được import bởi component của trang đó. BậtimportMode: 'dynamic'sẽ triệt tiêu hoàn toàn rò rỉ ngôn ngữ. - Kích thước component: 24-27 KB ở
react-i18nextso với 6-8 KB ở Intlayer.useTranslation()luôn gắn chặt từng component với instance i18next toàn cục.
Nguyên nhân của sự chênh lệch: Global Instance vs Từ điển biên dịch
i18next được xây dựng từ năm 2012 theo hướng runtime: một instance toàn cục nắm giữ kho tài nguyên, các plugin mở rộng nó và hàm t() tìm khóa khi render. Thiết kế này đem lại tính linh hoạt cao nhưng tạo ra gánh nặng lớn về dung lượng:
Sao chép mã vào clipboard
Instance toàn cục không thể biết trước component sẽ gọi những khóa nào; vì vậy nó buộc phải lưu trữ toàn bộ các namespace được chỉ định nạp. Muốn tối ưu, bạn phải chia nhỏ danh mục, bạn phải liệt kê các namespace cần thiết cho từng trang, và bạn phải duy trì danh sách này khi component di chuyển. Đúng như ghi chú benchmark đã chỉ ra: "Vừa duy trì an toàn kiểu dữ liệu vừa quản lý đúng namespace cho từng trang thực sự là một cơn ác mộng".
Intlayer loại bỏ hoàn toàn instance toàn cục. Nội dung được khai báo ngay cạnh component và trình biên dịch sẽ giải quyết cây phụ thuộc vào lúc build:
Sao chép mã vào clipboard
@intlayer/swc / @intlayer/babel biết chính xác component nào cần từ điển nào, chỉ đóng gói đúng phần đó cho ngôn ngữ đang kích hoạt và loại bỏ nội dung thừa. Mô hình "scoped-dynamic" trở thành kết quả mặc định của quá trình build mà nhóm phát triển không phải bận tâm quản lý thủ công.
Để đạt được các thông số của dòngdynamic, bạn chỉ cần khai báodictionary.importMode: 'dynamic'trongintlayer.config.ts. Chi tiết xem thêm tại tài liệu tối ưu bundle.
Trải nghiệm lập trình viên (DX)
Khởi tạo cấu hình
next-i18next (App Router)
Sao chép mã vào clipboard
Bên cạnh đó, bạn phải viết thêm I18nProvider ở phía client, khai báo generateStaticParams và quản lý mảng namespaces trên từng trang.
Intlayer
Sao chép mã vào clipboard
Sao chép mã vào clipboard
Component phía Client
react-i18next
Sao chép mã vào clipboard
Sao chép mã vào clipboard
Trang hiển thị component này bắt buộc phải nạp namespaceabout, vàt("counter.label")sẽ chỉ là chuỗi thông thường nếu chưa mở rộngCustomTypeOptions.
Intlayer
Sao chép mã vào clipboard
Sao chép mã vào clipboard
label và increment đều được kiểm tra kiểu dữ liệu nghiêm ngặt; gõ sai tên sẽ báo lỗi TypeScript ngay lập tức, và nếu thiếu bản dịch tiếng Pháp thì quá trình build sẽ bị dừng lại.
Component phía Server đồng bộ
next-i18next
Sao chép mã vào clipboard
Trang cha phải gọi i18n.getFixedT(locale, "about") rồi truyền t và locale xuống dưới dạng props.
Intlayer
Sao chép mã vào clipboard
Giữ nguyên API của i18next, nhận ngay hiệu năng của Intlayer
Bạn không cần phải viết lại toàn bộ component để đạt được kết quả benchmark trên. @intlayer/i18next, @intlayer/react-i18next và @intlayer/next-i18next đóng vai trò là các adapter tương thích trực tiếp: các lệnh gọi useTranslation, t(), <Trans>, xử lý số nhiều và ngữ cảnh vẫn hoạt động bình thường, được vận hành bởi các từ điển do trình biên dịch Intlayer cung cấp.
Sao chép mã vào clipboard
Sao chép mã vào clipboard
Trong bài đo kiểm, bản build tương thích của cùng ứng dụng Next.js đã giảm từ 218.5 KB xuống 150.7 KB mỗi trang, từ 78.5 KB xuống 9.7 KB mỗi component, tỷ lệ rò rỉ giảm từ ~90% về 0%, và thời gian hydrate rút ngắn từ 15.6 ms xuống 11.3 ms mà không cần sửa bất kỳ dòng mã nghiệp vụ nào. Các tệp locales/{lng}/{ns}.json hiện tại vẫn có thể được giữ làm nguồn dữ liệu chính thông qua plugin đồng bộ JSON.
Xem hướng dẫn chuyển đổi: i18next, react-i18next, next-i18next.
Khi nào nên chọn giải pháp nào?
- Chọn i18next: Nếu bạn phụ thuộc chặt chẽ vào hệ sinh thái plugin (bộ nhận diện đặc thù, backend chuyên biệt, ICU, Locize), cần bản địa hóa ở cả ngoài React (Node services, vanilla JS, các framework khác), đội ngũ của bạn đã rất thành thạo, hoặc nền tảng dịch thuật bắt buộc định dạng
locales/{lng}/{ns}.json. Hãy dành thời gian để quản lý namespace và bản đồ route nếu hiệu năng là ưu tiên hàng đầu. - Chọn Intlayer: Nếu bạn muốn nội dung đặt ngay tại component, an toàn kiểu dữ liệu TypeScript nghiêm ngặt, báo lỗi thiếu bản dịch khi build, tự động tree-shaking và lazy loading không tốn công, chuyển đổi ngôn ngữ tức thì, hỗ trợ component server đồng bộ và các công cụ biên tập tích hợp sẵn (Visual Editor, CMS, dịch thuật AI, máy chủ MCP). Rất thích hợp cho dự án dạng module lớn và Design Systems.
- Chọn adapter
@intlayer/*-i18next: Nếu bạn đã có ứng dụng chạy i18next và muốn tối ưu hóa kích thước bundle và tốc độ phản hồi ngay lập tức mà không phải refactor mã nguồn.
Các bài so sánh liên quan
- next-intl vs Intlayer (cùng benchmark)
- Lingui vs Intlayer (cùng benchmark)
- vue-i18n vs Intlayer benchmark (cùng benchmark)
- next-i18next vs next-intl vs Intlayer
- react-i18next vs react-intl vs Intlayer
- i18next có bị lỗi thời không?
Lượt gắn sao trên GitHub
Lượt sao GitHub là thước đo tin cậy về mức độ phổ biến, niềm tin của cộng đồng và tiềm năng phát triển lâu dài. Dù không phản ánh trực tiếp chất lượng mã nguồn, nó cho thấy sự quan tâm và xu hướng đón nhận của cộng đồng phát triển phần mềm.
Kết luận
i18next hoàn toàn xứng đáng với vị thế của mình: hoạt động ở mọi môi trường, có plugin cho mọi bài toán và được duy trì ổn định hơn một thập kỷ qua. Tuy nhiên, benchmark này phản ánh chi phí của kiến trúc phụ thuộc vào runtime. Cách thiết lập thông thường làm tăng thêm +70-77 KB gzip mỗi trang, rò rỉ ~90% dữ liệu của các trang khác, và mất hơn 100 ms để đổi ngôn ngữ khi dùng lazy load. Dù có thể triệt tiêu rò rỉ nhưng đòi hỏi quản lý thủ công phức tạp và vẫn nặng hơn Intlayer từ 9-22 KB.
Intlayer chuyển toàn bộ gánh nặng này sang trình biên dịch. Từ điển theo component, tải lười theo ngôn ngữ và loại bỏ nội dung thừa đều là kết quả tự động của quá trình build. Trên cùng một ứng dụng: Chỉ tăng +0.3 KB mỗi trang, 0% rò rỉ, component nhỏ hơn 3-10 lần và đổi ngôn ngữ chỉ mất 3-4 ms.
Toàn bộ dữ liệu thô, ứng dụng mẫu và kịch bản thử nghiệm đều công khai tại kho lưu trữ Benchmark Bloom. Bạn hoàn toàn có thể tự mình kiểm chứng.
Tìm hiểu thêm tại tài liệu 'Tại sao chọn Intlayer?'.
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.
