The profile

Workbench refers to the desktop development environment. The goal of the workbench is to provide a common model for creating, managing, and navigating workspace resources, currently in the form of VSCode

The motivation

The dilemma of internationalization

The process of copy extraction, copy translation and grammar conversion in the system is tedious and inefficient, which reduces the development efficiency and disturbes people’s mentality. Existing patterns for addressing internationalization tools include:

  • Cli tool
  • Interactive plug-in based on VSCode

There are plenty of tools available on the web about these patterns, so I won’t go into details here. In general, these tools have the following problems:

  • Text extraction & text transformation does not support complex combinations (the concatenation of template characters and static text) and requires human intervention.
  • The overall interaction link is too complicated and the improvement effect is not obvious.
  • Due to the influence of the overall interactive link, the keys extracted from many documents are random.
  • Too many implicit questions.

Set a goal

By VSCode as a carrier, to achieve a workbench interactive plug-in. On the premise that the whole interactive link of document extraction, translation, insertion and replacement is smooth, reliable, efficient and easy to expand, create and manage document resources and international resources in the current workspace.

  • Workspace: represents the current workspace of VSCode
  • Document resources: represents all resources in the current document or resource manager that VSCode has opened
  • Internationalization resources: Represents internationalization files in the project

Design details

Plug-in class diagram display

Parse local internationalization resources

Parse internationalization files in the local workspace into object data types

Supported types

  • ecmascript
  • json

Different modes are set based on the directory structure of internationalized resources set globally

File (file mode) : -locales-zh-cn.js-en-us.js dir(directory mode) :  - locales - zh-CN - common.js - table.js - en-US - common.js - table.jsCopy the code

Use the class diagramParserParse resource files

  • Ecmascript: Export default {} > module.exports = {}) is output as CJS using Babel, and module object is compiled using Node’s module
  • Json: json. Parse Violence parsing
/ / ecmascript:
const code = readFileSync('locales/zh-CN.js'.'utf-8')
const ast = parse(texts, {
    sourceType: 'module'
})
traverse(ast, {
    ExportDefaultDeclaration(path: NodePath<{ declaration: any }>) {
        path.replaceWith(
            expressionStatement(
                assignmentExpression(
                    '=',
                    memberExpression(identifier('module'), identifier('exports')),
                    objectExpression(path.node.declaration.properties),
                )
            )
       )
    }
})
const { code } = generate(ast)
const module = new Module('module')
module._compile(code, 'module')
console.log(module.exports) / / {}

