npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@mico_fe/vite-plugin-i18n-vue-ast

v1.0.0

Published

Vue i18n Vite plugin with AST-based transformation - More precise Chinese to $t(key) conversion

Readme

@mico_fe/vite-plugin-i18n-vue-ast

基于 AST (抽象语法树) 的 Vue i18n 编译时转换插件。相比正则匹配方案,AST 方案更加精确,能够准确识别代码结构,避免误转换。

✨ 特性

  • 🎯 精确转换 - 基于 AST 分析,精确识别需要转换的中文文本
  • 🔍 语义理解 - 理解代码上下文,自动跳过注释、import、console 等
  • 📦 完整支持 - 支持 Template、Script、Script Setup
  • ⚡ 高性能 - 智能缓存,增量编译
  • 🛡️ 类型安全 - 完整的 TypeScript 支持
  • 🔧 可配置 - 丰富的配置选项

🔬 AST vs 正则方案对比

| 特性 | AST 方案 (本包) | 正则方案 | |------|----------------|----------| | 精确度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | | 跳过注释 | ✅ 自动 | ❌ 需特殊处理 | | 跳过 import | ✅ 自动 | ❌ 需特殊处理 | | 跳过 console | ✅ 自动 | ❌ 需特殊处理 | | 嵌套结构 | ✅ 完美支持 | ⚠️ 可能误匹配 | | 模板字符串 | ✅ 精确处理 | ⚠️ 复杂情况难处理 | | 性能 | 中等 | 快 | | 实现复杂度 | 高 | 低 |

📦 安装

# npm
npm install @mico_fe/vite-plugin-i18n-vue-ast -D

# pnpm
pnpm add @mico_fe/vite-plugin-i18n-vue-ast -D

# yarn
yarn add @mico_fe/vite-plugin-i18n-vue-ast -D

🚀 快速开始

1. 配置 Vite

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueI18nAst from '@mico_fe/vite-plugin-i18n-vue-ast'

export default defineConfig({
  plugins: [
    vueI18nAst({
      localesDir: 'locales',  // 语言包目录名(向上查找)或绝对路径
      sourceLocale: 'zh-CN',
      debug: true, // 开发时开启,查看转换日志
    }),
    vue(),
  ],
})

2. 准备语言包

// src/locales/zh-CN.json
{
  "hello": "你好世界",
  "welcome": "欢迎使用",
  "button": {
    "submit": "提交",
    "cancel": "取消"
  }
}
// src/locales/en-US.json
{
  "hello": "Hello World",
  "welcome": "Welcome",
  "button": {
    "submit": "Submit",
    "cancel": "Cancel"
  }
}

3. 编写 Vue 组件

<template>
  <div>
    <!-- 自动转换为 {{ $t('hello') }} -->
    <h1>你好世界</h1>
    
    <!-- 属性自动转换为 :title="$t('welcome')" -->
    <div title="欢迎使用">
      <button>提交</button>
      <button>取消</button>
    </div>
  </div>
</template>

<script setup lang="ts">
// 自动转换为 t('welcome')
const message = '欢迎使用'
console.log('这不会被转换') // console 自动跳过
</script>

编译后

<template>
  <div>
    <h1>{{ $t('hello') }}</h1>
    <div :title="$t('welcome')">
      <button>{{ $t('button.submit') }}</button>
      <button>{{ $t('button.cancel') }}</button>
    </div>
  </div>
</template>

<script setup lang="ts">
const message = t('welcome')
console.log('这不会被转换')
</script>

⚙️ 配置选项

interface LocalesRule {
  /** 文件路径匹配模式(glob 或正则) */
  match: string | RegExp
  /** 该匹配使用的语言包目录 */
  localesDir: string
  /** 可选的命名空间前缀 */
  namespace?: string
}

interface VueI18nAstPluginConfig {
  /** 语言包目录名(向上查找)或绝对路径(直接使用)
   * 
   * - 字符串:单一目录名,如 'locales'
   * - 数组:多个目录名,按优先级排序,如 ['locales', 'i18n', 'langs']
   * - 绝对路径:直接使用,不进行向上查找
   * 
   * @default 'locales'
   */
  localesDir?: string | string[]
  
  /** 路径匹配规则(优先级高于 localesDir)
   * 用于特殊项目的精确控制
   */
  localesRules?: LocalesRule[]
  
  /** 源语言代码(用于构建反向映射)
   * @default 'zh-CN'
   */
  sourceLocale?: string
  
  /** 模板中的翻译函数名
   * @default '$t'
   */
  translateFn?: string
  
  /** 脚本中的翻译函数名
   * @default 't'
   */
  scriptTranslateFn?: string
  
  /** 是否转换 <script> 中的中文
   * @default true
   */
  transformScript?: boolean
  
  /** 是否转换 <script setup> 中的中文
   * @default true
   */
  transformScriptSetup?: boolean
  
  /** 开发模式是否启用
   * @default true
   */
  enableInDev?: boolean
  
  /** 排除的文件模式
   * @default [/node_modules/, /\.d\.ts$/, /\/locales\//, /\/langs\//]
   */
  exclude?: (string | RegExp)[]
  
  /** 调试模式 - 输出详细转换日志
   * @default false
   */
  debug?: boolean
  
