From 0f1e1ee4a8cb42f082acafe4b7171aaabc301d7c Mon Sep 17 00:00:00 2001 From: luventa Date: Mon, 25 Sep 2023 22:13:19 +0800 Subject: [PATCH 1/4] feat: i18n solution for prompts messages --- index.ts | 133 ++++++++++++++++++++++++++++++++------------- locales/en-US.json | 60 ++++++++++++++++++++ locales/zh-CN.json | 58 ++++++++++++++++++++ package.json | 1 + scripts/build.mjs | 1 + 5 files changed, 214 insertions(+), 39 deletions(-) create mode 100644 locales/en-US.json create mode 100644 locales/zh-CN.json diff --git a/index.ts b/index.ts index b5fd6867..e099b226 100755 --- a/index.ts +++ b/index.ts @@ -5,7 +5,7 @@ import * as path from 'node:path' import minimist from 'minimist' import prompts from 'prompts' -import { red, green, bold } from 'kolorist' +import { red, green, bold, yellow } from 'kolorist' import ejs from 'ejs' @@ -115,6 +115,33 @@ async function init() { const forceOverwrite = argv.force + type LocaleItem = { + message: string + dirForPrompts?: string[] + toggleOptions?: string[] + selectOptions?: { title: string; desc?: string }[] + } + let locales: { + projectName: LocaleItem + shouldOverwrite: LocaleItem + packageName: LocaleItem + needsTypeScript: LocaleItem + needsJsx: LocaleItem + needsRouter: LocaleItem + needsPinia: LocaleItem + needsVitest: LocaleItem + needsE2eTesting: LocaleItem + needsEslint: LocaleItem + needsPrettier: LocaleItem + errors: { + operationCancelled: string + } + infos: { + scaffolding: string + done: string + } + } + let result: { projectName?: string shouldOverwrite?: boolean @@ -130,6 +157,26 @@ async function init() { } = {} try { + const i18n = await prompts( + [ + { + name: 'language', + type: 'select', + message: 'Select Language', + choices: () => [ + { title: 'English', value: 'en-US' }, + { title: '简体中文', value: 'zh-CN' } + ] + } + ], + { + onCancel: () => { + console.log(yellow('>') + ' Default Language is English') + } + } + ) + const localesRoot = path.resolve(__dirname, 'locales') + locales = require(path.resolve(localesRoot, `${i18n.language || 'en-US'}.json`)) // Prompts: // - Project name: // - whether to overwrite the existing directory or not? @@ -148,25 +195,30 @@ async function init() { { name: 'projectName', type: targetDir ? null : 'text', - message: 'Project name:', + message: locales.projectName.message, initial: defaultProjectName, onState: (state) => (targetDir = String(state.value).trim() || defaultProjectName) }, { name: 'shouldOverwrite', - type: () => (canSkipEmptying(targetDir) || forceOverwrite ? null : 'confirm'), + type: () => (canSkipEmptying(targetDir) || forceOverwrite ? null : 'toggle'), message: () => { const dirForPrompt = - targetDir === '.' ? 'Current directory' : `Target directory "${targetDir}"` + targetDir === '.' + ? locales.shouldOverwrite.dirForPrompts[0] + : `${locales.shouldOverwrite.dirForPrompts[1]}Target directory "${targetDir}"` - return `${dirForPrompt} is not empty. Remove existing files and continue?` - } + return `${dirForPrompt} ${locales.shouldOverwrite.message}` + }, + initial: true, + active: locales.shouldOverwrite.toggleOptions[0], + inactive: locales.shouldOverwrite.toggleOptions[1] }, { name: 'overwriteChecker', type: (prev, values) => { if (values.shouldOverwrite === false) { - throw new Error(red('✖') + ' Operation cancelled') + throw new Error(red('✖') + ` ${locales.errors.operationCancelled}`) } return null } @@ -174,73 +226,76 @@ async function init() { { name: 'packageName', type: () => (isValidPackageName(targetDir) ? null : 'text'), - message: 'Package name:', + message: locales.packageName.message, initial: () => toValidPackageName(targetDir), validate: (dir) => isValidPackageName(dir) || 'Invalid package.json name' }, { name: 'needsTypeScript', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: 'Add TypeScript?', + message: locales.needsTypeScript.message, initial: false, - active: 'Yes', - inactive: 'No' + active: locales.needsTypeScript.toggleOptions[0], + inactive: locales.needsTypeScript.toggleOptions[1] }, { name: 'needsJsx', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: 'Add JSX Support?', + message: locales.needsJsx.message, initial: false, - active: 'Yes', - inactive: 'No' + active: locales.needsJsx.toggleOptions[0], + inactive: locales.needsJsx.toggleOptions[1] }, { name: 'needsRouter', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: 'Add Vue Router for Single Page Application development?', + message: locales.needsRouter.message, initial: false, - active: 'Yes', - inactive: 'No' + active: locales.needsRouter.toggleOptions[0], + inactive: locales.needsRouter.toggleOptions[1] }, { name: 'needsPinia', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: 'Add Pinia for state management?', + message: locales.needsPinia.message, initial: false, - active: 'Yes', - inactive: 'No' + active: locales.needsPinia.toggleOptions[0], + inactive: locales.needsPinia.toggleOptions[1] }, { name: 'needsVitest', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: 'Add Vitest for Unit Testing?', + message: locales.needsVitest.message, initial: false, - active: 'Yes', - inactive: 'No' + active: locales.needsVitest.toggleOptions[0], + inactive: locales.needsVitest.toggleOptions[1] }, { name: 'needsE2eTesting', type: () => (isFeatureFlagsUsed ? null : 'select'), - message: 'Add an End-to-End Testing Solution?', + message: locales.needsE2eTesting.message, initial: 0, choices: (prev, answers) => [ - { title: 'No', value: false }, { - title: 'Cypress', + title: locales.needsE2eTesting.selectOptions[0].title, + value: false + }, + { + title: locales.needsE2eTesting.selectOptions[1].title, description: answers.needsVitest ? undefined - : 'also supports unit testing with Cypress Component Testing', + : locales.needsE2eTesting.selectOptions[1].desc, value: 'cypress' }, { - title: 'Nightwatch', + title: locales.needsE2eTesting.selectOptions[2].title, description: answers.needsVitest ? undefined - : 'also supports unit testing with Nightwatch Component Testing', + : locales.needsE2eTesting.selectOptions[2].desc, value: 'nightwatch' }, { - title: 'Playwright', + title: locales.needsE2eTesting.selectOptions[3].title, value: 'playwright' } ] @@ -248,10 +303,10 @@ async function init() { { name: 'needsEslint', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: 'Add ESLint for code quality?', + message: locales.needsEslint.message, initial: false, - active: 'Yes', - inactive: 'No' + active: locales.needsEslint.toggleOptions[0], + inactive: locales.needsEslint.toggleOptions[1] }, { name: 'needsPrettier', @@ -261,15 +316,15 @@ async function init() { } return 'toggle' }, - message: 'Add Prettier for code formatting?', + message: locales.needsPrettier.message, initial: false, - active: 'Yes', - inactive: 'No' + active: locales.needsPrettier.toggleOptions[0], + inactive: locales.needsPrettier.toggleOptions[1] } ], { onCancel: () => { - throw new Error(red('✖') + ' Operation cancelled') + throw new Error(red('✖') + ` ${locales.errors.operationCancelled}`) } } ) @@ -308,7 +363,7 @@ async function init() { fs.mkdirSync(root) } - console.log(`\nScaffolding project in ${root}...`) + console.log(`\n${locales.infos.scaffolding} ${root}...`) const pkg = { name: packageName, version: '0.0.0' } fs.writeFileSync(path.resolve(root, 'package.json'), JSON.stringify(pkg, null, 2)) @@ -496,7 +551,7 @@ async function init() { }) ) - console.log(`\nDone. Now run:\n`) + console.log(`\n${locales.infos.done}\n`) if (root !== cwd) { const cdProjectName = path.relative(cwd, root) console.log( diff --git a/locales/en-US.json b/locales/en-US.json new file mode 100644 index 00000000..15f17769 --- /dev/null +++ b/locales/en-US.json @@ -0,0 +1,60 @@ +{ + "projectName": { + "message": "Project name:" + }, + "shouldOverwrite": { + "dirForPrompts": ["Current directory", "Target directory"], + "message": "is not empty. Remove existing files and continue?", + "toggleOptions": ["Yes", "No"] + }, + "packageName": { + "message": "Package name:" + }, + "needsTypeScript": { + "message": "Add TypeScript?", + "toggleOptions": ["Yes", "No"] + }, + "needsJsx": { + "message": "Add JSX Support?", + "toggleOptions": ["Yes", "No"] + }, + "needsRouter": { + "message": "Add Vue Router for Single Page Application development?", + "toggleOptions": ["Yes", "No"] + }, + "needsPinia": { + "message": "Add Pinia for state management?", + "toggleOptions": ["Yes", "No"] + }, + "needsVitest": { + "message": "Add Vitest for Unit Testing?", + "toggleOptions": ["Yes", "No"] + }, + "needsE2eTesting": { + "message": "Add an End-to-End Testing Solution?", + "selectOptions": [ + { "title": "No" }, + { "title": "Cypress", "desc": "also supports unit testing with Cypress Component Testing" }, + { + "title": "Nightwatch", + "desc": "also supports unit testing with Nightwatch Component Testing" + }, + { "title": "Playwright" } + ] + }, + "needsEslint": { + "message": "Add ESLint for code quality?", + "toggleOptions": ["Yes", "No"] + }, + "needsPrettier": { + "message": "Add Prettier for code formatting?", + "toggleOptions": ["Yes", "No"] + }, + "errors": { + "operationCancelled": "Operation cancelled" + }, + "infos": { + "scaffolding": "Scaffolding project in", + "done": "Done. Now run:" + } +} diff --git a/locales/zh-CN.json b/locales/zh-CN.json new file mode 100644 index 00000000..68c2cde3 --- /dev/null +++ b/locales/zh-CN.json @@ -0,0 +1,58 @@ +{ + "projectName": { + "message": "请输入项目名称:" + }, + "shouldOverwrite": { + "dirForPrompts": ["当前目录", "目标文件夹"], + "message": "非空,是否覆盖?", + "toggleOptions": ["是", "否"] + }, + "packageName": { + "message": "请输入包名称:", + "toggleOptions": ["是", "否"] + }, + "needsTypeScript": { + "message": "是否使用 TypeScript 语法?", + "toggleOptions": ["是", "否"] + }, + "needsJsx": { + "message": "是否启用 JSX 支持?", + "toggleOptions": ["是", "否"] + }, + "needsRouter": { + "message": "是否引入 Vue Router 进行单页面应用开发?", + "toggleOptions": ["是", "否"] + }, + "needsPinia": { + "message": "是否引入 Pinia 用于状态管理?", + "toggleOptions": ["是", "否"] + }, + "needsVitest": { + "message": "是否引入 Vitest 用于单元测试?", + "toggleOptions": ["是", "否"] + }, + "needsE2eTesting": { + "message": "是否要引入一款端到端(End to End)测试工具?", + "selectOptions": [ + { "title": "不需要" }, + { "title": "Cypress", "desc": "同时支持基于 Cypress Component Testing 的单元测试" }, + { "title": "Nightwatch", "desc": "同时支持基于 Nightwatch Component Testing 的单元测试" }, + { "title": "Playwright" } + ] + }, + "needsEslint": { + "message": "是否引入 ESLint 用于代码质量检测?", + "toggleOptions": ["是", "否"] + }, + "needsPrettier": { + "message": "是否引入 Prettier 用于代码格式化?", + "toggleOptions": ["是", "否"] + }, + "errors": { + "operationCancelled": "操作取消" + }, + "infos": { + "scaffolding": "正在构建项目", + "done": "项目构建完成,可执行以下命令:" + } +} diff --git a/package.json b/package.json index 0334dbda..2c341f33 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "create-vue": "outfile.cjs" }, "files": [ + "locales", "outfile.cjs", "template" ], diff --git a/scripts/build.mjs b/scripts/build.mjs index adca4c6e..f1d780be 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -27,6 +27,7 @@ SOFTWARE. await esbuild.build({ bundle: true, entryPoints: ['index.ts'], + external: ['locales/*'], outfile: 'outfile.cjs', format: 'cjs', platform: 'node', From 26ad6c156845f9e1ff1bc72bae039c0074cead7c Mon Sep 17 00:00:00 2001 From: luventa Date: Wed, 27 Sep 2023 23:03:15 +0800 Subject: [PATCH 2/4] feat: retrieve language setting from the environment variables --- index.ts | 126 ++++++++++++++----------------------------- utils/getLanguage.ts | 58 ++++++++++++++++++++ 2 files changed, 99 insertions(+), 85 deletions(-) create mode 100644 utils/getLanguage.ts diff --git a/index.ts b/index.ts index e099b226..f8935203 100755 --- a/index.ts +++ b/index.ts @@ -15,6 +15,7 @@ import renderTemplate from './utils/renderTemplate' import { postOrderDirectoryTraverse, preOrderDirectoryTraverse } from './utils/directoryTraverse' import generateReadme from './utils/generateReadme' import getCommand from './utils/getCommand' +import getLanguage from './utils/getLanguage' import renderEslint from './utils/renderEslint' import { FILES_TO_FILTER } from './utils/filterList' @@ -115,32 +116,7 @@ async function init() { const forceOverwrite = argv.force - type LocaleItem = { - message: string - dirForPrompts?: string[] - toggleOptions?: string[] - selectOptions?: { title: string; desc?: string }[] - } - let locales: { - projectName: LocaleItem - shouldOverwrite: LocaleItem - packageName: LocaleItem - needsTypeScript: LocaleItem - needsJsx: LocaleItem - needsRouter: LocaleItem - needsPinia: LocaleItem - needsVitest: LocaleItem - needsE2eTesting: LocaleItem - needsEslint: LocaleItem - needsPrettier: LocaleItem - errors: { - operationCancelled: string - } - infos: { - scaffolding: string - done: string - } - } + const language = getLanguage() let result: { projectName?: string @@ -157,26 +133,6 @@ async function init() { } = {} try { - const i18n = await prompts( - [ - { - name: 'language', - type: 'select', - message: 'Select Language', - choices: () => [ - { title: 'English', value: 'en-US' }, - { title: '简体中文', value: 'zh-CN' } - ] - } - ], - { - onCancel: () => { - console.log(yellow('>') + ' Default Language is English') - } - } - ) - const localesRoot = path.resolve(__dirname, 'locales') - locales = require(path.resolve(localesRoot, `${i18n.language || 'en-US'}.json`)) // Prompts: // - Project name: // - whether to overwrite the existing directory or not? @@ -195,7 +151,7 @@ async function init() { { name: 'projectName', type: targetDir ? null : 'text', - message: locales.projectName.message, + message: language.projectName.message, initial: defaultProjectName, onState: (state) => (targetDir = String(state.value).trim() || defaultProjectName) }, @@ -205,20 +161,20 @@ async function init() { message: () => { const dirForPrompt = targetDir === '.' - ? locales.shouldOverwrite.dirForPrompts[0] - : `${locales.shouldOverwrite.dirForPrompts[1]}Target directory "${targetDir}"` + ? language.shouldOverwrite.dirForPrompts[0] + : `${language.shouldOverwrite.dirForPrompts[1]}Target directory "${targetDir}"` - return `${dirForPrompt} ${locales.shouldOverwrite.message}` + return `${dirForPrompt} ${language.shouldOverwrite.message}` }, initial: true, - active: locales.shouldOverwrite.toggleOptions[0], - inactive: locales.shouldOverwrite.toggleOptions[1] + active: language.shouldOverwrite.toggleOptions[0], + inactive: language.shouldOverwrite.toggleOptions[1] }, { name: 'overwriteChecker', type: (prev, values) => { if (values.shouldOverwrite === false) { - throw new Error(red('✖') + ` ${locales.errors.operationCancelled}`) + throw new Error(red('✖') + ` ${language.errors.operationCancelled}`) } return null } @@ -226,76 +182,76 @@ async function init() { { name: 'packageName', type: () => (isValidPackageName(targetDir) ? null : 'text'), - message: locales.packageName.message, + message: language.packageName.message, initial: () => toValidPackageName(targetDir), validate: (dir) => isValidPackageName(dir) || 'Invalid package.json name' }, { name: 'needsTypeScript', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: locales.needsTypeScript.message, + message: language.needsTypeScript.message, initial: false, - active: locales.needsTypeScript.toggleOptions[0], - inactive: locales.needsTypeScript.toggleOptions[1] + active: language.needsTypeScript.toggleOptions[0], + inactive: language.needsTypeScript.toggleOptions[1] }, { name: 'needsJsx', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: locales.needsJsx.message, + message: language.needsJsx.message, initial: false, - active: locales.needsJsx.toggleOptions[0], - inactive: locales.needsJsx.toggleOptions[1] + active: language.needsJsx.toggleOptions[0], + inactive: language.needsJsx.toggleOptions[1] }, { name: 'needsRouter', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: locales.needsRouter.message, + message: language.needsRouter.message, initial: false, - active: locales.needsRouter.toggleOptions[0], - inactive: locales.needsRouter.toggleOptions[1] + active: language.needsRouter.toggleOptions[0], + inactive: language.needsRouter.toggleOptions[1] }, { name: 'needsPinia', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: locales.needsPinia.message, + message: language.needsPinia.message, initial: false, - active: locales.needsPinia.toggleOptions[0], - inactive: locales.needsPinia.toggleOptions[1] + active: language.needsPinia.toggleOptions[0], + inactive: language.needsPinia.toggleOptions[1] }, { name: 'needsVitest', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: locales.needsVitest.message, + message: language.needsVitest.message, initial: false, - active: locales.needsVitest.toggleOptions[0], - inactive: locales.needsVitest.toggleOptions[1] + active: language.needsVitest.toggleOptions[0], + inactive: language.needsVitest.toggleOptions[1] }, { name: 'needsE2eTesting', type: () => (isFeatureFlagsUsed ? null : 'select'), - message: locales.needsE2eTesting.message, + message: language.needsE2eTesting.message, initial: 0, choices: (prev, answers) => [ { - title: locales.needsE2eTesting.selectOptions[0].title, + title: language.needsE2eTesting.selectOptions[0].title, value: false }, { - title: locales.needsE2eTesting.selectOptions[1].title, + title: language.needsE2eTesting.selectOptions[1].title, description: answers.needsVitest ? undefined - : locales.needsE2eTesting.selectOptions[1].desc, + : language.needsE2eTesting.selectOptions[1].desc, value: 'cypress' }, { - title: locales.needsE2eTesting.selectOptions[2].title, + title: language.needsE2eTesting.selectOptions[2].title, description: answers.needsVitest ? undefined - : locales.needsE2eTesting.selectOptions[2].desc, + : language.needsE2eTesting.selectOptions[2].desc, value: 'nightwatch' }, { - title: locales.needsE2eTesting.selectOptions[3].title, + title: language.needsE2eTesting.selectOptions[3].title, value: 'playwright' } ] @@ -303,10 +259,10 @@ async function init() { { name: 'needsEslint', type: () => (isFeatureFlagsUsed ? null : 'toggle'), - message: locales.needsEslint.message, + message: language.needsEslint.message, initial: false, - active: locales.needsEslint.toggleOptions[0], - inactive: locales.needsEslint.toggleOptions[1] + active: language.needsEslint.toggleOptions[0], + inactive: language.needsEslint.toggleOptions[1] }, { name: 'needsPrettier', @@ -316,15 +272,15 @@ async function init() { } return 'toggle' }, - message: locales.needsPrettier.message, + message: language.needsPrettier.message, initial: false, - active: locales.needsPrettier.toggleOptions[0], - inactive: locales.needsPrettier.toggleOptions[1] + active: language.needsPrettier.toggleOptions[0], + inactive: language.needsPrettier.toggleOptions[1] } ], { onCancel: () => { - throw new Error(red('✖') + ` ${locales.errors.operationCancelled}`) + throw new Error(red('✖') + ` ${language.errors.operationCancelled}`) } } ) @@ -363,7 +319,7 @@ async function init() { fs.mkdirSync(root) } - console.log(`\n${locales.infos.scaffolding} ${root}...`) + console.log(`\n${language.infos.scaffolding} ${root}...`) const pkg = { name: packageName, version: '0.0.0' } fs.writeFileSync(path.resolve(root, 'package.json'), JSON.stringify(pkg, null, 2)) @@ -551,7 +507,7 @@ async function init() { }) ) - console.log(`\n${locales.infos.done}\n`) + console.log(`\n${language.infos.done}\n`) if (root !== cwd) { const cdProjectName = path.relative(cwd, root) console.log( diff --git a/utils/getLanguage.ts b/utils/getLanguage.ts new file mode 100644 index 00000000..d57ffa69 --- /dev/null +++ b/utils/getLanguage.ts @@ -0,0 +1,58 @@ +import * as fs from 'node:fs' +import * as path from 'node:path' + +interface LanguageItem { + message: string + dirForPrompts?: string[] + toggleOptions?: string[] + selectOptions?: { title: string; desc?: string }[] +} + +interface Language { + projectName: LanguageItem + shouldOverwrite: LanguageItem + packageName: LanguageItem + needsTypeScript: LanguageItem + needsJsx: LanguageItem + needsRouter: LanguageItem + needsPinia: LanguageItem + needsVitest: LanguageItem + needsE2eTesting: LanguageItem + needsEslint: LanguageItem + needsPrettier: LanguageItem + errors: { + operationCancelled: string + } + infos: { + scaffolding: string + done: string + } +} + +function getLocale() { + const shellLocale = + process.env.LC_ALL || + process.env.LANG || // Unix maybe + process.env.LC_CTYPE || // C libraries maybe + process.env.LANGSPEC || // Windows maybe + Intl.DateTimeFormat().resolvedOptions().locale || // Node.js - Internationalization support + 'en-US' + + const locale = shellLocale.split('.')[0].replace('_', '-') + + // locale might be 'C' or something else + return locale.length < 5 ? 'en-US' : locale +} + +export default function getLanguage() { + const locale = getLocale() + const localesRoot = path.resolve(__dirname, 'locales') + const languageFilePath = path.resolve(localesRoot, `${locale}.json`) + const doesLanguageExist = fs.existsSync(languageFilePath) + + const lang: Language = doesLanguageExist + ? require(languageFilePath) + : require(path.resolve(localesRoot, 'en-US.json')) + + return lang +} From ac73b9a5442954a4c5ea62a3c2439e48854b51d8 Mon Sep 17 00:00:00 2001 From: luventa Date: Wed, 27 Sep 2023 23:17:41 +0800 Subject: [PATCH 3/4] refactor: remove useless import statement --- index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.ts b/index.ts index f8935203..69941dce 100755 --- a/index.ts +++ b/index.ts @@ -5,7 +5,7 @@ import * as path from 'node:path' import minimist from 'minimist' import prompts from 'prompts' -import { red, green, bold, yellow } from 'kolorist' +import { red, green, bold } from 'kolorist' import ejs from 'ejs' From de142e4bfc62f7c6f715f03af841eee4d6480b1c Mon Sep 17 00:00:00 2001 From: luventa Date: Thu, 12 Oct 2023 22:26:02 +0800 Subject: [PATCH 4/4] refactor: improve code readability --- index.ts | 48 +++++++++++++++++------------------ locales/en-US.json | 48 ++++++++++++++++++----------------- locales/zh-CN.json | 60 +++++++++++++++++++++++--------------------- utils/getLanguage.ts | 18 ++++++++++--- 4 files changed, 96 insertions(+), 78 deletions(-) diff --git a/index.ts b/index.ts index 69941dce..9daf1f8b 100755 --- a/index.ts +++ b/index.ts @@ -161,14 +161,14 @@ async function init() { message: () => { const dirForPrompt = targetDir === '.' - ? language.shouldOverwrite.dirForPrompts[0] - : `${language.shouldOverwrite.dirForPrompts[1]}Target directory "${targetDir}"` + ? language.shouldOverwrite.dirForPrompts.current + : `${language.shouldOverwrite.dirForPrompts.target} "${targetDir}"` return `${dirForPrompt} ${language.shouldOverwrite.message}` }, initial: true, - active: language.shouldOverwrite.toggleOptions[0], - inactive: language.shouldOverwrite.toggleOptions[1] + active: language.defaultToggleOptions.active, + inactive: language.defaultToggleOptions.inactive }, { name: 'overwriteChecker', @@ -191,40 +191,40 @@ async function init() { type: () => (isFeatureFlagsUsed ? null : 'toggle'), message: language.needsTypeScript.message, initial: false, - active: language.needsTypeScript.toggleOptions[0], - inactive: language.needsTypeScript.toggleOptions[1] + active: language.defaultToggleOptions.active, + inactive: language.defaultToggleOptions.inactive }, { name: 'needsJsx', type: () => (isFeatureFlagsUsed ? null : 'toggle'), message: language.needsJsx.message, initial: false, - active: language.needsJsx.toggleOptions[0], - inactive: language.needsJsx.toggleOptions[1] + active: language.defaultToggleOptions.active, + inactive: language.defaultToggleOptions.inactive }, { name: 'needsRouter', type: () => (isFeatureFlagsUsed ? null : 'toggle'), message: language.needsRouter.message, initial: false, - active: language.needsRouter.toggleOptions[0], - inactive: language.needsRouter.toggleOptions[1] + active: language.defaultToggleOptions.active, + inactive: language.defaultToggleOptions.inactive }, { name: 'needsPinia', type: () => (isFeatureFlagsUsed ? null : 'toggle'), message: language.needsPinia.message, initial: false, - active: language.needsPinia.toggleOptions[0], - inactive: language.needsPinia.toggleOptions[1] + active: language.defaultToggleOptions.active, + inactive: language.defaultToggleOptions.inactive }, { name: 'needsVitest', type: () => (isFeatureFlagsUsed ? null : 'toggle'), message: language.needsVitest.message, initial: false, - active: language.needsVitest.toggleOptions[0], - inactive: language.needsVitest.toggleOptions[1] + active: language.defaultToggleOptions.active, + inactive: language.defaultToggleOptions.inactive }, { name: 'needsE2eTesting', @@ -233,25 +233,25 @@ async function init() { initial: 0, choices: (prev, answers) => [ { - title: language.needsE2eTesting.selectOptions[0].title, + title: language.needsE2eTesting.selectOptions.negative.title, value: false }, { - title: language.needsE2eTesting.selectOptions[1].title, + title: language.needsE2eTesting.selectOptions.cypress.title, description: answers.needsVitest ? undefined - : language.needsE2eTesting.selectOptions[1].desc, + : language.needsE2eTesting.selectOptions.cypress.desc, value: 'cypress' }, { - title: language.needsE2eTesting.selectOptions[2].title, + title: language.needsE2eTesting.selectOptions.nightwatch.title, description: answers.needsVitest ? undefined - : language.needsE2eTesting.selectOptions[2].desc, + : language.needsE2eTesting.selectOptions.nightwatch.desc, value: 'nightwatch' }, { - title: language.needsE2eTesting.selectOptions[3].title, + title: language.needsE2eTesting.selectOptions.playwright.title, value: 'playwright' } ] @@ -261,8 +261,8 @@ async function init() { type: () => (isFeatureFlagsUsed ? null : 'toggle'), message: language.needsEslint.message, initial: false, - active: language.needsEslint.toggleOptions[0], - inactive: language.needsEslint.toggleOptions[1] + active: language.defaultToggleOptions.active, + inactive: language.defaultToggleOptions.inactive }, { name: 'needsPrettier', @@ -274,8 +274,8 @@ async function init() { }, message: language.needsPrettier.message, initial: false, - active: language.needsPrettier.toggleOptions[0], - inactive: language.needsPrettier.toggleOptions[1] + active: language.defaultToggleOptions.active, + inactive: language.defaultToggleOptions.inactive } ], { diff --git a/locales/en-US.json b/locales/en-US.json index 15f17769..7369c2ef 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -3,56 +3,58 @@ "message": "Project name:" }, "shouldOverwrite": { - "dirForPrompts": ["Current directory", "Target directory"], - "message": "is not empty. Remove existing files and continue?", - "toggleOptions": ["Yes", "No"] + "dirForPrompts": { + "current": "Current directory", + "target": "Target directory" + }, + "message": "is not empty. Remove existing files and continue?" }, "packageName": { "message": "Package name:" }, "needsTypeScript": { - "message": "Add TypeScript?", - "toggleOptions": ["Yes", "No"] + "message": "Add TypeScript?" }, "needsJsx": { - "message": "Add JSX Support?", - "toggleOptions": ["Yes", "No"] + "message": "Add JSX Support?" }, "needsRouter": { - "message": "Add Vue Router for Single Page Application development?", - "toggleOptions": ["Yes", "No"] + "message": "Add Vue Router for Single Page Application development?" }, "needsPinia": { - "message": "Add Pinia for state management?", - "toggleOptions": ["Yes", "No"] + "message": "Add Pinia for state management?" }, "needsVitest": { - "message": "Add Vitest for Unit Testing?", - "toggleOptions": ["Yes", "No"] + "message": "Add Vitest for Unit Testing?" }, "needsE2eTesting": { "message": "Add an End-to-End Testing Solution?", - "selectOptions": [ - { "title": "No" }, - { "title": "Cypress", "desc": "also supports unit testing with Cypress Component Testing" }, - { + "selectOptions": { + "negative": { "title": "No" }, + "cypress": { + "title": "Cypress", + "desc": "also supports unit testing with Cypress Component Testing" + }, + "nightwatch": { "title": "Nightwatch", "desc": "also supports unit testing with Nightwatch Component Testing" }, - { "title": "Playwright" } - ] + "playwright": { "title": "Playwright" } + } }, "needsEslint": { - "message": "Add ESLint for code quality?", - "toggleOptions": ["Yes", "No"] + "message": "Add ESLint for code quality?" }, "needsPrettier": { - "message": "Add Prettier for code formatting?", - "toggleOptions": ["Yes", "No"] + "message": "Add Prettier for code formatting?" }, "errors": { "operationCancelled": "Operation cancelled" }, + "defaultToggleOptions": { + "active": "Yes", + "inactive": "No" + }, "infos": { "scaffolding": "Scaffolding project in", "done": "Done. Now run:" diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 68c2cde3..6abe87a1 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -1,58 +1,62 @@ { "projectName": { - "message": "请输入项目名称:" + "message": "请输入项目名称:" }, "shouldOverwrite": { - "dirForPrompts": ["当前目录", "目标文件夹"], - "message": "非空,是否覆盖?", - "toggleOptions": ["是", "否"] + "dirForPrompts": { + "current": "当前目录", + "target": "目标文件夹" + }, + "message": "非空,是否覆盖?" }, "packageName": { - "message": "请输入包名称:", - "toggleOptions": ["是", "否"] + "message": "请输入包名称:" }, "needsTypeScript": { - "message": "是否使用 TypeScript 语法?", - "toggleOptions": ["是", "否"] + "message": "是否使用 TypeScript 语法?" }, "needsJsx": { - "message": "是否启用 JSX 支持?", - "toggleOptions": ["是", "否"] + "message": "是否启用 JSX 支持?" }, "needsRouter": { - "message": "是否引入 Vue Router 进行单页面应用开发?", - "toggleOptions": ["是", "否"] + "message": "是否引入 Vue Router 进行单页面应用开发?" }, "needsPinia": { - "message": "是否引入 Pinia 用于状态管理?", - "toggleOptions": ["是", "否"] + "message": "是否引入 Pinia 用于状态管理?" }, "needsVitest": { - "message": "是否引入 Vitest 用于单元测试?", - "toggleOptions": ["是", "否"] + "message": "是否引入 Vitest 用于单元测试?" }, "needsE2eTesting": { - "message": "是否要引入一款端到端(End to End)测试工具?", - "selectOptions": [ - { "title": "不需要" }, - { "title": "Cypress", "desc": "同时支持基于 Cypress Component Testing 的单元测试" }, - { "title": "Nightwatch", "desc": "同时支持基于 Nightwatch Component Testing 的单元测试" }, - { "title": "Playwright" } - ] + "message": "是否要引入一款端到端(End to End)测试工具?", + "selectOptions": { + "negative": { "title": "不需要" }, + "cypress": { + "title": "Cypress", + "desc": "同时支持基于 Cypress Component Testing 的单元测试" + }, + "nightwatch": { + "title": "Nightwatch", + "desc": "同时支持基于 Nightwatch Component Testing 的单元测试" + }, + "playwright": { "title": "Playwright" } + } }, "needsEslint": { - "message": "是否引入 ESLint 用于代码质量检测?", - "toggleOptions": ["是", "否"] + "message": "是否引入 ESLint 用于代码质量检测?" }, "needsPrettier": { - "message": "是否引入 Prettier 用于代码格式化?", - "toggleOptions": ["是", "否"] + "message": "是否引入 Prettier 用于代码格式化?" }, "errors": { "operationCancelled": "操作取消" }, + "defaultToggleOptions": { + "active": "是", + "inactive": "否" + }, "infos": { "scaffolding": "正在构建项目", - "done": "项目构建完成,可执行以下命令:" + "done": "项目构建完成,可执行以下命令:" } } diff --git a/utils/getLanguage.ts b/utils/getLanguage.ts index d57ffa69..537439b7 100644 --- a/utils/getLanguage.ts +++ b/utils/getLanguage.ts @@ -3,9 +3,17 @@ import * as path from 'node:path' interface LanguageItem { message: string - dirForPrompts?: string[] - toggleOptions?: string[] - selectOptions?: { title: string; desc?: string }[] + dirForPrompts?: { + current: string + target: string + } + toggleOptions?: { + active: string + inactive: string + } + selectOptions?: { + [key: string]: { title: string; desc?: string } + } } interface Language { @@ -23,6 +31,10 @@ interface Language { errors: { operationCancelled: string } + defaultToggleOptions: { + active: string + inactive: string + } infos: { scaffolding: string done: string