Skip to content

Commit df664bf

Browse files
authored
diff command feature (#46)
* feat: diff command * update docs
1 parent b3670ce commit df664bf

File tree

9 files changed

+425
-55
lines changed

9 files changed

+425
-55
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ yarn global vue-i18n-locale-message
5454
- infuse the locale messages to `i18n` custom block
5555
- push the locale messages to localization service
5656
- pull the locale mesagees from localization service
57+
- diff locale messages between local and localization service
5758

5859
## :rocket: Usages
5960

@@ -128,6 +129,15 @@ vue-i18n-locale-message pull --provider=l10n-service-provider \
128129
--output=./src/locales
129130
```
130131

132+
#### Diff
133+
134+
```sh
135+
vue-i18n-locale-message diff --provider=l10n-service-provider \
136+
--conf=110n-service-provider-conf.json \
137+
--target-paths=./src/locales/*.json \
138+
--filename-match=^([\\w]*)\\.json
139+
```
140+
131141
## :book: API: Specifications
132142

133143
<p align="center"><img width="476px" height="544px" src="./assets/api-usage-image.png" alt="API Usage Image"></p>
@@ -162,7 +172,7 @@ You can use the `push` or `pull` commands to push the locale message to the loca
162172

163173
<p align="center"><img src="./assets/push-pull-command-image.png" alt="Push and Pull Image"></p>
164174

165-
When you run `push` or `pull` commands, you need the provider that implements the following.
175+
When you run `push`, `pull` and `diff` commands, you need the provider that implements the following.
166176

167177
- export provider factory function
168178
- provider factory function must return a provider object that have the following I/F:

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"glob": "^7.1.4",
3232
"js-yaml": "^3.13.1",
3333
"json5": "^2.1.0",
34+
"json-diff": "^0.5.4",
3435
"vue-template-compiler": "^2.6.10",
3536
"yargs": "^15.0.0"
3637
},

src/commands/diff.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { Arguments, Argv } from 'yargs'
2+
import { diffString } from 'json-diff'
3+
4+
import {
5+
resolveProviderConf,
6+
loadProvider,
7+
loadProviderConf,
8+
DEFUALT_CONF,
9+
getLocaleMessages,
10+
PushableOptions
11+
} from '../utils'
12+
13+
import { Locale } from '../../types'
14+
15+
type DiffOptions = {
16+
provider: string
17+
conf?: string
18+
normalize?: string
19+
} & PushableOptions
20+
21+
export const command = 'diff'
22+
export const aliases = 'df'
23+
export const describe = 'Diff locale messages between local and localization service'
24+
25+
export const builder = (args: Argv): Argv<DiffOptions> => {
26+
return args
27+
.option('provider', {
28+
type: 'string',
29+
alias: 'p',
30+
describe: 'the target localization service provider',
31+
demandOption: true
32+
})
33+
.option('conf', {
34+
type: 'string',
35+
alias: 'c',
36+
describe: 'the json file configration of localization service provider. If omitted, use the suffix file name with `-conf` for provider name of --provider (e.g. <provider>-conf.json).'
37+
})
38+
.option('target', {
39+
type: 'string',
40+
alias: 't',
41+
describe: 'target path that locale messages file is stored, default push with the filename of target path as locale'
42+
})
43+
.option('locale', {
44+
type: 'string',
45+
alias: 'l',
46+
describe: `option for the locale of locale messages file specified with --target, if it's specified single-file`
47+
})
48+
.option('targetPaths', {
49+
type: 'string',
50+
alias: 'T',
51+
describe: 'target directory paths that locale messages files is stored, Can also be specified multi paths with comma delimiter'
52+
})
53+
.option('filenameMatch', {
54+
type: 'string',
55+
alias: 'm',
56+
describe: `option should be accepted a regex filenames, must be specified together --targets if it's directory path of locale messages`
57+
})
58+
.option('normalize', {
59+
type: 'string',
60+
alias: 'n',
61+
describe: 'option for the locale messages structure, you can specify the option, if you hope to normalize for the provider.'
62+
})
63+
}
64+
65+
export const handler = async (args: Arguments<DiffOptions>): Promise<unknown> => {
66+
const { normalize } = args
67+
const ProviderFactory = loadProvider(args.provider)
68+
69+
if (ProviderFactory === null) {
70+
// TODO: should refactor console message
71+
console.log(`Not found ${args.provider} provider`)
72+
return
73+
}
74+
75+
if (!args.target && !args.targetPaths) {
76+
// TODO: should refactor console message
77+
console.log('You need to specify either --target or --target-paths')
78+
return
79+
}
80+
81+
const confPath = resolveProviderConf(args.provider, args.conf)
82+
const conf = loadProviderConf(confPath) || DEFUALT_CONF
83+
84+
let localeMessages
85+
try {
86+
localeMessages = getLocaleMessages(args)
87+
} catch (e) {
88+
console.log(e.message)
89+
return
90+
}
91+
92+
try {
93+
const provider = ProviderFactory(conf)
94+
const locales = Object.keys(localeMessages) as Locale[]
95+
const serviceMessages = await provider.pull({ locales, dryRun: false, normalize })
96+
console.log(diffString(localeMessages, serviceMessages))
97+
} catch (e) {
98+
// TODO: should refactor console message
99+
console.error('diff fail', e)
100+
}
101+
}
102+
103+
export default {
104+
command,
105+
aliases,
106+
describe,
107+
builder,
108+
handler
109+
}

src/commands/pull.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ export const handler = async (args: Arguments<PullOptions>): Promise<unknown> =>
9090
try {
9191
const locales = args.locales?.split(',').filter(p => p) as Locale[] || []
9292
const provider = ProviderFactory(conf)
93-
const resource = await provider.pull({ locales, dryRun, normalize })
94-
await applyPullLocaleMessages(args.output, resource, args.dryRun)
93+
const messages = await provider.pull({ locales, dryRun, normalize })
94+
await applyPullLocaleMessages(args.output, messages, args.dryRun)
9595
// TODO: should refactor console message
9696
console.log('pull success')
9797
} catch (e) {

src/commands/push.ts

Lines changed: 4 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,20 @@
11
import { Arguments, Argv } from 'yargs'
22

33
import {
4-
resolve,
54
resolveProviderConf,
65
loadProvider,
76
loadProviderConf,
8-
DEFUALT_CONF
7+
DEFUALT_CONF,
8+
getLocaleMessages,
9+
PushableOptions
910
} from '../utils'
10-
import path from 'path'
11-
import glob from 'glob'
12-
13-
import { debug as Debug } from 'debug'
14-
const debug = Debug('vue-i18n-locale-message:commands:push')
15-
16-
import { LocaleMessages } from '../../types'
1711

1812
type PushOptions = {
1913
provider: string
2014
conf?: string
21-
target?: string
22-
locale?: string
23-
targetPaths?: string
24-
filenameMatch?: string
2515
normalize?: string
2616
dryRun: boolean
27-
}
17+
} & PushableOptions
2818

2919
export const command = 'push'
3020
export const aliases = 'ph'
@@ -114,42 +104,6 @@ export const handler = async (args: Arguments<PushOptions>): Promise<unknown> =>
114104
}
115105
}
116106

117-
function getLocaleMessages (args: Arguments<PushOptions>): LocaleMessages {
118-
let messages = {} as LocaleMessages
119-
120-
if (args.target) {
121-
const targetPath = resolve(args.target)
122-
const parsed = path.parse(targetPath)
123-
const locale = args.locale ? args.locale : parsed.name
124-
messages = Object.assign(messages, { [locale]: require(targetPath) })
125-
} else if (args.targetPaths) {
126-
const filenameMatch = args.filenameMatch
127-
if (!filenameMatch) {
128-
// TODO: should refactor console message
129-
throw new Error('You need to specify together --filename-match')
130-
}
131-
const targetPaths = args.targetPaths.split(',').filter(p => p)
132-
targetPaths.forEach(targetPath => {
133-
const globedPaths = glob.sync(targetPath).map(p => resolve(p))
134-
globedPaths.forEach(fullPath => {
135-
const parsed = path.parse(fullPath)
136-
const re = new RegExp(filenameMatch, 'ig')
137-
const match = re.exec(parsed.base)
138-
debug('regex match', match, fullPath)
139-
if (match && match[1]) {
140-
const locale = match[1]
141-
messages = Object.assign(messages, { [locale]: require(fullPath) })
142-
} else {
143-
// TODO: should refactor console message
144-
console.log(`${fullPath} is not matched with ${filenameMatch}`)
145-
}
146-
})
147-
})
148-
}
149-
150-
return messages
151-
}
152-
153107
export default {
154108
command,
155109
aliases,

src/utils.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { Arguments } from 'yargs'
12
import { SFCDescriptor } from 'vue-template-compiler'
23
import { SFCFileInfo, FormatOptions } from '../types'
34
import { VueTemplateCompiler } from '@vue/component-compiler-utils/dist/types'
4-
import { ProviderFactory, ProviderConfiguration } from '../types'
5+
import { LocaleMessages, ProviderFactory, ProviderConfiguration } from '../types'
56

67
import { parse } from '@vue/component-compiler-utils'
78
import * as compiler from 'vue-template-compiler'
@@ -14,6 +15,13 @@ import yaml from 'js-yaml'
1415
import { debug as Debug } from 'debug'
1516
const debug = Debug('vue-i18n-locale-message:utils')
1617

18+
export type PushableOptions = {
19+
target?: string
20+
locale?: string
21+
targetPaths?: string
22+
filenameMatch?: string
23+
}
24+
1725
const ESC: { [key in string]: string } = {
1826
'<': '&lt;',
1927
'>': '&gt;',
@@ -123,7 +131,7 @@ function resolveGlob (target: string) {
123131
return glob.sync(`${target}/**/*.vue`)
124132
}
125133

126-
export const DEFUALT_CONF = { provider: {}, pushMode: 'locale-message' } as ProviderConfiguration
134+
export const DEFUALT_CONF = { provider: {}} as ProviderConfiguration
127135

128136
export function resolveProviderConf (provider: string, conf?: string) {
129137
if (conf) {
@@ -157,3 +165,39 @@ export function loadProviderConf (confPath: string): ProviderConfiguration {
157165
} catch (e) { }
158166
return conf
159167
}
168+
169+
export function getLocaleMessages (args: Arguments<PushableOptions>): LocaleMessages {
170+
let messages = {} as LocaleMessages
171+
172+
if (args.target) {
173+
const targetPath = resolve(args.target)
174+
const parsed = path.parse(targetPath)
175+
const locale = args.locale ? args.locale : parsed.name
176+
messages = Object.assign(messages, { [locale]: require(targetPath) })
177+
} else if (args.targetPaths) {
178+
const filenameMatch = args.filenameMatch
179+
if (!filenameMatch) {
180+
// TODO: should refactor console message
181+
throw new Error('You need to specify together --filename-match')
182+
}
183+
const targetPaths = args.targetPaths.split(',').filter(p => p)
184+
targetPaths.forEach(targetPath => {
185+
const globedPaths = glob.sync(targetPath).map(p => resolve(p))
186+
globedPaths.forEach(fullPath => {
187+
const parsed = path.parse(fullPath)
188+
const re = new RegExp(filenameMatch, 'ig')
189+
const match = re.exec(parsed.base)
190+
debug('regex match', match, fullPath)
191+
if (match && match[1]) {
192+
const locale = match[1]
193+
messages = Object.assign(messages, { [locale]: require(fullPath) })
194+
} else {
195+
// TODO: should refactor console message
196+
console.log(`${fullPath} is not matched with ${filenameMatch}`)
197+
}
198+
})
199+
})
200+
}
201+
202+
return messages
203+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`--locale option 1`] = `
4+
" {
5+
en: {
6+
+ nest: {
7+
+ world: \\"world!\\"
8+
+ }
9+
- hello: \\"世界\\"
10+
+ hello: \\"hello!\\"
11+
}
12+
}
13+
"
14+
`;
15+
16+
exports[`--target option 1`] = `
17+
" {
18+
ja: {
19+
- world: \\"ザ・ワールド\\"
20+
+ nest: {
21+
+ world: \\"世界!\\"
22+
+ }
23+
- hello: \\"こんにちわわわ!\\"
24+
+ hello: \\"こんにちは!\\"
25+
}
26+
}
27+
"
28+
`;
29+
30+
exports[`--target-paths option 1`] = `
31+
" {
32+
- ja: {
33+
- hello: \\"こんにちわわわ!\\"
34+
- world: \\"ザ・ワールド\\"
35+
- }
36+
- lang: {
37+
- hello: \\"世界\\"
38+
- }
39+
en: {
40+
+ nest: {
41+
+ world: \\"world!\\"
42+
+ }
43+
- hello: \\"world\\"
44+
+ hello: \\"hello!\\"
45+
}
46+
}
47+
"
48+
`;

0 commit comments

Comments
 (0)