  /** 缺少翻译时的处理方式
   * - 'warn': 控制台警告(默认)
   * - 'error': 抛出错误,中断构建
   * - 'ignore': 静默忽略
   * @default 'warn'
   */
  onMissing?: 'warn' | 'error' | 'ignore'
  
  /** 是否自动导入 t 函数
   * @default false
   */
  autoImport?: boolean
  
  /** 自动导入来源
   * @default '@mico_fe/i18n-vue'
   */
  importFrom?: string
}

🔍 AST 转换原理

1. SFC 解析

使用 @vue/compiler-sfc.vue 文件解析为三个部分:

┌─────────────────────────────────────────┐
│            Vue SFC (.vue)               │
├─────────────────────────────────────────┤
│  <template>...</template>               │
│  <script>...</script>                   │
│  <script setup>...</script>             │
│  <style>...</style>                     │
└─────────────────────────────────────────┘
                   │
                   ▼
        @vue/compiler-sfc.parse()
                   │
        ┌──────────┼──────────┐
        ▼          ▼          ▼
    Template    Script    ScriptSetup

2. Template AST 处理

使用 @vue/compiler-dom 解析模板:

Template Content
       │
       ▼
@vue/compiler-dom.parse()
       │
       ▼
┌─────────────────────────────────────────┐
│              Template AST               │
├─────────────────────────────────────────┤
│  ROOT                                   │
│  └── ELEMENT (div)                      │
│      ├── props: [ATTRIBUTE, DIRECTIVE]  │
│      └── children:                      │
│          ├── TEXT "你好"                │
│          ├── INTERPOLATION {{ msg }}    │
│          └── ELEMENT (span)             │
└─────────────────────────────────────────┘

节点类型处理

| 节点类型 | 示例 | 转换结果 | |---------|------|---------| | TEXT | >你好< | >{{ $t('key') }}< | | ATTRIBUTE | title="提示" | :title="$t('key')" | | INTERPOLATION | {{ '中文' }} | {{ $t('key') }} |

3. Script AST 处理

使用 @babel/parser + @babel/traverse

Script Content
       │
       ▼
@babel/parser.parse()
       │
       ▼
┌─────────────────────────────────────────┐
│               Script AST                │
├─────────────────────────────────────────┤
│  Program                                │
│  └── statements:                        │
│      ├── ImportDeclaration              │
│      ├── VariableDeclaration            │
│      │   └── StringLiteral "中文"       │
│      └── ExpressionStatement            │
│          └── CallExpression (console)   │
│              └── StringLiteral "日志"   │
└─────────────────────────────────────────┘
       │
       ▼
@babel/traverse (遍历并收集需要转换的节点)

自动跳过的上下文

  • import 声明
  • export 声明的 source
  • console.xxx() 调用
  • ✅ 已经是 t() / $t() 的参数
  • ✅ 对象的 key(非计算属性)
  • ✅ TypeScript 类型注解
  • require() 调用

4. 代码生成

使用 magic-string 根据收集的位置信息进行精确替换:

// 从后往前替换,避免位置偏移
const sortedTransforms = transforms.sort((a, b) => b.start - a.start)

for (const { start, end, replacement } of sortedTransforms) {
  magicString.overwrite(start, end, replacement)
}

🎯 最佳实践

1. 语言包结构

推荐使用扁平化或浅层嵌套:

{
  "common.confirm": "确认",
  "common.cancel": "取消",
  "user.login": "登录",
  "user.logout": "退出登录"
}

2. 参数化翻译

// zh-CN.json
{
  "welcome.user": "欢迎,{name}!"
}
<template>
  <!-- 需要手动使用 $t -->
  <p>{{ $t('welcome.user', { name: userName }) }}</p>
</template>

3. 动态内容

对于动态生成的文本,AST 无法在编译时处理,需要手动使用 t()$t()

<script setup>
// 动态内容需要手动处理
const dynamicText = computed(() => t(props.messageKey))
</script>

4. 开启调试

开发时建议开启 debug: true,查看转换详情:

vueI18nAst({
  debug: process.env.NODE_ENV === 'development',
})

控制台输出示例:

[vue-i18n-ast] Processing (AST): /src/components/Header.vue
[vue-i18n-ast] Text: "你好世界" → {{ $t('hello') }}
[vue-i18n-ast] Attr: title="欢迎使用" → :title="$t('welcome')"
[vue-i18n-ast] Script: "提交" → t('button.submit')

📊 构建统计

构建完成后会输出统计信息:

[vue-i18n-ast] Build complete: 156 transformations in 23/45 files
  - Template: 89, Script: 45, Attribute: 22
  - Cache hits: 12

⚠️ 注意事项

  1. 性能考虑:AST 解析比正则匹配慢,但更精确。对于大型项目,建议合理使用缓存。

  2. 复杂表达式:带变量的模板字符串无法自动转换:

    // ❌ 无法自动转换
    const msg = `欢迎 ${name}!`
       
    // ✅ 需要手动处理
    const msg = t('welcome.user', { name })
  3. 语言包同步:确保所有语言包的 key 保持一致。

  4. 与运行时配合:此插件只负责编译时转换,运行时需要配合 @mico_fe/i18n-vue 使用。

🔗 相关包

📄 License

MIT