diff --git a/.gitignore b/.gitignore index 9d6ea2c..e380392 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ /package-lock.json /tmp node_modules +.idea/.idea.fluent-vue-cli.dir/.idea/indexLayout.xml +.idea/.idea.fluent-vue-cli.dir/.idea/workspace.xml +.idea diff --git a/package.json b/package.json index 6244a84..cb2850a 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "@vue/compiler-dom": "^3.2.6", "@vue/compiler-sfc": "^3.2.6", "cac": "^6.7.12", - "fast-glob": "^3.2.11" + "fast-glob": "^3.2.11", + "unixify": "^1.0.0" }, "files": [ "/bin", @@ -33,6 +34,7 @@ "devDependencies": { "@antfu/eslint-config-basic": "0.16.1", "@types/node": "10.17.60", + "@types/unixify": "^1.0.0", "eslint": "8.9.0", "tsup": "5.11.13", "typescript": "4.5.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e387fa..98ef67c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,7 @@ specifiers: '@antfu/eslint-config-basic': 0.16.1 '@fluent/syntax': ^0.17.0 '@types/node': 10.17.60 + '@types/unixify': ^1.0.0 '@vue/compiler-dom': ^3.2.6 '@vue/compiler-sfc': ^3.2.6 cac: ^6.7.12 @@ -11,6 +12,7 @@ specifiers: fast-glob: ^3.2.11 tsup: 5.11.13 typescript: 4.5.5 + unixify: ^1.0.0 vitest: 0.3.6 dependencies: @@ -19,10 +21,12 @@ dependencies: '@vue/compiler-sfc': 3.2.31 cac: 6.7.12 fast-glob: 3.2.11 + unixify: 1.0.0 devDependencies: '@antfu/eslint-config-basic': 0.16.1_eslint@8.9.0 '@types/node': 10.17.60 + '@types/unixify': 1.0.0 eslint: 8.9.0 tsup: 5.11.13_typescript@4.5.5 typescript: 4.5.5 @@ -155,6 +159,10 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true + /@types/unixify/1.0.0: + resolution: {integrity: sha512-JZMxxE1x9vR5TfKM0SmiePGlAc4h7LK4likxmwYKDCZfJjPHe4yL/ulkuKWV7oOYTxtbmBHYisnpiaBssosQdw==} + dev: true + /@vue/compiler-core/3.2.31: resolution: {integrity: sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==} dependencies: @@ -1653,6 +1661,13 @@ packages: validate-npm-package-license: 3.0.4 dev: true + /normalize-path/2.1.1: + resolution: {integrity: sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=} + engines: {node: '>=0.10.0'} + dependencies: + remove-trailing-separator: 1.1.0 + dev: false + /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -1898,6 +1913,10 @@ packages: engines: {node: '>=8'} dev: true + /remove-trailing-separator/1.1.0: + resolution: {integrity: sha1-wkvOKig62tW8P1jg1IJJuSN52O8=} + dev: false + /resolve-from/4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2231,6 +2250,13 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /unixify/1.0.0: + resolution: {integrity: sha1-OmQcjC/7zk2mg6XHDwOkYpQMIJA=} + engines: {node: '>=0.10.0'} + dependencies: + normalize-path: 2.1.1 + dev: false + /uri-js/4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: diff --git a/src/cli/commands/export.ts b/src/cli/commands/export.ts index 5ef50ed..5750129 100644 --- a/src/cli/commands/export.ts +++ b/src/cli/commands/export.ts @@ -1,29 +1,27 @@ import { stream } from 'fast-glob' import { promises as fs, existsSync } from 'fs' -import { resolve, dirname } from 'path' - +import {resolve, dirname, join, relative} from 'path' +import unixify from 'unixify' import { getVueMessages, mergeFtl } from '../..' interface Options { + inDir: string outDir: string clean: boolean } const log = console.log.bind(console) -export const run = async (argv: any[], flags: Options) => { +export const run = async (flags: Options) => { let count = 0 - for await (const file of stream(argv)) { + for await (const file of stream(unixify(flags.inDir) + '/**', {ignore: ['**/node_modules']})) { count ++ const data = await fs.readFile(file) const vueMessages = getVueMessages(data.toString()) - for (const { locale, source, messages } of vueMessages) { - const outputPath = resolve(flags.outDir, locale, `${file}.ftl`) - + const outputPath = join(flags.outDir, locale, relative(flags.inDir, `${file}.ftl`)) await fs.mkdir(dirname(outputPath), { recursive: true }) - if (flags.clean || !existsSync(outputPath)) { await fs.writeFile(outputPath, source) } else { diff --git a/src/cli/commands/import.ts b/src/cli/commands/import.ts new file mode 100644 index 0000000..d2fcfeb --- /dev/null +++ b/src/cli/commands/import.ts @@ -0,0 +1,36 @@ +import { stream } from 'fast-glob' +import { promises as fs } from 'fs' +import { resolve, dirname, basename, extname, sep, relative, join } from 'path' +import unixify from 'unixify' +import { getFtlMessages, mergeVue } from '../..' + +interface Options { + outDir: string, + inDir: string +} + +const log = console.log.bind(console) + +export const run = async (flags: Options) => { + let count = 0 + for await (const file of stream(unixify(flags.inDir) + '/**', {ignore: ['**/node_modules']})) { + count ++ + const fileString = file.toString() + const fileDirName= dirname(file.toString()) + + const vueComponentName = basename(fileString).replace(extname(fileString), '') + const outputPath = join(relative(flags.inDir, fileDirName), vueComponentName) + const [locale, ...rest] = outputPath.split(sep) + const realOutputPath = join(flags.outDir, rest.join(sep)) + + const vueFile = await fs.readFile(realOutputPath) + const data = await fs.readFile(file) + const ftlMessages = getFtlMessages(data.toString()) + const newData = mergeVue(vueFile.toString(), locale, ftlMessages) + + await fs.writeFile(realOutputPath, newData) + } + + log(`Imported messages to ${count} files`) +} + diff --git a/src/cli/index.ts b/src/cli/index.ts index 003fdf4..a5651ce 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -2,14 +2,23 @@ import cac from 'cac' import { run as runExport } from './commands/export' +import { run as runImport } from './commands/import' + const cli = cac('fluent-vue') cli - .command('export ', 'Exports translation from Vue.js SFC files into ftl files.') + .command('export', 'Exports translation from Vue.js SFC files into ftl files.') + .option('--in-dir', 'Input directory with vue files', { default: 'example/' }) .option('--out-dir', 'Output directory for extracted ftl files', { default: 'translations/' }) .option('--clean', 'Whether to clean output directory', { default: true }) .action(runExport) +cli + .command('import', 'Import translation from ftl files into Vue.js SFC') + .option('--in-dir', 'Input directory with ftl files', { default: 'translations/' }) + .option('--out-dir', 'Output directory for extracted vue files', { default: 'example/' }) + .action(runImport) + cli.help() cli.parse() diff --git a/translations/en/example/App.vue.ftl b/translations/en/App.vue.ftl similarity index 73% rename from translations/en/example/App.vue.ftl rename to translations/en/App.vue.ftl index 80170c8..454dc50 100644 --- a/translations/en/example/App.vue.ftl +++ b/translations/en/App.vue.ftl @@ -1,5 +1,5 @@ # Comment user-name = World aria-key = Aria value -greeting = Hello, {$name} +greeting = Hello, { $name } .aria-label = Label value