// json
const code = readFileSync(locales/zh-CN.js', 'utf-8')
console.log(JSON.parse(text)) // {}
Copy the code

Extract the copy from the document

Use @vue/ Compiler & @babel buckets to extract the copy to be internationalized from the current document resources

Supported types:

  • sfc
  • tsx&jsx

Use the class diagramExtractorExtract the copy in the document

template

For VUE SFC internal template syntax parsing AST and JS AST are very different, mainly because there are no processing tools such as @babel/traverse, but only parsing tools such as VUe-template-compiler or @vue/compiler-*. Therefore, the template-ast tree can only be recursively extracted from elements, TEXT, and SIMPLE_EXPRESSION.

AST node types that contain copywriting:

export enum NodeTypes{
    // Element nodes, including the template element
    ELEMENT = 1.// The text type, including all whitespace characters in the code, such as newlines, Spaces, etc
    //  
    TEXT = 2.// Expressions, including template strings, etc
    / / < span: title = "${data} expression ` `" > {{" expression "+ ` ${data} expression `}} < / span >
    SIMPLE_EXPRESSION = 4./ / the interpolation
    INTERPOLATION = 5.// Common attributes
    ATTRIBUTE = 6.// The value of the instruction
    DIRECTIVE = 7,}Copy the code
script

Script is to parse the contents of JS inside the

/ / pseudo code
traverse(ast, {
    StringLiteral: (path) = > {
        const { value, start, end } = path.node
        if(! start || ! end)return
        if (path.findParent(p= > p.isImportDeclaration())) return
        const range = new Range(
            document.positionAt(offset + start + 1),
            document.positionAt(offset + end - 1)
        )
        words.push({
            id: this.id,
            text: value,
            start,
            end,
            range,
            isSetup,
            type: 'js-string'})},TemplateLiteral: (path) = > {
        if (path.findParent(p= > p.isImportDeclaration())) return
        const value = path.get('quasis').map(item= > ({
            text: item.node.value.raw,
            start: item.node.start
        }))
        value.forEach(item= > {
            this.getShouldExtractedText(item.text).forEach(t= > {
                if (item.start) {
                    const start = source.indexOf(t, item.start)
                    const end = start + t.length
                    const range = new Range(
                        document.positionAt(start + offset),
                        document.positionAt(end + offset)
                    )
                    words.push({
                        id: this.id,
                        text: t,
                        start,
                        end,
                        range,
                        isDynamic: true,
                        isSetup,
                        type: 'js-template'})}})})Copy the code

Creating the Workbench page

Create a workbench, a Webview, using VSCode’s built-in API

Use the class diagramWorkbenchAct as a bridge between Webview creation and communication

For security reasons, Webview cannot directly access local resources by default. It runs in an isolated context. To load local images, JS, CSS, etc., you must use the special VS code-resource: protocol.

class Workbench{
    private constructor() {
        this.panel = window.createWebviewPanel('workbench'.'Workbench', ViewColumn.Beside, {
            enableScripts: true.retainContextWhenHidden: true,})this.panel.iconPath = Uri.file(
            join(Config.extensionPath, 'resources/workbench.svg'))this.panel.webview.html = getHtmlForWebview(Config.extensionPath, 'resources/workbench/index.html')
        this.panel.webview.onDidReceiveMessage((message) = > this.handleMessages(message), null.this.disposables)
        this.panel.onDidDispose(() = > this.dispose(), null.this.disposables)
    }
    
    private handleMessages(message: Message) {
        const { type, data } = message
        switch (type) {
            case EventTypes.READY:
                this.panel.webview.postMessage({
                    type: EventTypes.CONFIG,
                    data: this.config
                })
                break
            case EventTypes.SAVE:
                CurrentFile.write(data)
                break
            case EventTypes.TRANSLATE_SINGLE:
                this.translateSignal(data)
                break}}}Copy the code

Vite & Vue as the carrier of the workbench, the following simple page display

Insert the internationalization resource file

Inserts the copy edited in the current workbench into the internationalization file

Supported types:

  • ecmascript
  • json

Use the class diagramInserterInsert the data processed in the above workbench

1. Convert the data of the workbench
[ { key: "common.confirm", insertPaths: { "zh-CN": "/xxx/zh-CN/common.ts" "en-US": "/ccc/en-US/common.ts" }, languages: {" useful - CN ":" confirm "" en - US" : "confirm"}}] into: {"/XXX/useful - CN/common ts ": {flattenData: {"common.confirm":" confirm", "common.cancel":" cancel"}, an unflattener data: {common: {confirm: "confirm", cancel: }}}, "/ CCC/en-us /common.ts": {// same as above}}Copy the code
2. Merge workbench data withParserClass to extract data from
{"common.confirm": "confirm", "common.cancel":" cancel"} // Data for Step 1 {"common.add": "add", "common.delete": "Delete"} // Data from the Parser class is merged into {common: {confirm: "confirm ", cancel:" cancel ", add: "add ", delete:" delete "}}Copy the code
3, according to the path to insert, implementation logic involves some@babel/traversewithJSON.stringifySome of the operations, do not elaborate

Replace the copy in the current document

Convert the copy in the current document to the Key in the internationalization resource and the internationalization call function name ($i18n, $t)

Use the class diagramReplacerHandle the copy in the document

1. Convert the data extracted from the Parser class into text: key
{common: {confirm: "confirm", cancel:" cancel ", add: "add ", delete:" delete "}} Convert to: {" confirm" : "common.confirm", "cancel ": "Common. Cancel", "add" : "common. Add", "delete" : "common. Delete"}Copy the code
2. Use the class diagramExtractorThe copy data extracted by the module is replaced
Interface
export type ExtractorType = 'html-attribute' | 'html-attribute-template' | 'html-inline' | 'html-inline-template' | 'js-string' | 'js-template' | 'jsx-text'
export interface ExtractorResult {
    id: ExtractorId
    text: string
    start: number
    end: number
    range: Range isDynamic? :booleanisSetup? :boolean // script setupisJsx? :booleanfullText? :stringfullStart? :numberfullEnd? :numberfullRange? : Range attrName? :string // when the type is html-attribute
    type: ExtractorType
}
Copy the code
3. Use the class diagramRefactorPerform a refactoring of the copywriting data

Specific reconstruction copywriting logic does not elaborate oh

switch (type) {
    case 'html-inline':
        break
    case 'html-inline-template':
        break
    case 'html-attribute':
        break
    case 'html-attribute-template':
        break
    case 'js-string':
        break
    case 'js-template':
        break
    case 'jsx-text':
        break
    default:
        break
}
Copy the code

How to use

Setting the language directory

No accident after import the plugin will automatically identify the directory, if out of the accident then Ctrl + P input manually language directory | i18n – workbench. Config – locales

The directory structure

File (file mode) : -locales-zh-cn.js-en-us.js dir(directory mode) :  - locales - zh-CN - common.js - table.js - en-US - common.js - table.jsCopy the code

Open translation workbench

Ctrl + P input open translation table | i18n – workbench. The open – workbench

Replace the current document

Ctrl + P input to replace the current document | i18n – workbench. Replace with

future

1. The single package architecture is changed into multiple packages, and the core functions (extraction, replacement and translation) are removed. The client software is used as the workbench carrier, and VSCode is supported. 2. Enhance plug-in functions

At the end

This plug-in is not distributed to the VSCode extension store. Warehouse address: github.com/yzydevelope… Download address: i18N-Workbench-vscode-extension Hope this plug-in can help you, or partners with ideas can do some customized functions based on this plug-in, improve team efficiency. Or we can inspire people from the level of consciousness to start from what aspects to solve the problem. If you have any problems or ideas that are difficult to solve, you can add me on wechat DK573432332. If you can contribute PR, it’s really great 😊