@i18next-plugin/vue 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +177 -0
- package/README.zh-CN.md +177 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +299 -0
- package/dist/index.js.map +1 -0
- package/dist/options.d.ts +4 -0
- package/dist/script/extract.d.ts +4 -0
- package/dist/sfc/parser.d.ts +3 -0
- package/dist/sfc/vue2.d.ts +2 -0
- package/dist/sfc/vue3.d.ts +2 -0
- package/dist/template/extract.d.ts +2 -0
- package/dist/types.d.ts +78 -0
- package/dist/utils/index.d.ts +4 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 bin <bin@pbk6.cn>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# @i18next-plugin/vue
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@i18next-plugin/vue) [](LICENSE) [](https://vuejs.org/) [](https://vuejs.org/)
|
|
4
|
+
|
|
5
|
+
[English](README.md) | [中文](README.zh-CN.md)
|
|
6
|
+
|
|
7
|
+
i18next-cli plugin for extracting i18n translation keys from Vue Single File Components (SFC).
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Full Vue Support**: Supports Vue 2.6+ and Vue 3.x
|
|
12
|
+
- **Dual-mode Parsing**: Automatically handles translation calls in `<script>` and `<template>`
|
|
13
|
+
- **Multiple Syntaxes**: Supports `t()` function, `data-i18n` attribute, dynamic bindings, and more
|
|
14
|
+
- **TypeScript Support**: Complete type definitions
|
|
15
|
+
- **Highly Configurable**: Custom function names, attribute names, file patterns, and more
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @i18next-plugin/vue --save-dev
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Basic Configuration
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
// i18next.config.js
|
|
29
|
+
import { defineConfig } from 'i18next-cli';
|
|
30
|
+
import i18nextVuePlugin from '@i18next-plugin/vue';
|
|
31
|
+
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
locales: ['en', 'zh', 'fr'],
|
|
34
|
+
extract: {
|
|
35
|
+
input: 'src/**/*.{vue,ts}',
|
|
36
|
+
output: 'locales/{{language}}.json',
|
|
37
|
+
defaultNS: 'translation',
|
|
38
|
+
},
|
|
39
|
+
plugins: [i18nextVuePlugin()],
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Full Configuration
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
// i18next.config.js
|
|
47
|
+
import { defineConfig } from 'i18next-cli';
|
|
48
|
+
import i18nextVuePlugin from '@i18next-plugin/vue';
|
|
49
|
+
|
|
50
|
+
export default defineConfig({
|
|
51
|
+
locales: ['en', 'zh'],
|
|
52
|
+
extract: {
|
|
53
|
+
input: 'src/**/*.{vue,ts,js}',
|
|
54
|
+
output: 'locales/{{language}}/{{ns}}.json',
|
|
55
|
+
defaultNS: 'common',
|
|
56
|
+
},
|
|
57
|
+
plugins: [
|
|
58
|
+
i18nextVuePlugin({
|
|
59
|
+
// Explicitly specify Vue version (2 | 3)
|
|
60
|
+
vueVersion: 3,
|
|
61
|
+
|
|
62
|
+
// Whether to parse dynamic binding attributes (`:attr`, `v-bind:attr`)
|
|
63
|
+
vueBindAttr: true,
|
|
64
|
+
|
|
65
|
+
// Translation function names
|
|
66
|
+
functions: ['t', '$t'],
|
|
67
|
+
|
|
68
|
+
// Namespace function names
|
|
69
|
+
namespaceFunctions: ['useTranslation', 'withTranslation'],
|
|
70
|
+
|
|
71
|
+
// HTML attribute name
|
|
72
|
+
attr: 'data-i18n',
|
|
73
|
+
|
|
74
|
+
// Options attribute name
|
|
75
|
+
optionAttr: 'data-i18n-options',
|
|
76
|
+
|
|
77
|
+
// File patterns to match
|
|
78
|
+
filePatterns: ['.vue', '.nvue'],
|
|
79
|
+
}),
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Supported Syntax
|
|
85
|
+
|
|
86
|
+
### Template Syntax
|
|
87
|
+
|
|
88
|
+
```vue
|
|
89
|
+
<template>
|
|
90
|
+
<!-- data-i18n attribute -->
|
|
91
|
+
<h1 data-i18n="welcome.title">Welcome</h1>
|
|
92
|
+
|
|
93
|
+
<!-- Multiple keys (semicolon separated) -->
|
|
94
|
+
<p data-i18n="greeting;welcome.message"></p>
|
|
95
|
+
|
|
96
|
+
<!-- Dynamic binding :attr -->
|
|
97
|
+
<button :aria-label="t('button.submit')">Submit</button>
|
|
98
|
+
|
|
99
|
+
<!-- v-bind:attr -->
|
|
100
|
+
<input v-bind:placeholder="t('input.placeholder')" />
|
|
101
|
+
|
|
102
|
+
<!-- v-on:click -->
|
|
103
|
+
<button @click="t('button.click')">Click</button>
|
|
104
|
+
</template>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Script Syntax
|
|
108
|
+
|
|
109
|
+
```vue
|
|
110
|
+
<script>
|
|
111
|
+
import { useTranslation } from 'vue-i18next';
|
|
112
|
+
|
|
113
|
+
export default {
|
|
114
|
+
setup() {
|
|
115
|
+
const { t } = useTranslation('namespace');
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
// Simple key
|
|
119
|
+
title: t('welcome.message'),
|
|
120
|
+
|
|
121
|
+
// With default value
|
|
122
|
+
greeting: t('greeting', 'Hello World'),
|
|
123
|
+
|
|
124
|
+
// With namespace prefix
|
|
125
|
+
namespaced: t('shared:key'),
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
</script>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## API
|
|
133
|
+
|
|
134
|
+
### Options
|
|
135
|
+
|
|
136
|
+
| Option | Type | Default Value | Description |
|
|
137
|
+
| -------------------- | --------------------------------- | --------------------------------------- | -------------------------- |
|
|
138
|
+
| `vueVersion` | `2` | `3` | `undefined` | `undefined` | Explicit Vue version |
|
|
139
|
+
| `vueBindAttr` | `boolean` | `true` | Parse dynamic bindings |
|
|
140
|
+
| `functions` | `string[]` | `['t', '$t']` | Translation function names |
|
|
141
|
+
| `namespaceFunctions` | `string[]` | `['useTranslation', 'withTranslation']` | Namespace functions |
|
|
142
|
+
| `attr` | `string` | `'data-i18n'` | i18n attribute name |
|
|
143
|
+
| `optionAttr` | `string` | `'data-i18n-options'` | Options attribute name |
|
|
144
|
+
| `filePatterns` | `string[]` | `['.vue', '.nvue']` | File matching patterns |
|
|
145
|
+
|
|
146
|
+
## Development
|
|
147
|
+
|
|
148
|
+
### Install Dependencies
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm install
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Build
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npm run build
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Test
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npm run test # watch mode
|
|
164
|
+
npm run test:run # single run
|
|
165
|
+
npm run test:coverage # coverage report
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Code Formatting
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npm run format # format code
|
|
172
|
+
npm run format:check # check formatting
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
[MIT License](LICENSE)
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# @i18next-plugin/vue
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@i18next-plugin/vue) [](LICENSE) [](https://vuejs.org/) [](https://vuejs.org/)
|
|
4
|
+
|
|
5
|
+
[English](README.md) | [中文](README.zh-CN.md)
|
|
6
|
+
|
|
7
|
+
i18next-cli 插件,用于从 Vue 单文件组件 (SFC) 中提取 i18n 翻译键。
|
|
8
|
+
|
|
9
|
+
## 特性
|
|
10
|
+
|
|
11
|
+
- **完整 Vue 支持**: 支持 Vue 2.6+ 和 Vue 3.x
|
|
12
|
+
- **双模式解析**: 自动处理 `<script>` 和 `<template>` 中的翻译调用
|
|
13
|
+
- **多种语法**: 支持 `t()` 函数、`data-i18n` 属性、动态绑定等
|
|
14
|
+
- **TypeScript 支持**: 完整的类型定义
|
|
15
|
+
- **高度可配置**: 自定义函数名、属性名、文件模式等
|
|
16
|
+
|
|
17
|
+
## 安装
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @i18next-plugin/vue --save-dev
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 使用方法
|
|
24
|
+
|
|
25
|
+
### 基本配置
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
// i18next.config.js
|
|
29
|
+
import { defineConfig } from 'i18next-cli';
|
|
30
|
+
import i18nextVuePlugin from '@i18next-plugin/vue';
|
|
31
|
+
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
locales: ['en', 'zh', 'fr'],
|
|
34
|
+
extract: {
|
|
35
|
+
input: 'src/**/*.{vue,ts}',
|
|
36
|
+
output: 'locales/{{language}}.json',
|
|
37
|
+
defaultNS: 'translation',
|
|
38
|
+
},
|
|
39
|
+
plugins: [i18nextVuePlugin()],
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 完整配置
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
// i18next.config.js
|
|
47
|
+
import { defineConfig } from 'i18next-cli';
|
|
48
|
+
import i18nextVuePlugin from '@i18next-plugin/vue';
|
|
49
|
+
|
|
50
|
+
export default defineConfig({
|
|
51
|
+
locales: ['en', 'zh'],
|
|
52
|
+
extract: {
|
|
53
|
+
input: 'src/**/*.{vue,ts,js}',
|
|
54
|
+
output: 'locales/{{language}}/{{ns}}.json',
|
|
55
|
+
defaultNS: 'common',
|
|
56
|
+
},
|
|
57
|
+
plugins: [
|
|
58
|
+
i18nextVuePlugin({
|
|
59
|
+
// 显式指定 Vue 版本 (2 | 3)
|
|
60
|
+
vueVersion: 3,
|
|
61
|
+
|
|
62
|
+
// 是否解析动态绑定属性 (`:attr`, `v-bind:attr`)
|
|
63
|
+
vueBindAttr: true,
|
|
64
|
+
|
|
65
|
+
// 翻译函数名列表
|
|
66
|
+
functions: ['t', '$t'],
|
|
67
|
+
|
|
68
|
+
// 命名空间函数名列表
|
|
69
|
+
namespaceFunctions: ['useTranslation', 'withTranslation'],
|
|
70
|
+
|
|
71
|
+
// HTML 属性名
|
|
72
|
+
attr: 'data-i18n',
|
|
73
|
+
|
|
74
|
+
// 选项属性名
|
|
75
|
+
optionAttr: 'data-i18n-options',
|
|
76
|
+
|
|
77
|
+
// 匹配的文件模式
|
|
78
|
+
filePatterns: ['.vue', '.nvue'],
|
|
79
|
+
}),
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 支持的语法
|
|
85
|
+
|
|
86
|
+
### 模板语法
|
|
87
|
+
|
|
88
|
+
```vue
|
|
89
|
+
<template>
|
|
90
|
+
<!-- data-i18n 属性 -->
|
|
91
|
+
<h1 data-i18n="welcome.title">Welcome</h1>
|
|
92
|
+
|
|
93
|
+
<!-- 多个键 (分号分隔) -->
|
|
94
|
+
<p data-i18n="greeting;welcome.message"></p>
|
|
95
|
+
|
|
96
|
+
<!-- 动态绑定 :attr -->
|
|
97
|
+
<button :aria-label="t('button.submit')">Submit</button>
|
|
98
|
+
|
|
99
|
+
<!-- v-bind:attr -->
|
|
100
|
+
<input v-bind:placeholder="t('input.placeholder')" />
|
|
101
|
+
|
|
102
|
+
<!-- v-on:click -->
|
|
103
|
+
<button @click="t('button.click')">Click</button>
|
|
104
|
+
</template>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 脚本语法
|
|
108
|
+
|
|
109
|
+
```vue
|
|
110
|
+
<script>
|
|
111
|
+
import { useTranslation } from 'vue-i18next';
|
|
112
|
+
|
|
113
|
+
export default {
|
|
114
|
+
setup() {
|
|
115
|
+
const { t } = useTranslation('namespace');
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
// 简单键
|
|
119
|
+
title: t('welcome.message'),
|
|
120
|
+
|
|
121
|
+
// 带默认值
|
|
122
|
+
greeting: t('greeting', 'Hello World'),
|
|
123
|
+
|
|
124
|
+
// 带命名空间前缀
|
|
125
|
+
namespaced: t('shared:key'),
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
</script>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## API
|
|
133
|
+
|
|
134
|
+
### 选项
|
|
135
|
+
|
|
136
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
137
|
+
| -------------------- | --------------------------------- | --------------------------------------- | ---------------- |
|
|
138
|
+
| `vueVersion` | `2` | `3` | `undefined` | `undefined` | 指定 Vue 版本 |
|
|
139
|
+
| `vueBindAttr` | `boolean` | `true` | 是否解析动态绑定 |
|
|
140
|
+
| `functions` | `string[]` | `['t', '$t']` | 翻译函数名 |
|
|
141
|
+
| `namespaceFunctions` | `string[]` | `['useTranslation', 'withTranslation']` | 命名空间函数 |
|
|
142
|
+
| `attr` | `string` | `'data-i18n'` | i18n 属性名 |
|
|
143
|
+
| `optionAttr` | `string` | `'data-i18n-options'` | 选项属性名 |
|
|
144
|
+
| `filePatterns` | `string[]` | `['.vue', '.nvue']` | 文件匹配模式 |
|
|
145
|
+
|
|
146
|
+
## 开发
|
|
147
|
+
|
|
148
|
+
### 安装依赖
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm install
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 构建
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npm run build
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 测试
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npm run test # watch 模式
|
|
164
|
+
npm run test:run # 单次运行
|
|
165
|
+
npm run test:coverage # 覆盖率报告
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 代码格式化
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npm run format # 格式化代码
|
|
172
|
+
npm run format:check # 检查格式化
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## 许可证
|
|
176
|
+
|
|
177
|
+
[MIT License](LICENSE)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Plugin } from 'i18next-cli';
|
|
2
|
+
import type { VuePluginOptions, NormalizedVuePluginOptions } from './types';
|
|
3
|
+
export default function i18nextVuePlugin(options?: VuePluginOptions): Plugin;
|
|
4
|
+
export declare function extractKeysFromExpression(expression: string, options: NormalizedVuePluginOptions): string[];
|
|
5
|
+
export declare function extractContextFromExpression(expression: string, options: NormalizedVuePluginOptions): string[];
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { parse } from '@vue/compiler-sfc';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_OPTIONS = {
|
|
4
|
+
vueVersion: undefined,
|
|
5
|
+
vueBindAttr: true,
|
|
6
|
+
functions: ['t', '$t'],
|
|
7
|
+
namespaceFunctions: ['useTranslation', 'withTranslation'],
|
|
8
|
+
attr: 'data-i18n',
|
|
9
|
+
optionAttr: 'data-i18n-options',
|
|
10
|
+
filePatterns: ['.vue', '.nvue'],
|
|
11
|
+
};
|
|
12
|
+
function normalizeOptions(options = {}) {
|
|
13
|
+
return {
|
|
14
|
+
vueVersion: options.vueVersion ?? DEFAULT_OPTIONS.vueVersion,
|
|
15
|
+
vueBindAttr: options.vueBindAttr ?? DEFAULT_OPTIONS.vueBindAttr,
|
|
16
|
+
functions: options.functions ?? DEFAULT_OPTIONS.functions,
|
|
17
|
+
namespaceFunctions: options.namespaceFunctions ?? DEFAULT_OPTIONS.namespaceFunctions,
|
|
18
|
+
attr: options.attr ?? DEFAULT_OPTIONS.attr,
|
|
19
|
+
optionAttr: options.optionAttr ?? DEFAULT_OPTIONS.optionAttr,
|
|
20
|
+
filePatterns: options.filePatterns ?? DEFAULT_OPTIONS.filePatterns,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function validateOptions(options) {
|
|
24
|
+
if (options.vueVersion !== undefined && options.vueVersion !== 2 && options.vueVersion !== 3) {
|
|
25
|
+
throw new Error(`Invalid vueVersion: ${options.vueVersion}. Expected 2 or 3.`);
|
|
26
|
+
}
|
|
27
|
+
if (!Array.isArray(options.functions) || options.functions.length === 0) {
|
|
28
|
+
throw new Error('functions must be a non-empty array');
|
|
29
|
+
}
|
|
30
|
+
if (!Array.isArray(options.namespaceFunctions) || options.namespaceFunctions.length === 0) {
|
|
31
|
+
throw new Error('namespaceFunctions must be a non-empty array');
|
|
32
|
+
}
|
|
33
|
+
if (typeof options.attr !== 'string' || options.attr.length === 0) {
|
|
34
|
+
throw new Error('attr must be a non-empty string');
|
|
35
|
+
}
|
|
36
|
+
if (typeof options.optionAttr !== 'string' || options.optionAttr.length === 0) {
|
|
37
|
+
throw new Error('optionAttr must be a non-empty string');
|
|
38
|
+
}
|
|
39
|
+
if (!Array.isArray(options.filePatterns) || options.filePatterns.length === 0) {
|
|
40
|
+
throw new Error('filePatterns must be a non-empty array');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function extractTemplateKeys(template, options) {
|
|
45
|
+
if (!template) {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
const lines = [];
|
|
49
|
+
const i18nAttr = options.attr;
|
|
50
|
+
const bindEnabled = options.vueBindAttr;
|
|
51
|
+
const attrRegex = /([a-zA-Z0-9_:@-]+)=(["'])(.*?)\2/g;
|
|
52
|
+
let pos = 0;
|
|
53
|
+
while (pos < template.length) {
|
|
54
|
+
const tagOpen = template.indexOf('<', pos);
|
|
55
|
+
if (tagOpen === -1)
|
|
56
|
+
break;
|
|
57
|
+
const tagContentStart = tagOpen + 1;
|
|
58
|
+
const tagClose = template.indexOf('>', tagContentStart);
|
|
59
|
+
if (tagClose === -1)
|
|
60
|
+
break;
|
|
61
|
+
const tagContent = template.slice(tagContentStart, tagClose);
|
|
62
|
+
const isClosingTag = tagContent.trim().startsWith('/');
|
|
63
|
+
if (!isClosingTag) {
|
|
64
|
+
const tagNameMatch = tagContent.match(/^([a-zA-Z][a-zA-Z0-9-]*)/);
|
|
65
|
+
const tagName = tagNameMatch ? tagNameMatch[1] : '';
|
|
66
|
+
if (tagName && tagName !== 'template' && tagName !== 'script') {
|
|
67
|
+
let attrMatch;
|
|
68
|
+
while ((attrMatch = attrRegex.exec(tagContent)) !== null) {
|
|
69
|
+
const name = attrMatch[1];
|
|
70
|
+
const value = attrMatch[3];
|
|
71
|
+
if (name === i18nAttr && value) {
|
|
72
|
+
const keys = value
|
|
73
|
+
.split(';')
|
|
74
|
+
.map((k) => k.trim())
|
|
75
|
+
.filter(Boolean);
|
|
76
|
+
for (const key of keys) {
|
|
77
|
+
lines.push(`t('${key}')`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (bindEnabled) {
|
|
81
|
+
const isVBind = name.startsWith('v-bind:');
|
|
82
|
+
const isColon = name.startsWith(':');
|
|
83
|
+
const isVOn = name.startsWith('v-on:');
|
|
84
|
+
const isAt = name.startsWith('@');
|
|
85
|
+
if (isVBind || isColon || isVOn || isAt) {
|
|
86
|
+
const functions = options.functions ?? [];
|
|
87
|
+
if (isTranslationExpression(value, functions)) {
|
|
88
|
+
lines.push(value);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
pos = tagClose + 1;
|
|
96
|
+
}
|
|
97
|
+
return lines.join('\n');
|
|
98
|
+
}
|
|
99
|
+
function isTranslationExpression(expr, functions) {
|
|
100
|
+
const trimmed = expr.trim();
|
|
101
|
+
for (const func of functions) {
|
|
102
|
+
if (trimmed.startsWith(`${func}(`)) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let vueTemplateCompiler = null;
|
|
110
|
+
function loadVueTemplateCompiler() {
|
|
111
|
+
if (!vueTemplateCompiler) {
|
|
112
|
+
try {
|
|
113
|
+
vueTemplateCompiler = require('vue-template-compiler');
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
vueTemplateCompiler = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return vueTemplateCompiler;
|
|
120
|
+
}
|
|
121
|
+
function parseVue2SFC(code) {
|
|
122
|
+
const compiler = loadVueTemplateCompiler();
|
|
123
|
+
if (!compiler) {
|
|
124
|
+
return {
|
|
125
|
+
template: extractVueTemplate(code),
|
|
126
|
+
script: extractVueScript(code),
|
|
127
|
+
styles: [],
|
|
128
|
+
customBlocks: [],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const result = compiler.parseComponent(code);
|
|
132
|
+
return {
|
|
133
|
+
template: result.template?.content || '',
|
|
134
|
+
script: result.script?.content || '',
|
|
135
|
+
styles: result.styles.map((style) => style.content),
|
|
136
|
+
customBlocks: result.customBlocks.map((block) => ({
|
|
137
|
+
type: block.type,
|
|
138
|
+
content: block.content,
|
|
139
|
+
})),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function extractVueTemplate(code) {
|
|
143
|
+
const templateMatch = code.match(/<template[^>]*>([\s\S]*?)<\/template>/);
|
|
144
|
+
return templateMatch ? templateMatch[1] : '';
|
|
145
|
+
}
|
|
146
|
+
function extractVueScript(code) {
|
|
147
|
+
const scriptMatch = code.match(/<script[^>]*>([\s\S]*?)<\/script>/);
|
|
148
|
+
return scriptMatch ? scriptMatch[1] : '';
|
|
149
|
+
}
|
|
150
|
+
function createVue2Parser(code, options) {
|
|
151
|
+
const descriptor = parseVue2SFC(code);
|
|
152
|
+
return {
|
|
153
|
+
extractScript() {
|
|
154
|
+
return descriptor.script;
|
|
155
|
+
},
|
|
156
|
+
extractTemplate() {
|
|
157
|
+
return descriptor.template;
|
|
158
|
+
},
|
|
159
|
+
extractStyles() {
|
|
160
|
+
return descriptor.styles;
|
|
161
|
+
},
|
|
162
|
+
extractCustomBlocks() {
|
|
163
|
+
return descriptor.customBlocks.map((block) => ({
|
|
164
|
+
type: block.type,
|
|
165
|
+
content: block.content,
|
|
166
|
+
}));
|
|
167
|
+
},
|
|
168
|
+
generateVirtualJS() {
|
|
169
|
+
return extractTemplateKeys(descriptor.template, options);
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function createVue3Parser(code, options) {
|
|
175
|
+
const result = parse(code);
|
|
176
|
+
const descriptor = result.descriptor;
|
|
177
|
+
return {
|
|
178
|
+
extractScript() {
|
|
179
|
+
const scriptParts = [];
|
|
180
|
+
if (descriptor.script?.content) {
|
|
181
|
+
scriptParts.push(descriptor.script.content);
|
|
182
|
+
}
|
|
183
|
+
if (descriptor.scriptSetup?.content) {
|
|
184
|
+
scriptParts.push(descriptor.scriptSetup.content);
|
|
185
|
+
}
|
|
186
|
+
return scriptParts.join('\n');
|
|
187
|
+
},
|
|
188
|
+
extractTemplate() {
|
|
189
|
+
return descriptor.template?.content || '';
|
|
190
|
+
},
|
|
191
|
+
extractStyles() {
|
|
192
|
+
return descriptor.styles.map((style) => style.content);
|
|
193
|
+
},
|
|
194
|
+
extractCustomBlocks() {
|
|
195
|
+
return descriptor.customBlocks.map((block) => ({
|
|
196
|
+
type: block.type,
|
|
197
|
+
content: block.content,
|
|
198
|
+
}));
|
|
199
|
+
},
|
|
200
|
+
generateVirtualJS() {
|
|
201
|
+
const template = descriptor.template?.content || '';
|
|
202
|
+
return extractTemplateKeys(template, options);
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function createParser(vueVersion, code, options) {
|
|
208
|
+
if (vueVersion === 2) {
|
|
209
|
+
return createVue2Parser(code, options);
|
|
210
|
+
}
|
|
211
|
+
return createVue3Parser(code, options);
|
|
212
|
+
}
|
|
213
|
+
function detectVueVersion(code) {
|
|
214
|
+
if (code.includes('<script setup')) {
|
|
215
|
+
return 3;
|
|
216
|
+
}
|
|
217
|
+
if (code.includes('defineComponent')) {
|
|
218
|
+
return 3;
|
|
219
|
+
}
|
|
220
|
+
const scriptMatch = code.match(/<script[^>]*>([\s\S]*?)<\/script>/);
|
|
221
|
+
if (scriptMatch) {
|
|
222
|
+
const scriptContent = scriptMatch[1];
|
|
223
|
+
if (scriptContent.includes('data()')) {
|
|
224
|
+
return 2;
|
|
225
|
+
}
|
|
226
|
+
if (scriptContent.includes('export default')) {
|
|
227
|
+
return scriptContent.includes('setup(') ? 3 : 2;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return 3;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function extractKeysFromExpression$1(expression, options) {
|
|
234
|
+
const keys = [];
|
|
235
|
+
const functions = options.functions;
|
|
236
|
+
const funcPattern = new RegExp(`\\b(${functions.join('|')})\\s*\\(\\s*['"]([^'"]+)['"]`, 'g');
|
|
237
|
+
let match;
|
|
238
|
+
while ((match = funcPattern.exec(expression)) !== null) {
|
|
239
|
+
keys.push(match[2]);
|
|
240
|
+
}
|
|
241
|
+
return keys;
|
|
242
|
+
}
|
|
243
|
+
function extractContextFromExpression$1(expression, options) {
|
|
244
|
+
const contexts = [];
|
|
245
|
+
const contextPattern = /context\s*:\s*['"]([^'"]+)['"]/g;
|
|
246
|
+
let match;
|
|
247
|
+
while ((match = contextPattern.exec(expression)) !== null) {
|
|
248
|
+
contexts.push(match[1]);
|
|
249
|
+
}
|
|
250
|
+
return contexts;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function isVueFile(path, patterns) {
|
|
254
|
+
return patterns.some((pattern) => {
|
|
255
|
+
if (pattern.startsWith('.')) {
|
|
256
|
+
return path.endsWith(pattern);
|
|
257
|
+
}
|
|
258
|
+
return path.includes(pattern);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function createOnLoadHandler(normalizedOptions) {
|
|
263
|
+
return (code, path) => {
|
|
264
|
+
if (!isVueFile(path, normalizedOptions.filePatterns)) {
|
|
265
|
+
return code;
|
|
266
|
+
}
|
|
267
|
+
const vueVersion = normalizedOptions.vueVersion || detectVueVersion(code);
|
|
268
|
+
const parser = createParser(vueVersion, code, normalizedOptions);
|
|
269
|
+
const scriptContent = parser.extractScript();
|
|
270
|
+
const virtualJS = parser.generateVirtualJS();
|
|
271
|
+
if (scriptContent && virtualJS) {
|
|
272
|
+
return `${scriptContent}\n${virtualJS}`;
|
|
273
|
+
}
|
|
274
|
+
return scriptContent || virtualJS;
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function i18nextVuePlugin(options = {}) {
|
|
278
|
+
const normalizedOptions = normalizeOptions(options);
|
|
279
|
+
validateOptions(normalizedOptions);
|
|
280
|
+
return {
|
|
281
|
+
name: '@i18next-plugin/vue',
|
|
282
|
+
onLoad: createOnLoadHandler(normalizedOptions),
|
|
283
|
+
extractKeysFromExpression: (_expression, _config, _logger) => {
|
|
284
|
+
return extractKeysFromExpression$1(_expression, normalizedOptions);
|
|
285
|
+
},
|
|
286
|
+
extractContextFromExpression: (_expression, _config, _logger) => {
|
|
287
|
+
return extractContextFromExpression$1(_expression);
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function extractKeysFromExpression(expression, options) {
|
|
292
|
+
return extractKeysFromExpression$1(expression, options);
|
|
293
|
+
}
|
|
294
|
+
function extractContextFromExpression(expression, options) {
|
|
295
|
+
return extractContextFromExpression$1(expression);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export { i18nextVuePlugin as default, extractContextFromExpression, extractKeysFromExpression };
|
|
299
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/options.ts","../src/template/extract.ts","../src/sfc/vue2.ts","../src/sfc/vue3.ts","../src/sfc/parser.ts","../src/script/extract.ts","../src/utils/index.ts","../src/index.ts"],"sourcesContent":["import type { VuePluginOptions, NormalizedVuePluginOptions } from './types';\n\nexport const DEFAULT_OPTIONS: NormalizedVuePluginOptions = {\n\tvueVersion: undefined,\n\tvueBindAttr: true,\n\tfunctions: ['t', '$t'],\n\tnamespaceFunctions: ['useTranslation', 'withTranslation'],\n\tattr: 'data-i18n',\n\toptionAttr: 'data-i18n-options',\n\tfilePatterns: ['.vue', '.nvue'],\n};\n\nexport function normalizeOptions(options: VuePluginOptions = {}): NormalizedVuePluginOptions {\n\treturn {\n\t\tvueVersion: options.vueVersion ?? DEFAULT_OPTIONS.vueVersion,\n\t\tvueBindAttr: options.vueBindAttr ?? DEFAULT_OPTIONS.vueBindAttr,\n\t\tfunctions: options.functions ?? DEFAULT_OPTIONS.functions,\n\t\tnamespaceFunctions: options.namespaceFunctions ?? DEFAULT_OPTIONS.namespaceFunctions,\n\t\tattr: options.attr ?? DEFAULT_OPTIONS.attr,\n\t\toptionAttr: options.optionAttr ?? DEFAULT_OPTIONS.optionAttr,\n\t\tfilePatterns: options.filePatterns ?? DEFAULT_OPTIONS.filePatterns,\n\t};\n}\n\nexport function validateOptions(options: NormalizedVuePluginOptions): void {\n\tif (options.vueVersion !== undefined && options.vueVersion !== 2 && options.vueVersion !== 3) {\n\t\tthrow new Error(`Invalid vueVersion: ${options.vueVersion}. Expected 2 or 3.`);\n\t}\n\n\tif (!Array.isArray(options.functions) || options.functions.length === 0) {\n\t\tthrow new Error('functions must be a non-empty array');\n\t}\n\n\tif (!Array.isArray(options.namespaceFunctions) || options.namespaceFunctions.length === 0) {\n\t\tthrow new Error('namespaceFunctions must be a non-empty array');\n\t}\n\n\tif (typeof options.attr !== 'string' || options.attr.length === 0) {\n\t\tthrow new Error('attr must be a non-empty string');\n\t}\n\n\tif (typeof options.optionAttr !== 'string' || options.optionAttr.length === 0) {\n\t\tthrow new Error('optionAttr must be a non-empty string');\n\t}\n\n\tif (!Array.isArray(options.filePatterns) || options.filePatterns.length === 0) {\n\t\tthrow new Error('filePatterns must be a non-empty array');\n\t}\n}\n","import type { VuePluginOptions } from '../types';\n\nexport function extractTemplateKeys(template: string, options: VuePluginOptions): string {\n\tif (!template) {\n\t\treturn '';\n\t}\n\n\tconst lines: string[] = [];\n\tconst i18nAttr = options.attr;\n\tconst bindEnabled = options.vueBindAttr;\n\n\tconst attrRegex = /([a-zA-Z0-9_:@-]+)=([\"'])(.*?)\\2/g;\n\n\tlet pos = 0;\n\twhile (pos < template.length) {\n\t\tconst tagOpen = template.indexOf('<', pos);\n\t\tif (tagOpen === -1) break;\n\n\t\tconst tagContentStart = tagOpen + 1;\n\t\tconst tagClose = template.indexOf('>', tagContentStart);\n\t\tif (tagClose === -1) break;\n\n\t\tconst tagContent = template.slice(tagContentStart, tagClose);\n\t\tconst isClosingTag = tagContent.trim().startsWith('/');\n\n\t\tif (!isClosingTag) {\n\t\t\tconst tagNameMatch = tagContent.match(/^([a-zA-Z][a-zA-Z0-9-]*)/);\n\t\t\tconst tagName = tagNameMatch ? tagNameMatch[1] : '';\n\n\t\t\tif (tagName && tagName !== 'template' && tagName !== 'script') {\n\t\t\t\tlet attrMatch;\n\t\t\t\twhile ((attrMatch = attrRegex.exec(tagContent)) !== null) {\n\t\t\t\t\tconst name = attrMatch[1];\n\t\t\t\t\tconst value = attrMatch[3];\n\n\t\t\t\t\tif (name === i18nAttr && value) {\n\t\t\t\t\t\tconst keys = value\n\t\t\t\t\t\t\t.split(';')\n\t\t\t\t\t\t\t.map((k) => k.trim())\n\t\t\t\t\t\t\t.filter(Boolean);\n\t\t\t\t\t\tfor (const key of keys) {\n\t\t\t\t\t\t\tlines.push(`t('${key}')`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (bindEnabled) {\n\t\t\t\t\t\tconst isVBind = name.startsWith('v-bind:');\n\t\t\t\t\t\tconst isColon = name.startsWith(':');\n\t\t\t\t\t\tconst isVOn = name.startsWith('v-on:');\n\t\t\t\t\t\tconst isAt = name.startsWith('@');\n\n\t\t\t\t\t\tif (isVBind || isColon || isVOn || isAt) {\n\t\t\t\t\t\t\tconst functions = options.functions ?? [];\n\t\t\t\t\t\t\tif (isTranslationExpression(value, functions)) {\n\t\t\t\t\t\t\t\tlines.push(value);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpos = tagClose + 1;\n\t}\n\n\treturn lines.join('\\n');\n}\n\nfunction isTranslationExpression(expr: string, functions: string[]): boolean {\n\tconst trimmed = expr.trim();\n\tfor (const func of functions) {\n\t\tif (trimmed.startsWith(`${func}(`)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n","import type { VueSFCParser, VuePluginOptions, VueSFCPart } from '../types';\nimport { extractTemplateKeys } from '../template/extract';\n\ninterface Vue2Descriptor {\n\ttemplate: string;\n\tscript: string;\n\tstyles: string[];\n\tcustomBlocks: { type: string; content: string }[];\n}\n\nlet vueTemplateCompiler: typeof import('vue-template-compiler') | null = null;\n\nfunction loadVueTemplateCompiler() {\n\tif (!vueTemplateCompiler) {\n\t\ttry {\n\t\t\tvueTemplateCompiler = require('vue-template-compiler');\n\t\t} catch {\n\t\t\tvueTemplateCompiler = null;\n\t\t}\n\t}\n\treturn vueTemplateCompiler;\n}\n\nfunction parseVue2SFC(code: string): Vue2Descriptor {\n\tconst compiler = loadVueTemplateCompiler();\n\n\tif (!compiler) {\n\t\treturn {\n\t\t\ttemplate: extractVueTemplate(code),\n\t\t\tscript: extractVueScript(code),\n\t\t\tstyles: [],\n\t\t\tcustomBlocks: [],\n\t\t};\n\t}\n\n\tconst result = compiler.parseComponent(code);\n\n\treturn {\n\t\ttemplate: result.template?.content || '',\n\t\tscript: result.script?.content || '',\n\t\tstyles: result.styles.map((style: { content: string }) => style.content),\n\t\tcustomBlocks: result.customBlocks.map((block: { type: string; content: string }) => ({\n\t\t\ttype: block.type,\n\t\t\tcontent: block.content,\n\t\t})),\n\t};\n}\n\nfunction extractVueTemplate(code: string): string {\n\tconst templateMatch = code.match(/<template[^>]*>([\\s\\S]*?)<\\/template>/);\n\treturn templateMatch ? templateMatch[1] : '';\n}\n\nfunction extractVueScript(code: string): string {\n\tconst scriptMatch = code.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/);\n\treturn scriptMatch ? scriptMatch[1] : '';\n}\n\nexport function createVue2Parser(code: string, options: VuePluginOptions): VueSFCParser {\n\tconst descriptor = parseVue2SFC(code);\n\n\treturn {\n\t\textractScript(): string {\n\t\t\treturn descriptor.script;\n\t\t},\n\n\t\textractTemplate(): string {\n\t\t\treturn descriptor.template;\n\t\t},\n\n\t\textractStyles(): string[] {\n\t\t\treturn descriptor.styles;\n\t\t},\n\n\t\textractCustomBlocks(): VueSFCPart[] {\n\t\t\treturn descriptor.customBlocks.map((block) => ({\n\t\t\t\ttype: block.type as VueSFCPart['type'],\n\t\t\t\tcontent: block.content,\n\t\t\t}));\n\t\t},\n\n\t\tgenerateVirtualJS(): string {\n\t\t\treturn extractTemplateKeys(descriptor.template, options);\n\t\t},\n\t};\n}\n","import { parse } from '@vue/compiler-sfc';\nimport type { VueSFCParser, VuePluginOptions, VueSFCPart } from '../types';\nimport { extractTemplateKeys } from '../template/extract';\n\ninterface SFCParseResult {\n\tdescriptor: {\n\t\ttemplate: { content: string } | null;\n\t\tscript: { content: string } | null;\n\t\tscriptSetup: { content: string } | null;\n\t\tstyles: Array<{ content: string }>;\n\t\tcustomBlocks: Array<{ type: string; content: string }>;\n\t};\n}\n\nexport function createVue3Parser(code: string, options: VuePluginOptions): VueSFCParser {\n\tconst result = parse(code) as unknown as SFCParseResult;\n\tconst descriptor = result.descriptor;\n\n\treturn {\n\t\textractScript(): string {\n\t\t\tconst scriptParts: string[] = [];\n\n\t\t\tif (descriptor.script?.content) {\n\t\t\t\tscriptParts.push(descriptor.script.content);\n\t\t\t}\n\n\t\t\tif (descriptor.scriptSetup?.content) {\n\t\t\t\tscriptParts.push(descriptor.scriptSetup.content);\n\t\t\t}\n\n\t\t\treturn scriptParts.join('\\n');\n\t\t},\n\n\t\textractTemplate(): string {\n\t\t\treturn descriptor.template?.content || '';\n\t\t},\n\n\t\textractStyles(): string[] {\n\t\t\treturn descriptor.styles.map((style) => style.content);\n\t\t},\n\n\t\textractCustomBlocks(): VueSFCPart[] {\n\t\t\treturn descriptor.customBlocks.map((block) => ({\n\t\t\t\ttype: block.type as VueSFCPart['type'],\n\t\t\t\tcontent: block.content,\n\t\t\t}));\n\t\t},\n\n\t\tgenerateVirtualJS(): string {\n\t\t\tconst template = descriptor.template?.content || '';\n\t\t\treturn extractTemplateKeys(template, options);\n\t\t},\n\t};\n}\n","import type { VuePluginOptions, VueSFCParser } from '../types';\nimport { createVue2Parser } from './vue2';\nimport { createVue3Parser } from './vue3';\n\nexport function createParser(vueVersion: 2 | 3, code: string, options: VuePluginOptions): VueSFCParser {\n\tif (vueVersion === 2) {\n\t\treturn createVue2Parser(code, options);\n\t}\n\treturn createVue3Parser(code, options);\n}\n\nexport function detectVueVersion(code: string): 2 | 3 {\n\tif (code.includes('<script setup')) {\n\t\treturn 3;\n\t}\n\n\tif (code.includes('defineComponent')) {\n\t\treturn 3;\n\t}\n\n\tconst scriptMatch = code.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/);\n\tif (scriptMatch) {\n\t\tconst scriptContent = scriptMatch[1];\n\t\tif (scriptContent.includes('data()')) {\n\t\t\treturn 2;\n\t\t}\n\t\tif (scriptContent.includes('export default')) {\n\t\t\treturn scriptContent.includes('setup(') ? 3 : 2;\n\t\t}\n\t}\n\n\treturn 3;\n}\n","import type { NormalizedVuePluginOptions, ExtractedKeyInfo } from '../types';\n\nexport function extractScriptKeys(script: string, options: NormalizedVuePluginOptions): ExtractedKeyInfo[] {\n\tconst keys: ExtractedKeyInfo[] = [];\n\n\tconst functions = options.functions;\n\tconst namespaceFunctions = options.namespaceFunctions;\n\n\tconst functionCalls = extractFunctionCalls(script, functions);\n\n\tfor (const call of functionCalls) {\n\t\tconst keyInfo = parseFunctionCall(call, options);\n\t\tif (keyInfo) {\n\t\t\tkeys.push(keyInfo);\n\t\t}\n\t}\n\n\treturn keys;\n}\n\ninterface FunctionCall {\n\tfunctionName: string;\n\tkey: string;\n\tdefaultValue?: string;\n\toptions?: Record<string, unknown>;\n\tstart: number;\n\tend: number;\n}\n\nfunction extractFunctionCalls(script: string, functions: string[]): FunctionCall[] {\n\tconst calls: FunctionCall[] = [];\n\n\tconst funcPattern = new RegExp(`\\\\b(${functions.join('|')})\\\\s*\\\\(([^)]+)\\\\)`, 'g');\n\n\tlet match;\n\twhile ((match = funcPattern.exec(script)) !== null) {\n\t\tconst fullMatch = match[0];\n\t\tconst functionName = match[1];\n\t\tconst argsStr = match[2];\n\n\t\tconst keyResult = extractKeyFromArgs(argsStr);\n\t\tif (keyResult) {\n\t\t\tcalls.push({\n\t\t\t\tfunctionName,\n\t\t\t\tkey: keyResult.key,\n\t\t\t\tdefaultValue: keyResult.defaultValue,\n\t\t\t\toptions: keyResult.options,\n\t\t\t\tstart: match.index,\n\t\t\t\tend: match.index + fullMatch.length,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn calls;\n}\n\nfunction extractKeyFromArgs(argsStr: string): { key: string; defaultValue?: string; options?: Record<string, unknown> } | null {\n\tconst trimmed = argsStr.trim();\n\n\tconst keyMatch = trimmed.match(/^['\"]([^'\"]*)['\"]/);\n\n\tif (!keyMatch) {\n\t\treturn null;\n\t}\n\n\tconst key = keyMatch[1];\n\tconst afterKey = trimmed.slice(keyMatch[0].length).trim();\n\n\tif (!afterKey) {\n\t\treturn { key };\n\t}\n\n\tif (afterKey.startsWith(',')) {\n\t\tconst rest = afterKey.slice(1).trim();\n\n\t\tif (rest.startsWith('{')) {\n\t\t\tconst optionsEnd = findMatchingBrace(rest);\n\t\t\tif (optionsEnd !== -1) {\n\t\t\t\ttry {\n\t\t\t\t\tconst optionsStr = rest.slice(0, optionsEnd);\n\t\t\t\t\tconst parsedOptions = parseOptions(optionsStr);\n\t\t\t\t\tif (parsedOptions) {\n\t\t\t\t\t\tconst defaultValue = parsedOptions.defaultValue !== undefined ? String(parsedOptions.defaultValue) : undefined;\n\t\t\t\t\t\treturn { key, defaultValue, options: parsedOptions };\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\treturn { key };\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst defaultValueMatch = rest.match(/^['\"]([^'\"]*)['\"]/);\n\t\t\tif (defaultValueMatch) {\n\t\t\t\treturn { key, defaultValue: defaultValueMatch[1] };\n\t\t\t}\n\t\t\treturn { key };\n\t\t}\n\t}\n\n\treturn { key };\n}\n\nfunction findMatchingBrace(str: string): number {\n\tlet depth = 0;\n\tlet inString = false;\n\tlet stringChar: string | null = null;\n\n\tfor (let i = 0; i < str.length; i++) {\n\t\tconst char = str[i];\n\n\t\tif (inString) {\n\t\t\tif (char === stringChar && str[i - 1] !== '\\\\') {\n\t\t\t\tinString = false;\n\t\t\t\tstringChar = null;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (char === '\"' || char === \"'\") {\n\t\t\tinString = true;\n\t\t\tstringChar = char;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (char === '{') {\n\t\t\tdepth++;\n\t\t} else if (char === '}') {\n\t\t\tdepth--;\n\t\t\tif (depth === 0) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1;\n}\n\nfunction parseOptions(optionsStr: string): Record<string, unknown> | null {\n\tif (!optionsStr.startsWith('{') || !optionsStr.endsWith('}')) {\n\t\treturn null;\n\t}\n\n\tconst inner = optionsStr.slice(1, -1);\n\n\ttry {\n\t\treturn JSON.parse(`{${inner}}`);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction parseFunctionCall(call: FunctionCall, options: NormalizedVuePluginOptions): ExtractedKeyInfo | null {\n\tconst namespace = extractNamespace(call.key);\n\n\tlet key = call.key;\n\tif (namespace) {\n\t\tkey = key.replace(`${namespace}:`, '');\n\t}\n\n\treturn {\n\t\tkey,\n\t\tdefaultValue: call.defaultValue,\n\t\tnamespace,\n\t\toptions: call.options,\n\t};\n}\n\nfunction extractNamespace(key: string): string | undefined {\n\tif (key.includes(':')) {\n\t\treturn key.split(':')[0];\n\t}\n\treturn undefined;\n}\n\nexport function extractKeysFromExpression(expression: string, options: NormalizedVuePluginOptions): string[] {\n\tconst keys: string[] = [];\n\n\tconst functions = options.functions;\n\tconst funcPattern = new RegExp(`\\\\b(${functions.join('|')})\\\\s*\\\\(\\\\s*['\"]([^'\"]+)['\"]`, 'g');\n\n\tlet match;\n\twhile ((match = funcPattern.exec(expression)) !== null) {\n\t\tkeys.push(match[2]);\n\t}\n\n\treturn keys;\n}\n\nexport function extractContextFromExpression(expression: string, options: NormalizedVuePluginOptions): string[] {\n\tconst contexts: string[] = [];\n\n\tconst contextPattern = /context\\s*:\\s*['\"]([^'\"]+)['\"]/g;\n\n\tlet match;\n\twhile ((match = contextPattern.exec(expression)) !== null) {\n\t\tcontexts.push(match[1]);\n\t}\n\n\treturn contexts;\n}\n","export function isVueFile(path: string, patterns: string[]): boolean {\n\treturn patterns.some((pattern) => {\n\t\tif (pattern.startsWith('.')) {\n\t\t\treturn path.endsWith(pattern);\n\t\t}\n\t\treturn path.includes(pattern);\n\t});\n}\n\nexport function extractStringValue(str: string): string | null {\n\tconst match = str.match(/^['\"]([^'\"]*)['\"]/);\n\treturn match ? match[1] : null;\n}\n\nexport function parseJSONOptions(str: string): Record<string, unknown> | null {\n\ttry {\n\t\treturn JSON.parse(str);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function splitKeys(keysStr: string): string[] {\n\treturn keysStr\n\t\t.split(';')\n\t\t.map((k) => k.trim())\n\t\t.filter(Boolean);\n}\n","import type { Plugin } from 'i18next-cli';\nimport type { VuePluginOptions, NormalizedVuePluginOptions } from './types';\nimport { normalizeOptions, validateOptions } from './options';\nimport { createParser, detectVueVersion } from './sfc/parser';\nimport { extractKeysFromExpression as extractKeysFromExpr, extractContextFromExpression as extractContextFromExpr } from './script/extract';\nimport { isVueFile } from './utils';\n\nfunction createOnLoadHandler(normalizedOptions: NormalizedVuePluginOptions): (code: string, path: string) => string {\n\treturn (code: string, path: string): string => {\n\t\tif (!isVueFile(path, normalizedOptions.filePatterns)) {\n\t\t\treturn code;\n\t\t}\n\n\t\tconst vueVersion = normalizedOptions.vueVersion || detectVueVersion(code);\n\t\tconst parser = createParser(vueVersion, code, normalizedOptions);\n\n\t\tconst scriptContent = parser.extractScript();\n\t\tconst virtualJS = parser.generateVirtualJS();\n\n\t\tif (scriptContent && virtualJS) {\n\t\t\treturn `${scriptContent}\\n${virtualJS}`;\n\t\t}\n\n\t\treturn scriptContent || virtualJS;\n\t};\n}\n\nexport default function i18nextVuePlugin(options: VuePluginOptions = {}): Plugin {\n\tconst normalizedOptions = normalizeOptions(options);\n\tvalidateOptions(normalizedOptions);\n\n\treturn {\n\t\tname: '@i18next-plugin/vue',\n\n\t\tonLoad: createOnLoadHandler(normalizedOptions),\n\n\t\textractKeysFromExpression: (_expression: any, _config: any, _logger: any): string[] => {\n\t\t\treturn extractKeysFromExpr(_expression, normalizedOptions);\n\t\t},\n\n\t\textractContextFromExpression: (_expression: any, _config: any, _logger: any): string[] => {\n\t\t\treturn extractContextFromExpr(_expression, normalizedOptions);\n\t\t},\n\t};\n}\n\nexport function extractKeysFromExpression(expression: string, options: NormalizedVuePluginOptions): string[] {\n\treturn extractKeysFromExpr(expression, options);\n}\n\nexport function extractContextFromExpression(expression: string, options: NormalizedVuePluginOptions): string[] {\n\treturn extractContextFromExpr(expression, options);\n}\n"],"names":["extractKeysFromExpression","extractContextFromExpression","extractKeysFromExpr","extractContextFromExpr"],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { VuePluginOptions, NormalizedVuePluginOptions } from './types';
|
|
2
|
+
export declare const DEFAULT_OPTIONS: NormalizedVuePluginOptions;
|
|
3
|
+
export declare function normalizeOptions(options?: VuePluginOptions): NormalizedVuePluginOptions;
|
|
4
|
+
export declare function validateOptions(options: NormalizedVuePluginOptions): void;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { NormalizedVuePluginOptions, ExtractedKeyInfo } from '../types';
|
|
2
|
+
export declare function extractScriptKeys(script: string, options: NormalizedVuePluginOptions): ExtractedKeyInfo[];
|
|
3
|
+
export declare function extractKeysFromExpression(expression: string, options: NormalizedVuePluginOptions): string[];
|
|
4
|
+
export declare function extractContextFromExpression(expression: string, options: NormalizedVuePluginOptions): string[];
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Expression } from 'estree';
|
|
2
|
+
export interface VuePluginOptions {
|
|
3
|
+
vueVersion?: 2 | 3;
|
|
4
|
+
vueBindAttr?: boolean;
|
|
5
|
+
functions?: string[];
|
|
6
|
+
namespaceFunctions?: string[];
|
|
7
|
+
attr?: string;
|
|
8
|
+
optionAttr?: string;
|
|
9
|
+
filePatterns?: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface NormalizedVuePluginOptions {
|
|
12
|
+
vueVersion: 2 | 3 | undefined;
|
|
13
|
+
vueBindAttr: boolean;
|
|
14
|
+
functions: string[];
|
|
15
|
+
namespaceFunctions: string[];
|
|
16
|
+
attr: string;
|
|
17
|
+
optionAttr: string;
|
|
18
|
+
filePatterns: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface VueSFCPart {
|
|
21
|
+
type: 'template' | 'script' | 'scriptSetup' | 'style';
|
|
22
|
+
content: string;
|
|
23
|
+
lang?: string;
|
|
24
|
+
attrs?: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
export interface VueSFCDescriptor {
|
|
27
|
+
template: VueSFCPart | null;
|
|
28
|
+
script: VueSFCPart | null;
|
|
29
|
+
scriptSetup: VueSFCPart | null;
|
|
30
|
+
styles: VueSFCPart[];
|
|
31
|
+
customBlocks: VueSFCPart[];
|
|
32
|
+
}
|
|
33
|
+
export interface VueSFCParser {
|
|
34
|
+
extractScript(): string;
|
|
35
|
+
extractTemplate(): string;
|
|
36
|
+
extractStyles(): string[];
|
|
37
|
+
extractCustomBlocks(): VueSFCPart[];
|
|
38
|
+
generateVirtualJS(): string;
|
|
39
|
+
}
|
|
40
|
+
export interface ExtractedKeyInfo {
|
|
41
|
+
key: string;
|
|
42
|
+
defaultValue?: string;
|
|
43
|
+
namespace?: string;
|
|
44
|
+
options?: Record<string, unknown>;
|
|
45
|
+
context?: string;
|
|
46
|
+
plural?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface TemplateToken {
|
|
49
|
+
type: 'text' | 'tag' | 'attr' | 'directive' | 'binding' | 'interpolation';
|
|
50
|
+
value: string;
|
|
51
|
+
attrName?: string;
|
|
52
|
+
attrValue?: string;
|
|
53
|
+
loc?: {
|
|
54
|
+
start: {
|
|
55
|
+
line: number;
|
|
56
|
+
column: number;
|
|
57
|
+
};
|
|
58
|
+
end: {
|
|
59
|
+
line: number;
|
|
60
|
+
column: number;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export interface FunctionCallMatch {
|
|
65
|
+
functionName: string;
|
|
66
|
+
key: string;
|
|
67
|
+
defaultValue?: string;
|
|
68
|
+
options?: Record<string, unknown>;
|
|
69
|
+
start: number;
|
|
70
|
+
end: number;
|
|
71
|
+
}
|
|
72
|
+
export interface NamespaceInfo {
|
|
73
|
+
functionName: string;
|
|
74
|
+
namespace: string | undefined;
|
|
75
|
+
keyPrefix?: string;
|
|
76
|
+
}
|
|
77
|
+
export type ExtractKeysFunction = (expression: Expression, options: NormalizedVuePluginOptions) => string[];
|
|
78
|
+
export type ExtractContextFunction = (expression: Expression, options: NormalizedVuePluginOptions) => string[];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function isVueFile(path: string, patterns: string[]): boolean;
|
|
2
|
+
export declare function extractStringValue(str: string): string | null;
|
|
3
|
+
export declare function parseJSONOptions(str: string): Record<string, unknown> | null;
|
|
4
|
+
export declare function splitKeys(keysStr: string): string[];
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "module",
|
|
3
|
+
"name": "@i18next-plugin/vue",
|
|
4
|
+
"version": "1.0.2",
|
|
5
|
+
"description": "@i18next-plugin/vue plugin for extracting i18n keys/value from Vue SFC files",
|
|
6
|
+
"author": "PBK-B",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"module": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "rollup -c ./rollup.config.ts",
|
|
21
|
+
"test": "vitest",
|
|
22
|
+
"test:run": "vitest run",
|
|
23
|
+
"test:coverage": "vitest --coverage",
|
|
24
|
+
"lint": "eslint src --ext .ts",
|
|
25
|
+
"format": "prettier --write 'src/**/*.ts'",
|
|
26
|
+
"format:check": "prettier --check 'src/**/*.ts'",
|
|
27
|
+
"release": "changeset version && npm run build && git add -A && git commit -m 'chore: release' && npm run build && npm publish"
|
|
28
|
+
},
|
|
29
|
+
"changeset": {
|
|
30
|
+
"$schema": "https://unpkg.com/@changesets/schema@2.3.0/schema.json",
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"i18next-cli": "^1.11.11",
|
|
35
|
+
"vue": "^2.6.0 || ^3.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@changesets/changelog-github": "^0.5.2",
|
|
39
|
+
"@changesets/cli": "^2.29.8",
|
|
40
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
41
|
+
"@types/node": "^25.1.0",
|
|
42
|
+
"@vue/compiler-sfc": "^3.5.27",
|
|
43
|
+
"prettier": "^3.8.1",
|
|
44
|
+
"rollup": "^4.57.0",
|
|
45
|
+
"tslib": "^2.8.1",
|
|
46
|
+
"typescript": "^5.9.3",
|
|
47
|
+
"vitest": "^4.0.18",
|
|
48
|
+
"vue-template-compiler": "^2.7.16"
|
|
49
|
+
},
|
|
50
|
+
"keywords": [
|
|
51
|
+
"i18next",
|
|
52
|
+
"i18next-cli",
|
|
53
|
+
"vue",
|
|
54
|
+
"vue3",
|
|
55
|
+
"internationalization",
|
|
56
|
+
"translation"
|
|
57
|
+
],
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "https://github.com/PBK-B/@i18next-plugin/vue"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"registry": "https://registry.npmjs.org",
|
|
65
|
+
"access": "public"
|
|
66
|
+
}
|
|
67
|
+
}
|