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'
const forceOverwrite = argv.force
+ const language = getLanguage()
+
let result: {
projectName?: string
shouldOverwrite?: boolean
{
name: 'projectName',
type: targetDir ? null : 'text',
- message: 'Project name:',
+ message: language.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 === '.'
+ ? language.shouldOverwrite.dirForPrompts.current
+ : `${language.shouldOverwrite.dirForPrompts.target} "${targetDir}"`
- return `${dirForPrompt} is not empty. Remove existing files and continue?`
- }
+ return `${dirForPrompt} ${language.shouldOverwrite.message}`
+ },
+ initial: true,
+ active: language.defaultToggleOptions.active,
+ inactive: language.defaultToggleOptions.inactive
},
{
name: 'overwriteChecker',
type: (prev, values) => {
if (values.shouldOverwrite === false) {
- throw new Error(red('✖') + ' Operation cancelled')
+ throw new Error(red('✖') + ` ${language.errors.operationCancelled}`)
}
return null
}
{
name: 'packageName',
type: () => (isValidPackageName(targetDir) ? null : 'text'),
- message: 'Package name:',
+ message: language.packageName.message,
initial: () => toValidPackageName(targetDir),
validate: (dir) => isValidPackageName(dir) || 'Invalid package.json name'
},
{
name: 'needsTypeScript',
type: () => (isFeatureFlagsUsed ? null : 'toggle'),
- message: 'Add TypeScript?',
+ message: language.needsTypeScript.message,
initial: false,
- active: 'Yes',
- inactive: 'No'
+ active: language.defaultToggleOptions.active,
+ inactive: language.defaultToggleOptions.inactive
},
{
name: 'needsJsx',
type: () => (isFeatureFlagsUsed ? null : 'toggle'),
- message: 'Add JSX Support?',
+ message: language.needsJsx.message,
initial: false,
- active: 'Yes',
- inactive: 'No'
+ active: language.defaultToggleOptions.active,
+ inactive: language.defaultToggleOptions.inactive
},
{
name: 'needsRouter',
type: () => (isFeatureFlagsUsed ? null : 'toggle'),
- message: 'Add Vue Router for Single Page Application development?',
+ message: language.needsRouter.message,
initial: false,
- active: 'Yes',
- inactive: 'No'
+ active: language.defaultToggleOptions.active,
+ inactive: language.defaultToggleOptions.inactive
},
{
name: 'needsPinia',
type: () => (isFeatureFlagsUsed ? null : 'toggle'),
- message: 'Add Pinia for state management?',
+ message: language.needsPinia.message,
initial: false,
- active: 'Yes',
- inactive: 'No'
+ active: language.defaultToggleOptions.active,
+ inactive: language.defaultToggleOptions.inactive
},
{
name: 'needsVitest',
type: () => (isFeatureFlagsUsed ? null : 'toggle'),
- message: 'Add Vitest for Unit Testing?',
+ message: language.needsVitest.message,
initial: false,
- active: 'Yes',
- inactive: 'No'
+ active: language.defaultToggleOptions.active,
+ inactive: language.defaultToggleOptions.inactive
},
{
name: 'needsE2eTesting',
type: () => (isFeatureFlagsUsed ? null : 'select'),
- message: 'Add an End-to-End Testing Solution?',
+ message: language.needsE2eTesting.message,
initial: 0,
choices: (prev, answers) => [
- { title: 'No', value: false },
{
- title: 'Cypress',
+ title: language.needsE2eTesting.selectOptions.negative.title,
+ value: false
+ },
+ {
+ title: language.needsE2eTesting.selectOptions.cypress.title,
description: answers.needsVitest
? undefined
- : 'also supports unit testing with Cypress Component Testing',
+ : language.needsE2eTesting.selectOptions.cypress.desc,
value: 'cypress'
},
{
- title: 'Nightwatch',
+ title: language.needsE2eTesting.selectOptions.nightwatch.title,
description: answers.needsVitest
? undefined
- : 'also supports unit testing with Nightwatch Component Testing',
+ : language.needsE2eTesting.selectOptions.nightwatch.desc,
value: 'nightwatch'
},
{
- title: 'Playwright',
+ title: language.needsE2eTesting.selectOptions.playwright.title,
value: 'playwright'
}
]
{
name: 'needsEslint',
type: () => (isFeatureFlagsUsed ? null : 'toggle'),
- message: 'Add ESLint for code quality?',
+ message: language.needsEslint.message,
initial: false,
- active: 'Yes',
- inactive: 'No'
+ active: language.defaultToggleOptions.active,
+ inactive: language.defaultToggleOptions.inactive
},
{
name: 'needsPrettier',
}
return 'toggle'
},
- message: 'Add Prettier for code formatting?',
+ message: language.needsPrettier.message,
initial: false,
- active: 'Yes',
- inactive: 'No'
+ active: language.defaultToggleOptions.active,
+ inactive: language.defaultToggleOptions.inactive
}
],
{
onCancel: () => {
- throw new Error(red('✖') + ' Operation cancelled')
+ throw new Error(red('✖') + ` ${language.errors.operationCancelled}`)
}
}
)
fs.mkdirSync(root)
}
- console.log(`\nScaffolding project in ${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))
})
)
- console.log(`\nDone. Now run:\n`)
+ console.log(`\n${language.infos.done}\n`)
if (root !== cwd) {
const cdProjectName = path.relative(cwd, root)
console.log(
--- /dev/null
+{
+ "projectName": {
+ "message": "Project name:"
+ },
+ "shouldOverwrite": {
+ "dirForPrompts": {
+ "current": "Current directory",
+ "target": "Target directory"
+ },
+ "message": "is not empty. Remove existing files and continue?"
+ },
+ "packageName": {
+ "message": "Package name:"
+ },
+ "needsTypeScript": {
+ "message": "Add TypeScript?"
+ },
+ "needsJsx": {
+ "message": "Add JSX Support?"
+ },
+ "needsRouter": {
+ "message": "Add Vue Router for Single Page Application development?"
+ },
+ "needsPinia": {
+ "message": "Add Pinia for state management?"
+ },
+ "needsVitest": {
+ "message": "Add Vitest for Unit Testing?"
+ },
+ "needsE2eTesting": {
+ "message": "Add an End-to-End Testing Solution?",
+ "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"
+ },
+ "playwright": { "title": "Playwright" }
+ }
+ },
+ "needsEslint": {
+ "message": "Add ESLint for code quality?"
+ },
+ "needsPrettier": {
+ "message": "Add Prettier for code formatting?"
+ },
+ "errors": {
+ "operationCancelled": "Operation cancelled"
+ },
+ "defaultToggleOptions": {
+ "active": "Yes",
+ "inactive": "No"
+ },
+ "infos": {
+ "scaffolding": "Scaffolding project in",
+ "done": "Done. Now run:"
+ }
+}
--- /dev/null
+import * as fs from 'node:fs'
+import * as path from 'node:path'
+
+interface LanguageItem {
+ message: string
+ dirForPrompts?: {
+ current: string
+ target: string
+ }
+ toggleOptions?: {
+ active: string
+ inactive: string
+ }
+ selectOptions?: {
+ [key: string]: { 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
+ }
+ defaultToggleOptions: {
+ active: string
+ inactive: 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
+}