Tác giả:
    Ngày tạo:2026-09-13Cập nhật lần cuối:2026-09-13

    vue-i18n VS @intlayer/vue-i18n | API Giống Nhau, Bundle Khác

    @intlayer/vue-i18n là một compat adapter: nó expose API của vue-i18n (createI18n, useI18n, t(), d(), n(), $t, v-t, i18n.global.locale...) và phục vụ nó từ các dictionaries được biên dịch bởi Intlayer. Các file .vue của bạn không thay đổi. Cái mà t("footer.github") được bind tới thì có.

    Bài viết này đo lường việc thay thế đó trên cùng một ứng dụng Vite + Vue 3, được xây dựng một lần với vue-i18n và một lần với adapter. Các con số đến từ Benchmark Bloom. Để so sánh vue-i18n và Intlayer như các thư viện, hãy đọc vue-i18n vs Intlayerbenchmark vue-i18n vs Intlayer. Bài này nói về những gì adapter thay đổi khi bạn giữ các component của mình như cũ.

    tl;dr: Trên cùng một ứng dụng Vite + Vue 3, thay thế vue-i18n bằng @intlayer/vue-i18n đã giảm JavaScript trên mỗi trang từ 134.9 KB xuống 47.0 KB gzip (ứng dụng không có i18n nặng 41.3 KB), runtime từ 24.3 KB xuống 7.9 KB, thành phần trung bình từ 196 KB xuống 8.4 KB, và rò rỉ chuỗi trang nước ngoài từ 90% xuống 0%, mà không chỉnh sửa bất kỳ tệp .vue nào. createI18n({ messages }) tiếp tục hoạt động như một fallback; xóa các import JSON để có được những con số trên. Các khối SFC <i18n>setLocaleMessage() runtime là hai tính năng không được chuyển đổi qua.

    @intlayer/vue-i18n là gì

    vue-i18n là một runtime. createI18n({ messages: { en, fr, ... } }) xây dựng một instance toàn cục chứa mọi message của mọi locale; useI18n() liên kết mỗi component với nó; t("footer.github") duyệt cây tại thời điểm render. Thiết kế đó là điều làm cho các khối SFC <i18n>setLocaleMessage() trở nên có thể, và nó cũng là lý do tại sao đồ thị phụ thuộc của mọi component đều bao gồm toàn bộ cây.

    @intlayer/vue-i18n giữ lại API và thay thế cây:

    1. Import aliasing. vueI18nVitePlugin() từ @intlayer/vue-i18n/plugin bao bọc vite-intlayer và thêm một resolve.alias để vue-i18n được phân giải thành @intlayer/vue-i18n. Không có import nào được đổi tên.
    2. JSON as source of truth. Plugin syncJSON đọc file locales/{locale}.json của bạn với format: "vue-i18n" (vì vậy interpolation {name}, {0} list và plurals pipe "car | cars" được phân tích chính xác) và ghi lại các bản dịch khi CLI hoặc CMS cập nhật chúng.
    3. Call-site binding. Bước tối ưu hóa Intlayer viết lại các call site useI18n() để component nhận các dictionaries từ các key của nó, trong locale đang hoạt động, dưới dạng import mà bundler có thể truy vết và tách.
    src/components/Footer.vue
    <!-- Mã của bạn, không thay đổi -->
    <script setup lang="ts">
    import { useI18n } from "vue-i18n";
    
    const { t } = useI18n();
    </script>
    
    <template>
      <a href="https://github.com/intlayer-org/benchmark-bloom">{{
        t("footer.github")
      }}</a>
    </template>
    
    Những gì trình biên dịch phát ra (đơn giản hóa)
    import _dicHash_footer from "../.intlayer/dictionaries/footer.mjs";
    import { useDictionary as useI18n } from "@intlayer/vue-i18n";
    
    const { t } = useI18n(_dicHash_footer);
    

    Thành phần không còn truy cập toàn bộ cây thông báo toàn cục. Nó chỉ truy cập footer. Đó là lý do tại sao cột kích thước thành phần dưới đây giảm từ 196 KB xuống 8 KB.

    Những gì adapter giữ lại, bỏ qua và không thay thế

    vue-i18n APIVới @intlayer/vue-i18n
    useI18n(){ t, d, n, te, tm, rt, locale, availableLocales }✅ Được giữ lại. t keys được gõ theo các từ điển của bạn
    t("key", { name }), t("key", [a, b]), t("key", count)✅ Được giữ lại. {name}, {0} và plurals phân tách bằng dấu gạch chéo được phân giải như trước
    d(date, "long"), n(value, "currency")✅ Được giữ lại. datetimeFormats / numberFormats từ createI18n() được sử dụng, được hỗ trợ bởi Intl gốc
    i18n.global.locale.value = "fr"✅ Được giữ lại. Một WritableComputedRef được hỗ trợ bởi client của Intlayer; phản ứng hoạt động như trước
    $t, $tc, $te, $tm, $rt, $d, $n, $i18n (Options API)✅ Được giữ lại. Được đăng ký trên app.config.globalProperties bởi app.use(i18n)
    v-t directive✅ Được giữ lại
    legacy: true✅ Được chấp nhận
    createI18n({ messages })⚠️ messages được sử dụng như một fallback runtime với cảnh báo dev. Xóa các import JSON để giảm bundle
    setLocaleMessage(), mergeLocaleMessage()❌ Cảnh báo và không làm gì. Runtime message loading được thay thế bằng build-time dictionaries
    SFC <i18n> custom blocks❌ Không được đọc. Di chuyển các messages đó vào locale JSON (hoặc một .content.ts bên cạnh component)
    @nuxtjs/i18n⚠️ Adapter riêng biệt, xem tài liệu tương thích Nuxt

    Bài kiểm tra

    Những gì đã được đo lường

    Benchmark Bloom suite xây dựng cùng một ứng dụng Vite + Vue 3 với mỗi setup: 10 trang (home, about, blog, careers, contact, FAQ, pricing, products, settings, team), 10 locales (en, fr, es, de, it, pt, zh, ja, ko, ru), các components và nội dung giống hệt nhau. Các trang được đo lường trong enfr.

    Cả hai đều được xây dựng trong cấu hình tĩnh, cấu hình mà hầu hết các dự án Vue sử dụng: đối với vue-i18n, mỗi locale JSON được import và truyền vào createI18n({ messages }); đối với adapter, các components giống nhau với vite.config.tsintlayer.config.ts được thay đổi và import messages được loại bỏ. vue-intlayer native được bao gồm để tham khảo.

    Đối với mỗi build, suite ghi lại:

    • Kích thước thư viện: kích thước gzip (và minified) của một component rỗng chỉ import thư viện i18n.
    • Page JS: gzip JavaScript được tải xuống trên mỗi trang, được lấy trung bình trên tất cả các trang và locale.
    • Locale leak %: phần chia của các chuỗi được dịch trong JS được tải xuống thuộc về một locale mà người dùng không xem.
    • Page leak %: phần chia của các chuỗi được dịch trong JS được tải xuống thuộc về một trang mà người dùng không ở trên.
    • Component avg: kích thước gzip trung bình của mỗi component được biên dịch riêng lẻ.
    • E2E reactivity: thời gian treo tường giữa lúc chọn một locale mới và html[lang] cập nhật trong DOM (Playwright, 5 lần lặp).
    • Page load: PerformanceNavigationTiming.duration.
    Các con số dưới đây được lấy từ lần chạy ngày 2026-09-12 với vue-i18n 11.4.0 và @intlayer/vue-i18n 9.5.1. Ứng dụng kiểm tra được thiết kế cố ý nhỏ (một vài chục chuỗi mỗi locale), do đó tỷ lệ phần trăm rò rỉ mô tả một mẫu: chúng tăng lên khi nội dung của bạn tăng lên trong khi chi phí runtime vẫn cố định.

    Kết quả trên Vite + Vue 3

    SetupStrategyLib size (gz)Lib size (min)Page JS avg (gz)Locale leakPage leakComponent avg (gz)E2E reactivityPage load
    base (no i18n)-0.0 KB0.0 KB41.3 KB0.0%-1.1 KB1.8 ms10.8 ms
    vue-i18nstatic24.3 KB83.2 KB134.9 KB50.0%90.0%196.0 KB2.8 ms13.6 ms
    @intlayer/vue-i18nstatic7.9 KB23.2 KB47.0 KB15.0%0.0%8.4 KB1.5 ms9.3 ms
    vue-intlayer (native)static3.9 KB11.1 KB57.1 KB56.8%0.0%7.7 KB4.5 ms13.8 ms
    vue-intlayer (native)dynamic3.9 KB11.1 KB59.8 KB50.0%0.0%6.5 KB4.0 ms15.8 ms
    Cột page-leak của ứng dụng cơ sở được để trống: không có thư viện i18n, quá trình fingerprinting nhặt các chuỗi hard-coded trong các chunk được chia sẻ và con số không có ý nghĩa.

    Cách đọc nó

    • 88 KB ít hơn trên mỗi trang, cùng các component. vue-i18n đưa ứng dụng 41.3 KB lên 134.9 KB. Bản build adapter của cùng các component đạt 47.0 KB, vượt quá ứng dụng cơ sở 5.7 KB. Phần lớn sự khác biệt là 74.9 KB của src/localescreateI18n({ messages }) kéo vào từng trang và adapter không bao giờ bundle như một khối.
    • Runtime thu nhỏ 3x. Một component trống chỉ import vue-i18n tốn 24.3 KB gzip / 83.2 KB minified: @intlify/core-base, message compiler và runtime. Adapter tốn 7.9 KB / 23.2 KB, phần lớn là Intlayer's core cộng với vue-i18n API surface.
    • Components: nhỏ gọn 23x. Một component useI18n() được compile riêng lẻ trung bình 196 KB, vì t được bind với instance mà giữ mọi message của mọi locale. Với adapter, component tương tự trung bình 8.4 KB: nó chỉ load dictionary riêng của nó.
    • Rò rỉ. vue-i18n gửi mỗi locale và chuỗi của mỗi trang trên mỗi trang: rò rỉ locale 50% (trên hai locale được fingerprint; với mười locale được bundled, lãng phí thực tế cao hơn), rò rỉ trang 90%. Adapter giảm rò rỉ trang xuống 0% vì mỗi component chỉ import các dictionaries của nó. Rò rỉ locale là 15% trong lần chạy static này; importMode: 'dynamic' là cài đặt loại bỏ nó, và cấu hình đó không phải là một phần của lần chạy Vue này.
    • Reactivity và page load. Chuyển đổi locale rẻ tiền cho cả hai (1.5-2.8 ms); hệ thống reactivity của Vue làm cho nó diễn ra một khi các message đã ở trong bộ nhớ. Page load giảm từ 13.6 ms xuống 9.3 ms, phù hợp với 88 KB JavaScript ít hơn để phân tích.
    • Về các hàng gốc. vue-intlayer trong lần chạy này đã đóng gói mọi locale ở chế độ static và đạt 57.1 KB với runtime 3.9 KB; các từ điển đồng bộ của adapter mang ít chuỗi locale ngoài hơn, do đó con số trên mỗi trang thấp hơn. Runtime gốc vẫn là nhẹ nhất trong ba cái, và mô hình .content.ts của nó là nơi các khối <i18n> SFC tìm thấy tương đương của chúng.

    Tại sao các số liệu thay đổi

    Không có gì thay đổi trong src/components/, vì vậy những lợi ích đến từ những gì useI18n được ràng buộc với.

    Với vue-i18n, binding là global instance. createI18n({ messages: { en, fr, ... } }) là một import chứa toàn bộ; mọi component gọi useI18n() đều có thể truy cập tất cả nó, vì vậy bundler không thể split dưới instance. Tối ưu hóa có nghĩa là bạn phải split en.json theo route, gọi setLocaleMessage() trong router guard, và giữ route-to-file map chính xác khi components di chuyển.

    bash
    .
    ├── locales
       ├── en.json                    # chuỗi của mọi trang
       └── fr.json
    └── src
        ├── i18n.ts                    # createI18n({ messages: { en, fr, ... } })
        ├── main.ts                    # app.use(i18n)
        └── components
            └── Footer.vue             # useI18n(); t("footer.github")
    

    Với @intlayer/vue-i18n, binding là dictionary. syncJSON biến mỗi key cấp cao nhất của en.json thành một dictionary; bước optimize chuyển cho component những cái mà các key của nó đặt tên, như imports mà bundler theo dõi và chia tách per page.

    bash
    .
    ├── intlayer.config.ts             # syncJSON({ format: "vue-i18n", source: ... })
    ├── locales
       ├── en.json                    # unchanged, still the source of truth
       └── fr.json
    ├── .intlayer/                     # generated: one dictionary per top-level key, per locale
    └── src
        ├── i18n.ts                    # createI18n({})   ← messages import removed
        ├── main.ts                    # app.use(i18n)    ← unchanged
        └── components
            └── Footer.vue             # useI18n(); t("footer.github")  ← unchanged
    

    Import messages trong i18n.ts là dòng duy nhất cần xóa. Đó là 88 KB.

    Migration trong ba bước

    1. Cài đặt

      bash
      npx intlayer init --interactive
      

      Lệnh phát hiện vue-i18n, cài đặt intlayer, vue-intlayer, @intlayer/vue-i18n@intlayer/sync-json-plugin, và điền sẵn intlayer.config.ts. Giữ vue-i18n được cài đặt: nó là một peer dependency và cung cấp các types.

    2. Trỏ Intlayer tới các tệp locale của bạn

      intlayer.config.ts
      import { Locales, type IntlayerConfig } from "intlayer";
      import { syncJSON } from "@intlayer/sync-json-plugin";
      
      const config: IntlayerConfig = {
        internationalization: {
          locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
          defaultLocale: Locales.ENGLISH,
        },
        dictionary: {
          // "static" gói tất cả các locale; "dynamic" tải cái đang hoạt động theo yêu cầu
          importMode: "dynamic",
          format: "vue-i18n",
        },
        plugins: [
          syncJSON({
            // vue-i18n dialect: {name}, {0}, "car | cars"
            format: "vue-i18n",
            source: ({ locale }) => `./locales/${locale}.json`,
            location: "locales",
          }),
        ],
      };
      
      export default config;
      

      locales/{locale}.json vẫn ở vị trí của nó. Mỗi khóa cấp cao nhất (footer, hero...) trở thành một dictionary.

    3. Thêm plugin và xóa import messages

      vite.config.ts
      import { defineConfig } from "vite";
      import vue from "@vitejs/plugin-vue";
      import { vueI18nVitePlugin } from "@intlayer/vue-i18n/plugin";
      
      export default defineConfig({
        plugins: [vue(), vueI18nVitePlugin()],
      });
      
      src/i18n.ts
      import { createI18n } from "vue-i18n";
      
      // Trước: createI18n({ locale: "en", messages: { en, fr, es } })
      export const i18n = createI18n({ locale: "en" });
      

      vueI18nVitePlugin() bao bọc vite-intlayer (theo dõi nội dung, biên dịch từ điển, bước tối ưu hóa) và tạo alias vue-i18n thành adapter. Xóa import messages là cách loại bỏ 88 KB; giữ lại nó giữ cho ứng dụng hoạt động nhưng ship cả hai.

    Những gì bạn có thể xóa sau đó

    File / patternLý do
    import en from "./locales/en.json" và những cái khác tương tựChỉ được sử dụng làm fallback bởi adapter. Đây là nơi 88 KB đến từ
    setLocaleMessage() trong router guardsKhông hoạt động. Quá trình loading per-route giờ đây là công việc của compiler
    @intlify/unplugin-vue-i18nKhông cần thiết: nó precompiles messages và SFC blocks mà adapter không đọc
    SFC <i18n> blocksKhông được đọc; di chuyển chúng sang locale JSON hoặc một .content.ts cho mỗi component

    Những gì bạn nhận được ngoài việc tiết kiệm bytes

    • Typed keys. t("footer.github") được gõ so với compiled footer dictionary; một đường dẫn sai là lỗi TypeScript thay vì key được render dưới dạng text.
    • npx intlayer test làm cho CI thất bại nếu thiếu key trong bất kỳ locale nào. npx intlayer fill dịch các key bị thiếu bằng provider key của bạn (OpenAI, Anthropic, Mistral, Gemini...) và ghi lại chúng vào locales/{locale}.json.
    • Visual Editor và CMS hoạt động trên cùng JSON, vì vậy những người không phải developer có thể chỉnh sửa thông qua UI và các file sẽ được cập nhật.
    • Di chuyển tăng dần đến .content.ts. Bất kỳ component nào cũng có thể chuyển từ useI18n() sang useIntlayer("footer") với một file content cùng vị trí. Các từ điển JSON và .content.ts tồ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ắt đầu

    • Các khối SFC <i18n> không được đọc. Nếu các tin nhắn của bạn nằm bên trong các component, chúng cần được di chuyển đến các file locale (hoặc đến .content.ts, đó là ý tưởng tương tự nhưng có kiểu dữ liệu).
    • Tải tin nhắn trong thời gian chạy không còn nữa. setLocaleMessage()mergeLocaleMessage() cảnh báo và trả về. Các bản dịch được tìm nạp từ CMS trong thời gian chạy cần Intlayer's CMS hoặc các lệnh intlayer pull / push.
    • messages là một fallback, không miễn phí. Giữ lại các import JSON trong createI18n() sẽ giữ 75 KB trong bundle. Xóa chúng khi intlayer test vượt qua.
    • Adapter không phải là runtime gốc. 7.9 KB so với 3.9 KB cho vue-intlayer. Khi mọi component đã chuyển sang useIntlayer, hãy loại bỏ nó.

    Khi nào sử dụng cái nào?

    • Ở lại trên vue-i18n nếu ứng dụng của bạn phụ thuộc vào các khối SFC <i18n>, vào các luồng setLocaleMessage() runtime, hoặc nếu 90 KB cho mỗi trang không phải là mối quan tâm cho khán giả của bạn.
    • Sử dụng @intlayer/vue-i18n nếu bạn đang dùng vue-i18n và muốn giảm 88 KB, các component nhỏ hơn 23 lần, 0% page leakage, các key được gõ và kiểm tra CI mà không cần chỉnh sửa tệp .vue. Đây là điểm vào cho một codebase vue-i18n hiện có.
    • Đi native (vue-intlayer) cho các dự án mới, hoặc khi adapter đã hoàn thành công việc của nó. Nó có runtime nhẹ nhất (3.9 KB) và mô hình .content.ts cho từng component thay thế các khối <i18n> bằng nội dung được gõ.

    Những so sánh liên quan

    Kết luận

    @intlayer/vue-i18n thay đổi những gì useI18n() được liên kết đến: từ một instance toàn cục chứa mọi thông báo của mọi locale đến một dictionary được biên dịch cho component đó. Trên cùng một ứng dụng Vite + Vue 3 mà nhỏ hơn 88 KB trên mỗi trang, một runtime nhỏ hơn 3 lần, các component nhỏ hơn 23 lần0% page leakage, cho một tệp cấu hình, một dòng plugin và một import bị xóa. Các khối <i18n> SFC và tải thông báo runtime là hai điều mà nó không hỗ trợ, và runtime vue-intlayer native vẫn còn nhỏ hơn một nửa.

    Tất cả dữ liệu thô, các ứng dụng thử nghiệm và các script đều có trong kho lưu trữ Benchmark Bloom. Chạy nó bằng chính bạn.

    Tham khảo tài liệu 'Why 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.

    Bài viết liên quan

    Bài viết mới nhất