Intrusive language conversion script


Keywords
i8n, chinese-translation, code-replace, code-translate, commander, i18n, node, translate, typescript
License
BSD-3-Clause
Install
npm install code-i18n@3.0.4

Documentation

code-i18n

What is this?

code-i18n ๅผ€็ฎฑๅณ็”จ็š„้กน็›ฎๅ›ฝ้™…ๅŒ–ๅทฅๅ…ทใ€‚
่ฎฟ้—ฎhttps://vite-i18n.netlify.app/ๅฏไปฅๅœจ้กน็›ฎไธญๅฟซ้€Ÿไฝฟ็”จ code-i18nใ€‚ Netlify Status

Type

็›ฎๅ‰ๆ”ฏๆŒ่ฝฌๆข็š„่ฏญ่จ€

Type Support
js
  • jsx
  • ts
  • tsx
  • vue
  • Usage

    code-i18n ๅฏผๅ‡บไธ€ไธชๅพˆ็ฎ€ๅ•็š„ๅ‡ฝๆ•ฐ๏ผŒๅ‚ๆ•ฐๆ˜ฏ source ๅ’Œ config ๏ผŒ่ฟ”ๅ›žๅ€ผๆ˜ฏไธ€ไธชๅฏน่ฑก๏ผŒ่ฏฆ่ง ๆ–‡ๆกฃใ€‚

    import { transformCode } from 'code-i18n'
    const source = 'const language = "ไธญๆ–‡"'
    
    const { code } = transformCode(source, {
      type: 'js',
    })
    console.log(code) // const language = $t('StringLiteral_17_21');
    console.log(stack) // [ { StringLiteral_17_21: "ไธญๆ–‡" } ]

    ๅฆ‚ๆžœๅ‘็”Ÿ่งฃๆž้”™่ฏฏ๏ผŒๆˆ–่ฎธๆ˜ฏๅ› ไธบไฝฟ็”จไบ†้žๅธธ่ง„่ฏญๆณ•๏ผŒๆฏ”ๅฆ‚ ๅฑ•ๅผ€่ฏญๆณ• [Spread syntax]๏ผˆ้ป˜่ฎคๆ”ฏๆŒ๏ผ‰ใ€ไฟฎ้ฅฐๅ™จ [Decorator]๏ผŒไธ็”จๆ‹…ๅฟƒ๏ผŒไฝ ๅฏไปฅๆŒ‰็…งๅฆ‚ไธ‹้…็ฝฎๅฏนไปฃ็ ่ฟ›่กŒ้€‚้…ใ€‚ๅ…ถๅฎƒ่ฏญๆณ•้”™่ฏฏๅฏไปฅๅ…ณๆณจ babel๏ผŒ่ฟ›่กŒๅฏนๅบ”็š„้…็ฝฎใ€‚

    transformCode(source, {
      type: 'js',
      parserOptions: {
        babel: {
          plugins: [['decorators', { decoratorsBeforeExport: true }]],
        },
      },
    })

    Installation

    yarn add -D code-i18n
    npm install --save-dev code-i18n

    CLI

    code-i18n ๆไพ›ไบ†็ฎ€ๅ•้ซ˜ๆ•ˆ็š„ๅ‘ฝไปค่กŒ๏ผŒ่พ“ๅ…ฅ code-i18n --help ๆŸฅ็œ‹ๆ‰€ๆœ‰ๆ”ฏๆŒ็š„ๆ“ไฝœใ€‚ไน‹ๆ‰€ไปฅๆฒก็”จ code ไฝœไธบๅ”ค้†’ๅ…ณ้”ฎ่ฏ๏ผŒๆ˜ฏๅ› ไธบ vscode ๆไพ›ไบ† code ๅ‘ฝไปค่กŒใ€‚

    ๅ‘ฝไปค่กŒ้ป˜่ฎคไผš่ฏปๅ–ๆ‰ง่กŒ่ทฏๅพ„ไธ‹็š„ .code-i18n.js ๆ–‡ไปถใ€‚ๅฆ‚ๆžœๅœจๅ‘ฝไปค่กŒไธญๆŒ‡ๅฎšไบ† type ๏ผŒ้‚ฃไนˆไผš่ฆ†็›–้…็ฝฎๆ–‡ไปถไธญ็š„ type ใ€‚

    ่ฏฆ็ป†้…็ฝฎๅฆ‚ไธ‹๏ผš

    const recast = require('recast')
    
    const option = {
      // ๅ‚่€ƒ https://github.com/Linkontoask/code-i18n/blob/next/types/interface/index.d.ts#L11
    }
    module.exports = {
      ...options,
      ignore: [],
      prettier: (code, ast) => {
        if (ast) {
          return recast.print(ast, {
            tabWidth: 2
          }).code
        }
        return code
      }
    }

    ๅœจ่ฟ›่กŒไธ‹ๅˆ—ๆ“ไฝœไน‹ๅ‰๏ผŒ้œ€่ฆๅ…จๅฑ€ๅฎ‰่ฃ…

    yarn global add code-i18n
    npm install -g code-i18n
    Usage: code-i18n [options]
    
    Convert your code to help you code quickly (internationalization)
    
    Options:
      -v, --version                           output the version number
      --debug                                 Output more information for debugging the program (default: false)
      --config <path>                         Specify the configuration file
      -c, --code <code>                       Convert the specified code
      -n, --name <file name>                  Convert the specified file
      -d, --dir <directory>                   Convert files under the specified path
      -s, --stack <file name>                 Specify the output location of the collected language pack (json)
      -w, --write [path]                      Specify the write path (only used in --code and --name) or overwrite the current
                                              file (default: false)
      -t, --type <js | jsx | ts | tsx | vue>  Specify the current code type, must be specified when using --code
      -h, --help                              display help for command
    

    ๆœ‰็š„ๆ—ถๅ€™๏ผŒๆˆ‘ๅชๆƒณ็œ‹็œ‹ไธ€ไธช็ฎ€ๅ•็š„ไปฃ็ ่ฝฌๆข๏ผŒๆˆ‘ไปฌๅช้œ€่ฆ่พ“ๅ…ฅๅฆ‚ไธ‹ไฟกๆฏ

    code-i18n -c "const message = 'ๆˆ‘็ˆฑไธญๅ›ฝ'" -t js
    #โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    #โ”‚ (index) โ”‚ name โ”‚                      code                      โ”‚                  stack                   โ”‚
    #โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
    #โ”‚    0    โ”‚ null โ”‚ "const message = $t ...... teral_1_16_1_22');" โ”‚ '{"StringLiteral_1_16_1_22":"ๆˆ‘็ˆฑไธญๅ›ฝ"}'  โ”‚
    #โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

    ๅ…ถไธญ็š„ไฟกๆฏ่ขซ่ฃๅ‰ชไบ†๏ผŒๅ› ไธบๅœจ console ๅฑๅน•ไธŠๆฒกๅŠžๆณ•ๅฑ•็คบๆ‰€ๆœ‰ไฟกๆฏ๏ผŒ่ฟ™ไธชๆ—ถๅ€™ๅฏไปฅๆทปๅŠ  --write ๅ‚ๆ•ฐ๏ผŒๅฐ†ไปฃ็ ่พ“ๅ‡บๅˆฐๆ–‡ไปถไธญ

    code-i18n -c "const message = 'ๆˆ‘็ˆฑไธญๅ›ฝ'" -t js --write china.js
    // china.js
    const message = $t('StringLiteral_1_16_1_22');

    code-i18n ่ฟ˜ๅฏไปฅ่ฝฌๆขๅฏนๅบ”็š„ๆ–‡ไปถ๏ผŒๅนถไธ”ๆžๅ…ถ็ฎ€ๅ•

    code-i18n -n china.js -w

    ่ฝฌๆข็›ฎๅฝ•ไธ‹ๆ‰€ๆœ‰ js ๆ–‡ไปถ๏ผŒๅนถไธ”ๅ†™ๅ…ฅ

    code-i18n -d . -t js -w

    ๅˆฐ่ฟ™้‡Œ๏ผŒไฝ ไผšๅ‘็Žฐ๏ผŒ่ฟ˜ๆฒกๆœ‰็”Ÿๆˆ่ฏญ่จ€ๅŒ…ใ€‚ๆˆ‘ไปฌๅœจๅ‘ฝไปค่กŒไธญๆทปๅŠ  -s <path> ๅฐฑๅฏไปฅไบ†๏ผŒไป–ไผšๆŠŠไปฃ็ ไธญๆ‰€ๆœ‰ไธญๆ–‡ไฟกๆฏ่พ“ๅ‡บๅˆฐไธ€ไธช json ๆ–‡ไปถไธญใ€‚

    code-i18n -d . -t js -s zh-cn.json -w

    Documentation

    ไธ‹้ขไฝฟ็”จ typescript ็š„ๅฝขๅผไป‹็ป code-i18n ไฝฟ็”จๆ–นๆณ•ใ€‚ๅ…ถไธญๆœ‰ไบ›ๆ–‡ๆกฃ้œ€่ฆๅ‚่€ƒ่ฟ™ไบ›ๅœฐๆ–น vue-eslint-parserใ€babel-parserใ€espree

    import { ParserOptions as BabelParserOptions } from '@babel/parser'
    import { Linter } from 'eslint'
    
    interface ParserOptions {
      vue?: Linter.ParserOptions
      babel?: BabelParserOptions
    }
    
    interface Config {
      type: 'js' | 'jsx' | 'ts' | 'tsx' | 'vue'
      identifier?: string
      ruleKey?: (node: t.Node) => string | number
      parserOptions?: ParserOptions
    }
    
    export declare function transformCode(
      code: string,
      config: Config
    ): {
      code: string
      stack: Record<string, string>[]
    }

    ็Žฐๅœจ๏ผŒparserOptions ๅทฒ็ปๅญ˜ๅœจ้ป˜่ฎคๅ€ผ๏ผŒๅฝ“ไฝ ๅ‘็”Ÿๆœ‰ไปปไฝ•่ฏญๆณ•้”™่ฏฏ็š„ๆ—ถๅ€™ๆฃ€ๆŸฅๆญค้ป˜่ฎคๅ€ผๆ˜ฏๅฆ็ฌฆๅˆ้ข„ๆœŸใ€‚ๆฏ”ๅฆ‚ๅฝ“ไฝ ๅ‘็Žฐๆœ‰ optional expression๏ผˆๅฏ้€‰้“พ๏ผ‰ ้”™่ฏฏ็š„ๆ—ถๅ€™๏ผŒไฝ ้œ€่ฆ้…็ฝฎๅฆ‚ไธ‹ไฟกๆฏใ€‚้€šๅธธๅœจ่งฃๆž vue ๆ–‡ไปถ็š„ๆ—ถๅ€™ๆ‰ไผšๅ‡บ็Žฐ่ฟ™ไบ›้—ฎ้ข˜ใ€‚

    transformCode(source, {
      parserOptions: {
        vue: {
          ecmaVersion: 11,
        },
      },
    })

    ๆœ‰็š„ๆ—ถๅ€™ๆˆ‘ไปฌๅœจ็ผ–ๅ†™ vue ไปฃ็ ็š„ๆ—ถๅ€™้œ€่ฆไฝฟ็”จ jsx ็š„่ฏญๆณ•๏ผŒ็Žฐๅœจไฝ ๅฟ…้กปๅฐ† parserOptions ็š„ vue ๅฑžๆ€ง่ฟ›่กŒๆ›ดๆ”นใ€‚

    transformCode(source, {
      parserOptions: {
        vue: {
          ecmaFeatures: {
            jsx: true,
          },
        },
      },
    })

    ๆˆ‘ไปฌ่ฟ˜ๅฏไปฅๅƒ eslint ไธ€ๆ ท๏ผŒ่ฟ‡ๆปคๆŽ‰ไธ€ไบ›ไธ้œ€่ฆ่ฝฌๆข็š„ๆ–‡ไปถๆˆ–ไปฃ็ ่กŒใ€‚

    // code-i18n-disabled
    const country = 'ไธญๅ›ฝ'
    const city = '้‡ๅบ†'
    // ไธŠ้ขๆ‰€ๆœ‰ไธญๆ–‡้ƒฝไธไผš่ฝฌๆข
    const country = 'ไธญๅ›ฝ'
    // code-i18n-disabled-next-line
    const city = '้‡ๅบ†' // ่ฟ™่กŒไปฃ็ ไธไผš่ฝฌๆข

    ๅฅฝไบ†๏ผŒ่ฏดไบ†่ฟ™ไนˆๅคš๏ผŒไธ‹้ขๅฐฑๆ˜ฏ parserOptions ็š„้ป˜่ฎคๅ€ผใ€‚

    const DEFAULT_PARSER_OPTIONS = {
      // https://github.com/eslint/espree#options
      vue: {
        sourceType: 'module',
        ecmaVersion: 11,
        ecmaFeatures: {
          experimentalObjectRestSpread: true,
        },
      },
      babel: {
        sourceType: 'module',
      },
    }

    Example

    ่ฟ™้‡Œไฝฟ็”จไธ€ๆฎตไปฃ็ ๆฅไป‹็ป code-i18n ็š„้กน็›ฎ็”จๆณ•ใ€‚้€šๅธธๅœจๅ†™ไปฃ็ ็š„่ฟ‡็จ‹ไธญ๏ผŒ้œ€่ฆๆ—ถๅˆปๆณจๆ„ๆ–‡ๆœฌ็š„็กฌ็ผ–็ ๏ผŒๅ› ไธบ่ฟ™ไผš่ฎฉไบงๅ“ๅคฑๅŽปไธๅŒ่ฏญ่จ€็š„็”จๆˆทใ€‚ๅธ‚ๅœบไธŠๆˆ‘ๅ‘็Žฐไบ†ไธ€ไบ›ๅผ€ๆบ้กน็›ฎ๏ผŒไป–ไปฌ้€š่ฟ‡ webpack ็š„่ƒฝๅŠ›๏ผŒๅœจ build ๆœŸ้—ด๏ผŒๅ‘ๆœ€็ปˆไบง็‰ฉๆทปๅŠ ไธๅฏ้€†็š„่ฝฌๆขไปฃ็ ๏ผŒ่ฟ™ๅพˆไธๅ‹ๅฅฝ๏ผŒๅนถไธ”ๅœจๅ‘็”Ÿ้”™่ฏฏ็š„ๅŒๆ—ถๅพˆ้šพๅŽป็”„ๅˆซใ€‚

    code-i18n ๆ˜ฏๅฐ†ๆบ็ ไธญ็š„ๅญ—็ฌฆไธฒๆ›ฟๆขๆˆๅฏๆ‰ง่กŒๅ‡ฝๆ•ฐ๏ผŒๅœจๅผ€ๅ‘็š„ๅŒๆ—ถๅณๅฏ้˜…่ฏป่ฏญ่จ€้ƒจๅˆ†็š„ๆบ็ ๏ผŒๅนถไธ”ๅฏไปฅๅฐ†ๅทฒ็ปๅ‘ๅธƒ็š„ไบงๅ“ๅ›ฝ้™…ๅŒ–ๅค„็†ใ€‚่ฟ™้ƒฝๅพ—็›ŠไบŽๆ—ฅ่ถ‹ๅผบๅคง็š„ๅผ€ๆบ็คพๅŒบ๏ผŒๅœจ่ฟ™้‡Œๆ„Ÿ่ฐข babel ๅ’Œ eslint ็ป„็ป‡ใ€‚

    ไธ‹้ขๆ˜ฏๅธฎๅŠฉๆˆ‘ไปฌๅฐ†้กน็›ฎไธญๆ‰€ๆœ‰ไธญๆ–‡ๅญ—็ฌฆ่ฝฌๆขๆˆ $t('xxxx') ็š„ๅ‡ฝๆ•ฐ๏ผŒๅฆ‚ๆžœๅ‘็”Ÿ้”™่ฏฏๅฐ†ๆ‰“ๅฐๅฏนๅบ”็š„ๆ–‡ไปถ่ทฏๅพ„ๅ’Œ้”™่ฏฏๆ—ฅๅฟ—

    const glob = require('glob')
    const path = require('path')
    const fs = require('fs')
    const { transformCode } = require('../../code-i18n/index')
    
    const root = path.resolve(__dirname)
    
    glob(
      'test/**',
      {
        cwd: root,
      },
      (err, matches) => {
        const paths = matches
          .filter((matche) => ['js', 'jsx', 'ts', 'tsx', 'vue'].includes(path.extname(matche).slice(1)))
          .map((item) => path.join(root, item))
    
        const content = paths.map((p) => fs.readFileSync(p, 'utf-8'))
    
        content
          .map((item, index) => (/[\u4e00-\u9fa5]/.test(item) ? { content: item, path: paths[index] } : false))
          .filter((a) => a)
          .forEach((source) => {
            try {
              const filetype = path.extname(source.path).slice(1)
              const { code } = transformCode(source.content, {
                type: filetype,
                parserOptions: {
                  vue: {
                    ecmaVersion: 11,
                    ecmaFeatures: {
                      jsx: true,
                    },
                  },
                },
              })
    
              fs.writeFileSync(source.path, code, { encoding: 'utf-8' })
            } catch (e) {
              console.log(source.path, e)
            }
          })
      }
    )

    Features

    ่ฟ™้‡Œๅฑ•็คบ็š„ๆ˜ฏ AST ไธญๅฏนๅบ”็š„ Node๏ผŒๆ›ดๅคšไฟกๆฏๅ‚่€ƒ estreeใ€‚

    • StringLiteral
    • TemplateLiteral
    • JSXText
    • JSXAttribute
    • Vue
      • Literal
      • VText
      • VAttribute
      • VLiteral
      • TemplateLiteral

    Tests

    ่ฟ่กŒ yarn test ่ฟ›่กŒๆต‹่ฏ•ใ€‚

    ๅฝ“ๅ‰ๆต‹่ฏ•่ฆ†็›–็Ž‡่กจใ€‚

    File % Stmts % Branch % Funcs % Lines Uncovered Line #s
    All files 100 90.57 100 100
    core 100 90.57 100 100
    parser.ts 100 100 100 100
    transform.ts 100 91.3 100 100 68,107
    vue.ts 100 89.29 100 100 38,147,170
    utils 100 100 100 100
    index.ts 100 100 100 100