Skip to content

Commit

Permalink
feat: support user-custom parser plugin
Browse files Browse the repository at this point in the history
1. rename `cwd` to `root`
2. remove `enableParsers` option
3. add `parserPlugins` option for user-custom parser plugin
4. add built-in yaml support
  • Loading branch information
hemengke1997 committed Oct 18, 2023
1 parent e03d566 commit 19d1b49
Show file tree
Hide file tree
Showing 20 changed files with 124 additions and 98 deletions.
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"i18n-ally.localesPaths": ["playground/spa/src/locale"],
"i18n-ally.localesPaths": ["playground/spa/src/locales"],
"i18n-ally.keystyle": "flat",
"i18n-ally.enabledParsers": ["json"],
"i18n-ally.enabledParsers": ["json", "json5", "yaml"],
"i18n-ally.enabledFrameworks": ["react", "i18next"],
"i18n-ally.namespace": true,
"i18n-ally.pathMatcher": "{locale}/{namespaces}.json",
Expand Down
25 changes: 12 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
**NOTE:此插件跟语言框架无关,无论你使用React或Vue(或其他任意语言),只要是vite,都可以基于此插件实现国际化资源懒加载**

**考虑到更好的扩展性,此插件不负责国际化资源的解析,而是提供了一个 `setupI18n` 方法,你需要基于此方法实现自己的国际化资源懒加载逻辑**
**考虑到更好的扩展性,此插件不负责国际化资源的解析,而是提供了一个 `setupI18n` 方法,你需要基于此方法实现自己的国际化资源解析等逻辑**

## 特性

Expand All @@ -23,12 +23,12 @@ pnpm add vite-plugin-i18n-detector -D


## 配置项
| 参数 | 类型 | 默认值 | 描述 |
| -------------- | ---------- | ------------------- | ---------------------- |
| localesPaths | `string[]` | undefined | 存放语言资源的目录地址 |
| pathMatcher | `string` | undefined | 资源文件匹配规则 |
| enabledParsers | `string[]` | `['json', 'json5']` | 支持的资源文件类型 |
| cwd | `string` | `process.cwd()` | 项目根目录 |
| 参数 | 类型 | 默认值 | 描述 |
| ------------- | ---------------- | --------------------------------------- | ---------------------- |
| localesPaths | `string[]` | undefined | 存放语言资源的目录地址 |
| pathMatcher | `string` | undefined | 资源文件匹配规则 |
| parserPlugins | `ParserPlugin[]` | `[jsonParser, json5Parser, yamlParser]` | 资源文件解析插件 |
| root | `string` | `process.cwd()` | 项目根目录 |

## 配置参考

