npm package: @mico_fe/vite-plugin-i18n-react-ast
Description: React i18n Vite plugin with AST-based transformation - More precise Chinese to t(key) conversion
Author: mico
Latest version: 1.0.0

# @mico_fe/vite-plugin-i18n-react-ast

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

## ✨ 特性

- **🎯 精确转换** - 基于 AST 分析，精确识别 JSX 文本、属性和脚本中的中文
- **🔍 语义理解** - 理解代码上下文，自动跳过注释、import、console 等
- **📦 完整支持** - 支持 JSX/TSX、普通 JS/TS 文件
- **⚡ 高性能** - 智能缓存，增量编译
- **🛡️ 类型安全** - 完整的 TypeScript 支持
- **🔧 可配置** - 丰富的配置选项

## 🔬 AST vs 正则方案对比

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

## 📦 安装

```bash
# npm
npm install @mico_fe/vite-plugin-i18n-react-ast -D

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

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

## 🚀 快速开始

### 1. 配置 Vite

```typescript
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import reactI18nAst from '@mico_fe/vite-plugin-i18n-react-ast'

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

### 2. 准备语言包

```json
// src/locales/zh-CN.json
{
  "hello": "你好世界",
  "welcome": "欢迎使用",
  "button": {
    "submit": "提交",
    "cancel": "取消"
  }
}
```

```json
// src/locales/en-US.json
{
  "hello": "Hello World",
  "welcome": "Welcome",
  "button": {
    "submit": "Submit",
    "cancel": "Cancel"
  }
}
```

### 3. 编写 React 组件

```tsx
// App.tsx
import React from 'react'

function App() {
  // 脚本中的中文自动转换
  const message = '欢迎使用'
  console.log('这不会被转换') // console 自动跳过
  
  return (
    <div>
      {/* JSX 文本自动转换 */}
      <h1>你好世界</h1>
      
      {/* 属性自动转换 */}
      <div title="欢迎使用">
        <button>提交</button>
        <button>取消</button>
      </div>
    </div>
  )
}
```

### 编译后

```tsx
import React from 'react'

function App() {
  const message = t('welcome')
  console.log('这不会被转换')
  
  return (
    <div>
      <h1>{t('hello')}</h1>
      <div title={t('welcome')}>
        <button>{t('button.submit')}</button>
        <button>{t('button.cancel')}</button>
      </div>
    </div>
  )
}
```

## ⚙️ 配置选项

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

interface ReactI18nAstPluginConfig {
  /** 语言包目录名（向上查找）或绝对路径（直接使用）
   * 
   * - 字符串：单一目录名，如 'locales'
   * - 数组：多个目录名，按优先级排序，如 ['locales', 'i18n', 'langs']
   * - 绝对路径：直接使用，不进行向上查找
   * 
   * @default 'locales'
   */
  localesDir?: string | string[]
  
  /** 路径匹配规则（优先级高于 localesDir）
   * 用于特殊项目的精确控制
   */
  localesRules?: LocalesRule[]
  
  /** 源语言代码（用于构建反向映射）
   * @default 'zh-CN'
   */
  sourceLocale?: string
  
  /** 翻译函数名
   * @default 't'
   */
  translateFn?: string
  
  /** 开发模式是否启用
   * @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-react'
   */
  importFrom?: string
}
```

## 🔍 AST 转换原理

### 1. 代码解析

使用 `@babel/parser` 解析 JSX/TSX 代码：

```
React Component (.tsx/.jsx)
           │
           ▼
  @babel/parser.parse()
           │
           ▼
┌─────────────────────────────────────────┐
│               React AST                 │
├─────────────────────────────────────────┤
│  Program                                │
│  └── FunctionDeclaration                │
│      └── ReturnStatement                │
│          └── JSXElement                 │
│              ├── JSXOpeningElement      │
│              │   └── JSXAttribute       │
│              └── JSXText "你好"         │
└─────────────────────────────────────────┘
```

### 2. AST 遍历

使用 `@babel/traverse` 遍历并识别需要转换的节点：

```typescript
traverse(ast, {
  // JSX 文本: <div>你好</div>
  JSXText(path) { /* ... */ },
  
  // JSX 属性: <div title="标题">
  JSXAttribute(path) { /* ... */ },
  
  // JSX 表达式: <div>{'中文'}</div>
  JSXExpressionContainer(path) { /* ... */ },
  
  // 普通字符串: const msg = '中文'
  StringLiteral(path) { /* ... */ },
  
  // 模板字符串: const msg = `中文`
  TemplateLiteral(path) { /* ... */ },
})
```

### 3. 节点类型转换

| 原始代码 | AST 节点类型 | 转换结果 |
|---------|-------------|---------|
| `<h1>你好</h1>` | JSXText | `<h1>{t('key')}</h1>` |
| `title="中文"` | JSXAttribute | `title={t('key')}` |
| `{'中文'}` | JSXExpressionContainer | `{t('key')}` |
| `const x = '中文'` | StringLiteral | `const x = t('key')` |
| `` `中文` `` | TemplateLiteral | `t('key')` |

### 4. 自动跳过的上下文

基于 AST 的语义分析，自动跳过以下上下文：

- ✅ `import` 声明
- ✅ `export` 声明的 source
- ✅ `console.xxx()` 调用
- ✅ 已经是 `t()` 的参数
- ✅ 对象的 key（非计算属性）
- ✅ TypeScript 类型注解
- ✅ `require()` 调用
- ✅ Tagged template literals (如 `css\`...\``)

## 🎯 最佳实践

### 1. 配合 useTranslation Hook

```tsx
import { useTranslation } from '@mico_fe/i18n-react'

function MyComponent() {
  const { t } = useTranslation()
  
  // 自动转换后会使用这个 t 函数
  return <div>你好世界</div>
}
```

### 2. 动态内容处理

对于动态内容，需要手动使用 `t()`：

```tsx
// 动态 key - 需要手动处理
const statusMap = {
  success: t('status.success'),
  error: t('status.error'),
}

// 动态参数
const message = t('welcome.user', { name: userName })
```

### 3. 条件渲染

```tsx
// ✅ 正常转换
{isLoggedIn ? <span>已登录</span> : <span>未登录</span>}

// ✅ 转换后
{isLoggedIn ? <span>{t('logged_in')}</span> : <span>{t('not_logged_in')}</span>}
```

### 4. 开启调试

开发时建议开启 `debug: true`：

```typescript
reactI18nAst({
  debug: process.env.NODE_ENV === 'development',
})
```

控制台输出示例：

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

## 📊 构建统计

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

```
[react-i18n-ast] Build complete: 156 transformations in 23/45 files
  - JSX Text: 89, Script: 45, Attribute: 22
  - Cache hits: 12
```

## ⚠️ 注意事项

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

2. **复杂表达式**：带变量的模板字符串无法自动转换：
   ```tsx
   // ❌ 无法自动转换
   const msg = `欢迎 ${name}！`
   
   // ✅ 需要手动处理
   const msg = t('welcome.user', { name })
   ```

3. **Fragment 中的文本**：正常支持
   ```tsx
   // ✅ 正常转换
   <>你好世界</>
   ```

4. **与运行时配合**：此插件只负责编译时转换，运行时需要配合 `@mico_fe/i18n-react` 使用。

## 🔗 相关包

- [@mico_fe/i18n-core](../core) - 核心运行时库
- [@mico_fe/i18n-react](../react) - React 运行时支持
- [@mico_fe/vite-plugin-i18n-react](../vite-plugin-react) - 正则方案（更快但精确度较低）

## 📄 License

MIT