Skip to content

Banana-energy/i18n-transformer

Repository files navigation

i18n-transformer

A Vite/Webpack plugin/loader that automatically transforms Chinese text in JS code into i18n translation functions based on AST.

一个基于AST自动转换JS代码中的中文为i18n翻译函数的 Vite/Webpack 插件/loader。

理论上支持任何JS框架,目前只测试了Vue2/3。

Usage

Vite

  pnpm i -D @kapo/vite-plugin-i18n-transformer
// vite.config.ts
import Vue from "@vitejs/plugin-vue";
import VueJsx from "@vitejs/plugin-vue-jsx";
import I18nTransformer from "vite-plugin-i18n-transformer";
import {defineConfig} from 'vite'
import path from 'path'

export default defineConfig({
    plugins: [
        Vue(),
        VueJsx(),
        // 需要放在Vue和VueJsx插件之后
        I18nTransformer({
            include: ['**.js', '**.vue'],                           // 针对什么文件进行国际化
            exclude: [                                              // 项目内不需要国际化的文件或文件夹
                'src/locales/**',
                'node_modules/**',
                'src/store/modules/locale.ts'
            ],
            i18nCallee: 'useI18n().t',                              // 调用国际化函数
            dependency: {                                           // 国际化函数依赖引入配置
                name: 'useI18n',                                    // 国际化函数依赖的名称
                value: '@/hooks/web/useI18n',                       // 引入国际化函数的路径
                objectPattern: true,                                // 引入的国际化函数依赖的形式。true为解构形式:import { name } from 'xxx'
                preprocessing: 'const {t} = use18n()'               // 这行代码将添加至import依赖之后,可以用来做一些处理
            },
            output: {
                filename: 'zh-CN.json',                             // 生成中文配置的文件名
                langList: ['en-US.json'],                           // 生成其他语言配置的文件名列表
                path: path.resolve(process.cwd(), './public/lang'), // 生成文件的路径
            }
        }),
    ],
})

Webpack

  npm i -D @kapo/webpack-plugin-i18n-transformer

Webpack由于vue-loader版本不同,需要分版本处理。

以下是 vue-loader15.x 示例

// vue.config.js
const {I18nTransformerPlugin} = require('@kapo/webpack-plugin-i18n-transformer')
module.exports = {
    ...,
    chainWebpack: (config) => {
        const i18nOptions = {
            include: ['**.js', '**.jsx', '**.vue'],
            exclude: ['src/lang/**', 'node_modules/**', 'src/main.js',],
            i18nCallee: 'i18n.default.t',
            dependency: {
                name: 'i18n',
                path: '@/lang',
                modules: 'CommonJS',
            },
        }

        config.module
            .rule('js')
            .use('i18n-loader')
            .loader('@kapo/webpack-plugin-i18n-transformer')
            .options(i18nOptions)
            .before('babel-loader')
            .end()
            .end()
            .rule('vueTemplateRender')
            .test(/\.vue$/)
            .resourceQuery(/type=template/)
            .enforce('post')
            .use('i18n-loader')
            .loader('@kapo/webpack-plugin-i18n-transformer')
            .options(i18nOptions)
    }
}

Special

对于项目中可能存在并不想被翻译的项,在 vite/webpack 包中提供了 ignoreAutoI18n 函数,将不想翻译的条目使用该函数包裹起来即可。

为什么不采用类似 // eslint-disabled-next-line 形式?

vuejs/core#12114

Example

Before

pA193RO.png

After

pA19UeA.png

Configuration

export interface OutputSetting {
  /**
   * 生成中文配置的文件名
   */
  filename: string;
  /**
   * 生成文件的路径
   */
  path: string;
  /**
   * 生成其他语言配置的文件名列表
   */
  langList: string[]
}

export interface DependencySetting {
  /**
   * 国际化函数依赖的名称
   */
  name: string
  /**
   * 引入国际化函数的路径
   */
  path: string
  /**
   * 引入的国际化函数依赖的形式
   * 设置true为解构形式:import { name } from 'path'
   */
  objectPattern: boolean
  /**
   * 这行代码将添加至import依赖之后,可以用来做一些处理
   */
  preprocessing?: string
}

/**
 * 收集到的国际化词条,key为通过keyRule生成的key,value为收集到的中文
 */
export type WordMap = Record<string, string>;

export interface GlobalSetting {
  /**
   * 生产文件配置
   */
  output: OutputSetting;
  /**
   * 匹配需要翻译的正则表达式,默认/[\u4e00-\u9fa5]/
   */
  localePattern: RegExp;
  /**
   * 自定义生成key的函数,参数为收集到的中文,中文在AST中对应的Node,收集到的所有词条配置
   * 默认规则为通过中文生成一串md5作为key,重复的中文在md5后添加 -1,-2
   */
  keyRule?: ((value: string, node: Node, map: WordMap) => string);
  /**
   * 针对哪些文件进行处理
   */
  include?: string[];
  /**
   * 排除哪些文件
   */
  exclude?: string[];
  /**
   * 调用国际化函数
   */
  i18nCallee: string;
  /**
   * 国际化依赖配置
   */
  dependency?: DependencySetting;
}

Thanks