Expand All @@ -42,9 +42,8 @@ import { i18nDetector } from 'vite-plugin-i18n-detector'
export default defineConfig({
plugins: [
i18nDetector({
localesPaths: [path.join(__dirname, './src/locale')],
localesPaths: [path.join(__dirname, './src/locales')],
pathMatcher: '{locale}/{namespaces}.{ext}',
enabledParsers: ['json', 'json5'],
}),
],
})
Expand Down Expand Up @@ -121,9 +120,9 @@ i18next.changeLanguage = async (lang: string, ...args) => {
### .vscode => settings.json
``` json
{
"i18n-ally.localesPaths": ["src/locale"],
"i18n-ally.localesPaths": ["src/locales"],
"i18n-ally.keystyle": "flat",
"i18n-ally.enabledParsers": ["json", "json5"],
"i18n-ally.enabledParsers": ["json", "json5", "yaml"],
"i18n-ally.enabledFrameworks": ["react", "i18next"],
"i18n-ally.namespace": true,
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
Expand All @@ -132,6 +131,6 @@ i18next.changeLanguage = async (lang: string, ...args) => {
```


## ⚠️ 提示
## ⚠️ 温馨提示

目前仅支持 `json(5)` 资源文件
目前支持 `json` / `json5` / `yaml` / `yml` 资源文件
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# TODO

[ ] export inteface for user-custom parser plugin
[x] export inteface for user-custom parser plugin
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"fast-glob": "^3.3.1",
"fs-extra": "^11.1.1",
"iso-639-1": "^3.1.0",
"js-yaml": "^4.1.0",
"json5": "^2.2.3",
"parse-glob": "^3.0.4",
"picocolors": "^1.0.0",
Expand All @@ -81,6 +82,7 @@
"@types/clone-deep": "^4.0.2",
"@types/debug": "^4.1.9",
"@types/fs-extra": "^11.0.2",
"@types/js-yaml": "^4.0.7",
"@types/parse-glob": "^3.0.30",
"@types/react": "^18.2.28",
"@types/throttle-debounce": "^5.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"a": {
"b": {
"c": "dxx"
"c": "more info"
}
}
}
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"a": {
"b": {
"c": "aaaa"
"c": "more info default"
}
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
11 changes: 9 additions & 2 deletions playground/spa/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@ export default defineConfig({
plugins: [
react(),
i18nDetector({
localesPaths: [path.join(__dirname, './src/locale')],
localesPaths: [path.join(__dirname, './src/locales')],
pathMatcher: '{locale}/{namespace}.{ext}',
enabledParsers: ['json'],
parserPlugins: [
{
ext: 'properties', // just for example
parse() {
// how to parse properties file, it's up to you
},
},
],
}),
],
clearScreen: false,
Expand Down
20 changes: 14 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 20 additions & 8 deletions src/plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@ import { type PluginOption } from 'vite'
import { RESOLVED_VIRTUAL_PREFIX, RESOURCE_VIRTURL_HELPER, VIRTUAL } from './utils/constant'
import { LocaleDetector } from './locale-detector/LocaleDetector'
import { debug } from './utils/debugger'
import { type EnableParsersType } from './parsers'
import { initWatcher } from './utils/file-watcher'
import { hmr } from './utils/hmr'
import { type ParserConstructor } from './parsers/Parser'

export type ParserPlugin = ParserConstructor | undefined

export interface I18nDetectorOptions {
/**
* @description locales directory paths
* @example
* [path.resolve(__dirname, './src/locales')]
* ['./src/locales']
*/
localesPaths: string[]
/**
* @description rule of matching locale file
* @example
* `{locale}/{namespaces}.{ext}`
* `{locale}/{namespace}.json`
Expand All @@ -23,27 +27,35 @@ export interface I18nDetectorOptions {
*/
pathMatcher: string
/**
* @default
* ['json', 'json5']
* @description
* Currently support `['json', 'json5']` only
* parser plugins
*
* you can add custom parser plugin if there is no built-in parser for your file extension
* @example
* ```js
* [{
* ext: 'json',
* parser: (text) => JSON.parse(text),
* }]
* ```
*/
enabledParsers?: EnableParsersType
parserPlugins?: ParserPlugin[]
/**
* @description root path
* @default
* process.cwd()
*/
cwd?: string
root?: string
}

export async function i18nDetector(options: I18nDetectorOptions) {
debug('i18nDetector options:', options)

const localeDetector = new LocaleDetector({
cwd: options.cwd || process.cwd(),
root: options.root || process.cwd(),
localesPaths: options.localesPaths,
pathMatcher: options.pathMatcher,
enabledParsers: options.enabledParsers,
parserPlugins: options.parserPlugins,
})

await localeDetector.init()
Expand Down
35 changes: 18 additions & 17 deletions src/plugin/locale-detector/LocaleDetector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import uniq from 'uniq'
import fg from 'fast-glob'
import { normalizePath } from 'vite'
import cloneDeep from 'clone-deep'
import { AvailableParsers, DefaultEnabledParsers } from '../parsers'
import { DefaultEnabledParsers } from '../parsers'
import { type I18nDetectorOptions } from '..'
import { ParsePathMatcher } from '../path-matcher/PathMatcher'
import { PKGNAME, VIRTUAL } from '../utils/constant'
import { debug } from '../utils/debugger'
import { logger } from '../utils/logger'
import { Parser } from '../parsers/Parser'

export interface Config extends I18nDetectorOptions {
cwd: string
root: string
}

type PathMatcherType = RegExp
Expand All @@ -35,9 +36,8 @@ export class LocaleDetector {
private _pathMatcher: { regex: PathMatcherType; matcher: string }
private _localesPaths: string[]
private _rootPath: string
private _localeDirs: string[] = []

// need reset on init
private _localeDirs: string[] = []
private _files: Record<string, ParsedFile> = {}
private _localeModules: {
modules: Record<string, any>
Expand All @@ -48,7 +48,7 @@ export class LocaleDetector {
constructor(c: Config) {
this.config = c

this._rootPath = c.cwd
this._rootPath = c.root
const pathMatcher = c.pathMatcher
this._pathMatcher = {
regex: ParsePathMatcher(pathMatcher, this.enabledParserExts()),
Expand All @@ -68,8 +68,6 @@ export class LocaleDetector {
debug(`🚀 Initializing loader "${this._rootPath}"`)
debug(`🗃 Custom Path Matcher: ${this._pathMatcher.matcher}`)
debug(`🗃 Path Matcher Regex: ${this._pathMatcher.regex}`)
this._files = Object.create(null)
this._localeModules = Object.create(null)

await this.loadAll()

Expand Down Expand Up @@ -250,8 +248,6 @@ export class LocaleDetector {
matcher,
}

// this._allLocaleDirs.add(deepDirpath)

return true
} catch (e) {
this.unsetFile(relativePath)
Expand Down Expand Up @@ -303,28 +299,29 @@ export class LocaleDetector {
}
}

getMatchedParser(ext: string) {
private getMatchedParser(ext: string) {
if (!ext.startsWith('.') && ext.includes('.')) {
ext = path.extname(ext)
}

// resolve parser
return this.getEnabledParsers().find((parser) => parser.supports(ext))
return this.getParsers().find((parser) => parser.supports(ext))
}

private enabledParserExts() {
const enabledParsers = this.getEnabledParsers().map((item) => item.id)
const enabledParsers = this.getParsers().map((item) => item.supportedExts)
return enabledParsers.filter(Boolean).join('|')
}

getEnabledParsers() {
let ids = this.config.enabledParsers
private getParsers() {
const _parsers = this.config.parserPlugins?.filter(Boolean)

if (!ids?.length) {
ids = DefaultEnabledParsers
const parsers: Parser[] = DefaultEnabledParsers
if (_parsers?.length) {
parsers.push(..._parsers.filter(Boolean).map((parser) => new Parser(parser!)))
}

return AvailableParsers.filter((i) => ids!.includes(i.id))
return parsers
}

get pathMatcher() {
Expand All @@ -336,6 +333,10 @@ export class LocaleDetector {
}

async findLocaleDirs() {
this._files = Object.create(null)
this._localeModules = Object.create(null)
this._localeDirs = []

if (this._localesPaths?.length) {
try {
const _locale_dirs = await fg(this._localesPaths, {
Expand Down
Loading

0 comments on commit 19d1b49

Please sign in to comment.