前端代码自动国际化vscode插件开发

135 阅读2分钟

一:安装vscode工程脚手架

因为vscode插件开发的脚手架是基于 Yeoman开发的

  • 执行下面命令行进行安装 npm install -g yo generator-code
  • 执行yo code初始化插件

二:初始化插件配置

  • 首先在package.json中声明contributes下的commands字段,声明插件命令为extension.i18n
"contributes": {
    "commands": [
        {
            "command": "extension.i18n",
            "title": "autoi18n"
        }
    ],
}
  • 然后在激活插件,在package.json中声明activationEvents字段为"onCommand:extension.i18n"
"activationEvents": [
    "onCommand:extension.i18n"
],
  • 声明插件的使用方式 声明插件出现的时机,为resourceScheme =~ /^untitled$|^file$/时,也就是在编辑文件时
"contributes": {
    "menus": {
        "editor/title": [
            {
                "when": "resourceScheme =~ /^untitled$|^file$/",
                "command": "extension.i18n",
                "group": "navigation"
            }
        ]
    }
},

监听插件命令

  • 第一步:注册extension.i18n事件监听vscode.commands.registerCommand('extension.i18n', () => {})
  • 第二步:获取编辑文件的路径 const uri = vscode.window.activeTextEditor?.document.uri.fsPath;
  • 第三步:使用nodejs的fs模块读取文件fs.readFile(uri, (err, res) => {})
  • 第四步:引入babel文件内容解析为ast
  • 第五部:使用'@babel/traverse'便利时判断ast节点类型
  • 第六步:对ImportDeclaration做处理自动引入import intl from 'intl'
  • 第七步:对JSXTextStringLiteral做处理,使用intl包裹上对应文案的key进行对语言的调用
import * as vscode from 'vscode';
const fs = require('fs');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse');
const generate = require('@babel/generator').default;
const types = require('@babel/types').default;
const template = require('@babel/template').default;

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
    let disposable = vscode.commands.registerCommand('extension.i18n', () => {

    const uri = vscode.window.activeTextEditor?.document.uri.fsPath;
    fs.readFile(uri, (_err: any, res: { toString: () => any; }) => {
      const code = res.toString();
      const ast = parser.parse(code,  {
        sourceType: 'unambiguous',
        plugins: ['jsx', 'typescript']
      });

      try {
        traverse.default(ast, {
          Program: {
            enter(path, state = {}) {
                let imported;
                path.traverse({
                    ImportDeclaration(p) {
                        const source = p.node.source.value;
                        if(source === 'intl') {
                            imported = true;
                        }
                    }
                });
                if (!imported) {
                    const uid = path.scope.generateUid('intl');
                    const importAst = template.ast(`import ${uid} from 'intl'`);
                    path.node.body.unshift(importAst);
                }
            }
          },
          JSXText(path) {
            if (path.node.type === 'JSXText') {
              const value = path.node.value.replace('\n', '').trim();
              if (value) {
                const replaceExpression = template.ast(`{intl('${value}')}`);
                path.replaceWith(replaceExpression);
              }
            }
          },
          StringLiteral(path) {
            let isI18nDisabled = false;
            if(path.node.leadingComments) {
              isI18nDisabled = path.node.leadingComments.find(item => item.value.includes('i18n-disable'));
            }
            if (!isI18nDisabled && path.parentPath.node?.callee?.name !== 'intl' && path.node.value !== 'intl' && path.parentPath.type !== 'ImportDeclaration'){
              const replaceExpression = template.ast(`intl('${path.node.value}')`);
              path.replaceWith(replaceExpression);
            }
          },
        });
      } catch (error) {
        console.log(error);
      }
      const { code: codeAfterAst, map } = generate(ast);
      fs.writeFileSync(uri, codeAfterAst, 'utf8');
    });
	});

	context.subscriptions.push(disposable);
}

// this method is called when your extension is deactivated
export function deactivate() {}

todo

需要引入多语言的json文件,然后根据代码中JSXText节点StringLiteral节点的value,根据value找到对应的key,完成多语言的key调用