locize-cli 11.0.0 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +1 -1
  3. package/README.md +1 -0
  4. package/dist/cjs/add.js +90 -0
  5. package/{bin/locize → dist/cjs/cli.js} +390 -670
  6. package/dist/cjs/combineSubkeyPreprocessor.js +155 -0
  7. package/dist/cjs/convertToDesiredFormat.js +205 -0
  8. package/dist/cjs/convertToFlatFormat.js +231 -0
  9. package/dist/cjs/copyVersion.js +60 -0
  10. package/dist/cjs/createBranch.js +59 -0
  11. package/dist/cjs/deleteBranch.js +89 -0
  12. package/dist/cjs/deleteNamespace.js +37 -0
  13. package/dist/cjs/download.js +376 -0
  14. package/dist/cjs/filterNamespaces.js +13 -0
  15. package/dist/cjs/format.js +156 -0
  16. package/dist/cjs/formats.js +33 -0
  17. package/dist/cjs/get.js +66 -0
  18. package/dist/cjs/getBranches.js +37 -0
  19. package/dist/cjs/getJob.js +37 -0
  20. package/dist/cjs/getProjectStats.js +37 -0
  21. package/dist/cjs/getRemoteLanguages.js +38 -0
  22. package/dist/cjs/getRemoteNamespace.js +125 -0
  23. package/dist/cjs/index.js +37 -0
  24. package/dist/cjs/isValidUuid.js +6 -0
  25. package/dist/cjs/lngs.js +215 -0
  26. package/dist/cjs/mapLimit.js +22 -0
  27. package/dist/cjs/mergeBranch.js +80 -0
  28. package/dist/cjs/migrate.js +239 -0
  29. package/dist/cjs/missing.js +162 -0
  30. package/dist/cjs/package.json +5 -0
  31. package/{parseLocalLanguage.js → dist/cjs/parseLocalLanguage.js} +135 -142
  32. package/dist/cjs/parseLocalLanguages.js +18 -0
  33. package/dist/cjs/parseLocalReference.js +11 -0
  34. package/dist/cjs/publishVersion.js +42 -0
  35. package/dist/cjs/removeUndefinedFromArrays.js +19 -0
  36. package/dist/cjs/removeVersion.js +42 -0
  37. package/dist/cjs/request.js +66 -0
  38. package/dist/cjs/shouldUnflatten.js +21 -0
  39. package/dist/cjs/sortFlatResources.js +13 -0
  40. package/dist/cjs/sync.js +772 -0
  41. package/dist/cjs/unflatten.js +81 -0
  42. package/dist/esm/add.js +88 -0
  43. package/dist/esm/cli.js +1020 -0
  44. package/{combineSubkeyPreprocessor.js → dist/esm/combineSubkeyPreprocessor.js} +70 -73
  45. package/dist/esm/convertToDesiredFormat.js +203 -0
  46. package/dist/esm/convertToFlatFormat.js +229 -0
  47. package/dist/esm/copyVersion.js +58 -0
  48. package/dist/esm/createBranch.js +57 -0
  49. package/dist/esm/deleteBranch.js +87 -0
  50. package/dist/esm/deleteNamespace.js +35 -0
  51. package/dist/esm/download.js +374 -0
  52. package/{filterNamespaces.js → dist/esm/filterNamespaces.js} +4 -4
  53. package/dist/esm/format.js +154 -0
  54. package/{formats.js → dist/esm/formats.js} +7 -11
  55. package/dist/esm/get.js +64 -0
  56. package/dist/esm/getBranches.js +35 -0
  57. package/dist/esm/getJob.js +35 -0
  58. package/dist/esm/getProjectStats.js +35 -0
  59. package/dist/esm/getRemoteLanguages.js +36 -0
  60. package/dist/esm/getRemoteNamespace.js +123 -0
  61. package/dist/esm/index.js +16 -0
  62. package/dist/esm/isValidUuid.js +4 -0
  63. package/dist/esm/lngs.js +213 -0
  64. package/dist/esm/mapLimit.js +20 -0
  65. package/dist/esm/mergeBranch.js +78 -0
  66. package/dist/esm/migrate.js +237 -0
  67. package/dist/esm/missing.js +160 -0
  68. package/dist/esm/parseLocalLanguage.js +194 -0
  69. package/dist/esm/parseLocalLanguages.js +16 -0
  70. package/dist/esm/parseLocalReference.js +9 -0
  71. package/dist/esm/publishVersion.js +40 -0
  72. package/{removeUndefinedFromArrays.js → dist/esm/removeUndefinedFromArrays.js} +5 -5
  73. package/dist/esm/removeVersion.js +40 -0
  74. package/dist/esm/request.js +64 -0
  75. package/{shouldUnflatten.js → dist/esm/shouldUnflatten.js} +7 -7
  76. package/dist/esm/sortFlatResources.js +11 -0
  77. package/dist/esm/sync.js +770 -0
  78. package/{unflatten.js → dist/esm/unflatten.js} +36 -34
  79. package/package.json +39 -18
  80. package/rollup.config.js +57 -0
  81. package/add.js +0 -105
  82. package/convertToDesiredFormat.js +0 -268
  83. package/convertToFlatFormat.js +0 -322
  84. package/copyVersion.js +0 -69
  85. package/createBranch.js +0 -61
  86. package/deleteBranch.js +0 -97
  87. package/deleteNamespace.js +0 -39
  88. package/download.js +0 -516
  89. package/format.js +0 -206
  90. package/get.js +0 -81
  91. package/getBranches.js +0 -40
  92. package/getJob.js +0 -40
  93. package/getProjectStats.js +0 -40
  94. package/getRemoteLanguages.js +0 -40
  95. package/getRemoteNamespace.js +0 -122
  96. package/index.js +0 -9
  97. package/isValidUuid.js +0 -2
  98. package/lngs.json +0 -211
  99. package/mergeBranch.js +0 -102
  100. package/migrate.js +0 -314
  101. package/missing.js +0 -169
  102. package/parseLocalLanguages.js +0 -22
  103. package/parseLocalReference.js +0 -10
  104. package/publishVersion.js +0 -64
  105. package/removeVersion.js +0 -64
  106. package/request.js +0 -64
  107. package/sortFlatResources.js +0 -9
  108. package/sync.js +0 -786
@@ -0,0 +1,155 @@
1
+ 'use strict';
2
+
3
+ var yaml = require('yaml');
4
+
5
+ const delimiter = {
6
+ i18next: '_',
7
+ i18njs: '.'
8
+ };
9
+
10
+ const detectFormat = (keys) => {
11
+ const i18nextMatches = keys.filter((k) => k.indexOf(delimiter.i18next) > 0).length;
12
+ const i18njsMatches = keys.filter((k) => k.indexOf(delimiter.i18njs) > 0).length;
13
+ if (i18nextMatches > i18njsMatches) {
14
+ return 'i18next'
15
+ }
16
+ if (i18nextMatches < i18njsMatches) {
17
+ return 'i18njs'
18
+ }
19
+ };
20
+
21
+ const getBaseKey = (delimiter) => (k) => {
22
+ const parts = k.split(delimiter);
23
+ parts.pop();
24
+ const baseKey = parts.join(delimiter);
25
+ return baseKey
26
+ };
27
+
28
+ const uniq = (value, index, self) => self.indexOf(value) === index;
29
+
30
+ const stringify = (o) => {
31
+ let str = yaml.stringify(o);
32
+ const subKeys = Object.keys(o);
33
+ subKeys.forEach((sk) => {
34
+ if (isNaN(sk)) {
35
+ str = str.replace(new RegExp(`^(?:${sk}: )+`, 'm'), `{${sk}}: `);
36
+ } else {
37
+ str = str.replace(new RegExp(`^(?:'${sk}': )+`, 'm'), `{${sk}}: `);
38
+ }
39
+ });
40
+ return str
41
+ };
42
+
43
+ const transformKeys = (segments, baseKeys, toMerge, deli) => {
44
+ baseKeys.forEach((bk) => {
45
+ const asObj = toMerge[bk].reduce((mem, k) => {
46
+ const subKey = k.substring((bk + deli).length);
47
+ // special handling for i18next v3
48
+ if (deli === delimiter.i18next && subKey === 'plural' && segments[bk]) {
49
+ mem['__'] = segments[bk];
50
+ delete segments[bk];
51
+ }
52
+ mem[subKey] = segments[k];
53
+ return mem
54
+ }, {});
55
+ if (Object.keys(asObj).length > 0) {
56
+ const value = stringify(asObj);
57
+ segments[`${bk}__#locize.com/combinedSubkey`] = value;
58
+ toMerge[bk].forEach((k) => {
59
+ delete segments[k];
60
+ });
61
+ }
62
+ });
63
+ return segments
64
+ };
65
+
66
+ // CLDR
67
+ const pluralForms = [
68
+ 'zero',
69
+ 'one',
70
+ 'two',
71
+ 'few',
72
+ 'many',
73
+ 'other'
74
+ ];
75
+
76
+ const endsWithPluralForm = (k) => !!pluralForms.find((f) => k.endsWith(`.${f}`)) || !!pluralForms.find((f) => k.endsWith(`_${f}`)) || /_\d+$/.test(k) || k.endsWith('_plural');
77
+
78
+ const prepareExport = (refRes, trgRes) => {
79
+ const refLngKeys = Object.keys(refRes);
80
+ const trgLngKeys = Object.keys(trgRes);
81
+
82
+ const nonMatchInRef = refLngKeys.filter((k) => trgLngKeys.indexOf(k) < 0 && endsWithPluralForm(k));
83
+ const nonMatchInTrg = trgLngKeys.filter((k) => refLngKeys.indexOf(k) < 0 && endsWithPluralForm(k));
84
+
85
+ const allMatches = nonMatchInRef.concat(nonMatchInTrg);
86
+
87
+ const format = detectFormat(allMatches);
88
+ if (!format) return { ref: refRes, trg: trgRes }
89
+
90
+ const nonMatchBaseKeysInRef = nonMatchInRef.map(getBaseKey(delimiter[format])).filter(uniq);
91
+ const nonMatchBaseKeysInTrg = nonMatchInTrg.map(getBaseKey(delimiter[format])).filter(uniq);
92
+ const nonMatchBaseKeys = nonMatchBaseKeysInRef.concat(nonMatchBaseKeysInTrg).filter(uniq);
93
+
94
+ const toMergeInRef = nonMatchBaseKeys.reduce((mem, bk) => {
95
+ mem[bk] = refLngKeys.filter((k) => k.indexOf(bk + delimiter[format]) === 0);
96
+ return mem
97
+ }, {});
98
+ const toMergeInTrg = nonMatchBaseKeys.reduce((mem, bk) => {
99
+ mem[bk] = trgLngKeys.filter((k) => k.indexOf(bk + delimiter[format]) === 0);
100
+ return mem
101
+ }, {});
102
+
103
+ let falseFlags = nonMatchBaseKeysInRef.filter((k) => toMergeInRef[k].length < 2 && (!toMergeInTrg[k] || toMergeInTrg[k].length < 2));
104
+ falseFlags = falseFlags.concat(nonMatchBaseKeysInTrg.filter((k) => toMergeInTrg[k].length < 2 && (!toMergeInRef[k] || toMergeInRef[k].length < 2)));
105
+ falseFlags.forEach((k) => {
106
+ delete toMergeInRef[k];
107
+ delete toMergeInTrg[k];
108
+ nonMatchBaseKeys.splice(nonMatchBaseKeys.indexOf(k), 1);
109
+ });
110
+
111
+ const transformedRef = transformKeys(refRes, nonMatchBaseKeys, toMergeInRef, delimiter[format]);
112
+ const transformedTrg = transformKeys(trgRes, nonMatchBaseKeys, toMergeInTrg, delimiter[format]);
113
+ return { ref: transformedRef, trg: transformedTrg }
114
+ };
115
+
116
+ // eslint-disable-next-line prefer-regex-literals
117
+ const skRegex = new RegExp('^(?:{(.+)})+', 'gm');
118
+ const parse = (s) => {
119
+ let matchArray;
120
+ while ((matchArray = skRegex.exec(s)) !== null) {
121
+ const [match, sk] = matchArray;
122
+ if (isNaN(sk)) {
123
+ s = s.replace(new RegExp(`^(?:${match}: )+`, 'm'), `${sk}: `);
124
+ } else {
125
+ const escapedMatch = match.replace('{', '\\{').replace('}', '\\}');
126
+ s = s.replace(new RegExp(`^(?:${escapedMatch}: )+`, 'm'), `${sk}: `);
127
+ }
128
+ }
129
+ return yaml.parse(s)
130
+ };
131
+
132
+ const prepareImport = (resources) => {
133
+ const keys = Object.keys(resources);
134
+ keys.forEach((k) => {
135
+ if (k.indexOf('__#locize.com/combinedSubkey') > -1) {
136
+ const baseKey = k.substring(0, k.indexOf('__#locize.com/combinedSubkey'));
137
+ if (resources[k]) {
138
+ const parsed = parse(resources[k]);
139
+ Object.keys(parsed).forEach((sk) => {
140
+ const skVal = parsed[sk];
141
+ resources[`${baseKey}_${sk}`] = skVal;
142
+ if (sk === '__') {
143
+ resources[baseKey] = resources[`${baseKey}_${sk}`];
144
+ delete resources[`${baseKey}_${sk}`];
145
+ }
146
+ });
147
+ delete resources[k];
148
+ }
149
+ }
150
+ });
151
+ return resources
152
+ };
153
+
154
+ exports.prepareExport = prepareExport;
155
+ exports.prepareImport = prepareImport;
@@ -0,0 +1,205 @@
1
+ 'use strict';
2
+
3
+ var flatten = require('flat');
4
+ var i18next2po = require('gettext-converter/i18next2po');
5
+ var csv = require('fast-csv');
6
+ var xlsx = require('xlsx');
7
+ var yaml = require('yaml');
8
+ var js2asr = require('android-string-resource/js2asr');
9
+ var stringsFile = require('strings-file');
10
+ var createxliff = require('xliff/createxliff');
11
+ var createxliff12 = require('xliff/createxliff12');
12
+ var js2resx = require('resx/js2resx');
13
+ var js2ftl = require('fluent_conv/js2ftl');
14
+ var js2tmx = require('tmexchange/js2tmx');
15
+ var js2laravel = require('laravelphp/js2laravel');
16
+ var javaProperties = require('@js.properties/properties');
17
+ var unflatten = require('./unflatten.js');
18
+ var getRemoteNamespace = require('./getRemoteNamespace.js');
19
+ var removeUndefinedFromArrays = require('./removeUndefinedFromArrays.js');
20
+ var shouldUnflatten = require('./shouldUnflatten.js');
21
+ var combineSubkeyPreprocessor = require('./combineSubkeyPreprocessor.js');
22
+
23
+ const convertToDesiredFormat = async (
24
+ opt,
25
+ namespace,
26
+ lng,
27
+ data,
28
+ lastModified
29
+ ) => {
30
+ opt.getNamespace = opt.getNamespace || getRemoteNamespace;
31
+ const isEmpty = !data || Object.keys(data).length === 0;
32
+ if (opt.format === 'json') {
33
+ try {
34
+ data = unflatten(data, true);
35
+ } catch (err) {}
36
+ return JSON.stringify(data, null, 2)
37
+ }
38
+ if (opt.format === 'nested') {
39
+ try {
40
+ data = unflatten(data);
41
+ } catch (err) {}
42
+ return JSON.stringify(data, null, 2)
43
+ }
44
+ if (opt.format === 'flat') {
45
+ return JSON.stringify(flatten(data), null, 2)
46
+ }
47
+ if (opt.format === 'po' || opt.format === 'gettext') {
48
+ const flatData = flatten(data);
49
+ const gettextOpt = {
50
+ project: 'locize',
51
+ language: lng,
52
+ potCreationDate: lastModified,
53
+ poRevisionDate: lastModified,
54
+ ctxSeparator: '_ is default but we set it to something that is never found!!!',
55
+ persistMsgIdPlural: true
56
+ };
57
+ return i18next2po(lng, flatData, gettextOpt)
58
+ }
59
+ if (opt.format === 'po_i18next' || opt.format === 'gettext_i18next') {
60
+ const flatData = flatten(data);
61
+ const compatibilityJSON = !!Object.keys(flatData).find((k) => /_(zero|one|two|few|many|other)/.test(k)) && 'v4';
62
+ const gettextOpt = {
63
+ project: 'locize',
64
+ language: lng,
65
+ potCreationDate: lastModified,
66
+ poRevisionDate: lastModified,
67
+ compatibilityJSON
68
+ };
69
+ return i18next2po(lng, flatData, gettextOpt)
70
+ }
71
+ if (opt.format === 'csv') {
72
+ const refNs = await opt.getNamespace(opt, opt.referenceLanguage, namespace);
73
+ const js2CsvData = Object.keys(flatten(data)).reduce((mem, k) => {
74
+ const value = data[k] || '';
75
+ const line = {
76
+ key: k,
77
+ [opt.referenceLanguage]: refNs[k] || '',
78
+ [lng]: value
79
+ };
80
+ mem.push(line);
81
+ return mem
82
+ }, []);
83
+ return `\ufeff${await csv.writeToString(js2CsvData, { headers: true, quoteColumns: true })}`
84
+ }
85
+ if (opt.format === 'xlsx') {
86
+ const refNs = await opt.getNamespace(opt, opt.referenceLanguage, namespace);
87
+ const js2XlsxData = Object.keys(flatten(data)).reduce((mem, k) => {
88
+ const value = data[k] || '';
89
+ const line = {
90
+ key: k,
91
+ [opt.referenceLanguage]: refNs[k] || '',
92
+ [lng]: value
93
+ };
94
+ mem.push(line);
95
+ return mem
96
+ }, []);
97
+ const worksheet = xlsx.utils.json_to_sheet(js2XlsxData);
98
+ const workbook = xlsx.utils.book_new();
99
+ let workSheetName = namespace;
100
+ if (workSheetName.length > 31) workSheetName = workSheetName.substring(0, 31);
101
+ workbook.SheetNames.push(workSheetName);
102
+ workbook.Sheets[workSheetName] = worksheet;
103
+ return xlsx.write(workbook, { type: 'buffer' })
104
+ }
105
+ if (
106
+ opt.format === 'yaml' ||
107
+ opt.format === 'yml'
108
+ ) {
109
+ if (isEmpty) return ''
110
+ return yaml.stringify(flatten(data))
111
+ }
112
+ if (
113
+ opt.format === 'yaml-nested' ||
114
+ opt.format === 'yml-nested'
115
+ ) {
116
+ if (isEmpty) return ''
117
+ return yaml.stringify(shouldUnflatten(data) ? unflatten(data) : data)
118
+ }
119
+ if (
120
+ opt.format === 'yaml-rails' ||
121
+ opt.format === 'yml-rails'
122
+ ) {
123
+ if (isEmpty) return ''
124
+ const newData = {};
125
+ newData[lng] = shouldUnflatten(data) ? unflatten(data) : data;
126
+ return yaml.stringify(removeUndefinedFromArrays(newData))
127
+ }
128
+ if (
129
+ opt.format === 'yaml-rails-ns' ||
130
+ opt.format === 'yml-rails-ns'
131
+ ) {
132
+ if (isEmpty) return ''
133
+ const newDataNs = {};
134
+ newDataNs[lng] = {};
135
+ newDataNs[lng][namespace] = shouldUnflatten(data) ? unflatten(data) : data;
136
+ return yaml.stringify(removeUndefinedFromArrays(newDataNs))
137
+ }
138
+ if (opt.format === 'android') {
139
+ return await js2asr(flatten(data))
140
+ }
141
+ if (opt.format === 'strings') {
142
+ Object.keys(data).forEach((k) => {
143
+ if (data[k] === null) delete data[k];
144
+ });
145
+ return stringsFile.compile(data)
146
+ }
147
+ if (
148
+ opt.format === 'xliff2' ||
149
+ opt.format === 'xliff12' ||
150
+ opt.format === 'xlf2' ||
151
+ opt.format === 'xlf12'
152
+ ) {
153
+ const fn =
154
+ opt.format === 'xliff12' || opt.format === 'xlf12'
155
+ ? createxliff12
156
+ : createxliff;
157
+ const refNs = await opt.getNamespace(opt, opt.referenceLanguage, namespace);
158
+ const prepared = combineSubkeyPreprocessor.prepareExport(refNs, flatten(data));
159
+ return await fn(opt.referenceLanguage, lng, prepared.ref, prepared.trg, namespace)
160
+ }
161
+ if (opt.format === 'resx') {
162
+ return await js2resx(flatten(data))
163
+ }
164
+ if (opt.format === 'fluent') {
165
+ Object.keys(data).forEach((k) => {
166
+ if (!data[k] || data[k] === '') delete data[k];
167
+ data[k] = data[k].replace(
168
+ new RegExp(String.fromCharCode(160), 'g'),
169
+ String.fromCharCode(32)
170
+ );
171
+ });
172
+ return js2ftl(unflatten(data))
173
+ }
174
+ if (opt.format === 'tmx') {
175
+ const refNs = await opt.getNamespace(opt, opt.referenceLanguage, namespace);
176
+ const js = flatten(data);
177
+ const js2TmxData = Object.keys(js).reduce(
178
+ (mem, k) => {
179
+ const refItem = refNs[k];
180
+ if (!refItem) return mem
181
+ const value = js[k] || '';
182
+ mem.resources[namespace][k] = {};
183
+ mem.resources[namespace][k][opt.referenceLanguage] = refItem;
184
+ mem.resources[namespace][k][lng] = value;
185
+ return mem
186
+ },
187
+ {
188
+ resources: {
189
+ [namespace]: {}
190
+ },
191
+ sourceLanguage: opt.referenceLanguage
192
+ }
193
+ );
194
+ return await js2tmx(js2TmxData)
195
+ }
196
+ if (opt.format === 'laravel') {
197
+ return await js2laravel(unflatten(data))
198
+ }
199
+ if (opt.format === 'properties') {
200
+ return javaProperties.stringifyFromProperties(data, { eol: '\n' })
201
+ }
202
+ throw new Error(`${opt.format} is not a valid format!`)
203
+ };
204
+
205
+ module.exports = convertToDesiredFormat;
@@ -0,0 +1,231 @@
1
+ 'use strict';
2
+
3
+ var po2i18next = require('gettext-converter/po2i18next');
4
+ var csv = require('fast-csv');
5
+ var xlsx = require('xlsx');
6
+ var yaml = require('yaml');
7
+ var asr2js = require('android-string-resource/asr2js');
8
+ var stringsFile = require('strings-file');
9
+ var xliff2js = require('xliff/xliff2js');
10
+ var xliff12ToJs = require('xliff/xliff12ToJs');
11
+ var targetOfjs = require('xliff/targetOfjs');
12
+ var sourceOfjs = require('xliff/sourceOfjs');
13
+ var resx2js = require('resx/resx2js');
14
+ var ftl2js = require('fluent_conv/ftl2js');
15
+ var tmx2js = require('tmexchange/tmx2js');
16
+ var laravel2js = require('laravelphp/laravel2js');
17
+ var javaProperties = require('@js.properties/properties');
18
+ var xcstrings2locize = require('locize-xcstrings/xcstrings2locize');
19
+ var flatten = require('flat');
20
+ var combineSubkeyPreprocessor = require('./combineSubkeyPreprocessor.js');
21
+
22
+ const convertToFlatFormat = async (opt, data, lng) => {
23
+ if (lng && typeof lng !== 'string') lng = undefined;
24
+ if (opt.format === 'json' || opt.format === 'nested' || opt.format === 'flat') {
25
+ const dataString = data.toString().trim();
26
+ if (dataString[0] !== '{' && dataString[0] !== '[') {
27
+ throw new Error(`Not a valid json file: Content starts with "${dataString[0]}" but should start with "{"`)
28
+ }
29
+ const jsonParsed = JSON.parse(dataString);
30
+ return flatten(jsonParsed)
31
+ }
32
+ if (opt.format === 'po' || opt.format === 'gettext') {
33
+ const ret = po2i18next(data.toString(), {
34
+ persistMsgIdPlural: true,
35
+ ignoreCtx: true
36
+ });
37
+ return flatten(ret)
38
+ }
39
+ if (opt.format === 'po_i18next' || opt.format === 'gettext_i18next') {
40
+ const potxt = data.toString();
41
+ const compatibilityJSON = /msgctxt "(zero|one|two|few|many|other)"/.test(potxt) && 'v4';
42
+ const ret = po2i18next(potxt, { compatibilityJSON });
43
+ return flatten(ret)
44
+ }
45
+ if (opt.format === 'csv') {
46
+ // CRLF => LF
47
+ const text = data.toString().replace(/\r\n/g, '\n');
48
+ const rows = await csv.parseString(text, { headers: true, ignoreEmpty: true }).promise();
49
+ const result = rows.reduce((mem, entry) => {
50
+ if (entry.key && typeof entry[opt.referenceLanguage] === 'string') {
51
+ mem[entry.key] = entry[opt.referenceLanguage];
52
+ }
53
+ return mem
54
+ }, {});
55
+ return result
56
+ }
57
+ if (opt.format === 'xlsx') {
58
+ const wb = xlsx.read(data, { type: 'buffer' });
59
+ const jsonData = xlsx.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
60
+ const result = jsonData.reduce((mem, entry) => {
61
+ if (entry.key && typeof entry[opt.referenceLanguage] === 'string') {
62
+ mem[entry.key] = entry[opt.referenceLanguage];
63
+ }
64
+ return mem
65
+ }, {});
66
+ return result
67
+ }
68
+ if (
69
+ opt.format === 'yaml' ||
70
+ opt.format === 'yml'
71
+ ) {
72
+ const d = data.toString();
73
+ if (!d || d === '' || d === '\n') return {}
74
+ return flatten(yaml.parse(d))
75
+ }
76
+ if (
77
+ opt.format === 'yaml-nested' ||
78
+ opt.format === 'yml-nested'
79
+ ) {
80
+ const d = data.toString();
81
+ if (!d || d === '' || d === '\n') return {}
82
+ return flatten(yaml.parse(d))
83
+ }
84
+ if (
85
+ opt.format === 'yaml-rails' ||
86
+ opt.format === 'yml-rails'
87
+ ) {
88
+ const d = data.toString();
89
+ if (!d || d.trim() === '') return {}
90
+ const jsObj = yaml.parse(d);
91
+ return flatten(jsObj[Object.keys(jsObj)[0]])
92
+ }
93
+ if (
94
+ opt.format === 'yaml-rails-ns' ||
95
+ opt.format === 'yml-rails-ns'
96
+ ) {
97
+ const dn = data.toString();
98
+ if (!dn || dn.trim() === '') return {}
99
+ const jsObjn = yaml.parse(dn);
100
+ return flatten(jsObjn[Object.keys(jsObjn)[0]][Object.keys(jsObjn[Object.keys(jsObjn)[0]])[0]])
101
+ }
102
+ if (opt.format === 'android') {
103
+ const res = await asr2js(data.toString(), { comment: 'right' });
104
+ Object.keys(res).forEach((k) => {
105
+ if (res[k] !== 'string' && typeof res[k].comment === 'string') {
106
+ res[k] = {
107
+ value: res[k].value,
108
+ context: {
109
+ text: res[k].comment,
110
+ },
111
+ };
112
+ } else {
113
+ res[k] = { value: res[k].value || res[k] };
114
+ }
115
+ });
116
+ return res
117
+ }
118
+ if (opt.format === 'strings') {
119
+ // CRLF => LF
120
+ return stringsFile.parse(data.toString().replace(/\r\n/g, '\n'), false)
121
+ }
122
+ if (
123
+ opt.format === 'xliff2' ||
124
+ opt.format === 'xliff12' ||
125
+ opt.format === 'xlf2' ||
126
+ opt.format === 'xlf12'
127
+ ) {
128
+ const fn =
129
+ opt.format === 'xliff12' || opt.format === 'xlf12'
130
+ ? xliff12ToJs
131
+ : xliff2js;
132
+ const res = await fn(data.toString());
133
+ res.resources = res.resources || {};
134
+ const ns = Object.keys(res.resources)[0];
135
+ const orgRes = res.resources[ns] || res.resources;
136
+ function checkForPostProcessing (nsRes) {
137
+ Object.keys(nsRes).forEach((k) => {
138
+ if (orgRes[k].note && (typeof nsRes[k] === 'string' || !nsRes[k])) {
139
+ nsRes[k] = {
140
+ value: nsRes[k],
141
+ context: {
142
+ text: orgRes[k].note,
143
+ }
144
+ };
145
+ }
146
+ });
147
+ return combineSubkeyPreprocessor.prepareImport(nsRes)
148
+ }
149
+ if (!res.targetLanguage) {
150
+ const ret = await sourceOfjs(res);
151
+ return checkForPostProcessing(ret)
152
+ } else {
153
+ let ret = targetOfjs(res);
154
+ if (lng !== opt.referenceLanguage) return checkForPostProcessing(ret)
155
+ ret = ret || {};
156
+ const keys = Object.keys(ret);
157
+ if (keys.length === 0) return checkForPostProcessing(ret)
158
+ const allEmpty = keys.filter((k) => ret[k] !== '').length === 0;
159
+ if (!allEmpty) return checkForPostProcessing(ret)
160
+ ret = await sourceOfjs(res);
161
+ return checkForPostProcessing(ret)
162
+ }
163
+ }
164
+ if (opt.format === 'resx') {
165
+ let res = await resx2js(data.toString());
166
+ res = Object.keys(res).reduce((mem, k) => {
167
+ const value = res[k];
168
+ if (typeof value === 'string') {
169
+ mem[k] = value;
170
+ } else if (value.value) {
171
+ mem[k] = {
172
+ value: value.value,
173
+ context: value.comment ? { text: value.comment } : null,
174
+ };
175
+ }
176
+ return mem
177
+ }, {});
178
+ return res
179
+ }
180
+ if (opt.format === 'fluent') {
181
+ const fluentJS = ftl2js(
182
+ data
183
+ .toString()
184
+ .replace(
185
+ new RegExp(String.fromCharCode(160), 'g'),
186
+ String.fromCharCode(32)
187
+ )
188
+ );
189
+ const comments = {};
190
+ Object.keys(fluentJS).forEach((prop) => {
191
+ if (fluentJS[prop] && fluentJS[prop].comment) {
192
+ comments[prop] = fluentJS[prop].comment;
193
+ delete fluentJS[prop].comment;
194
+ }
195
+ });
196
+ const res = flatten(fluentJS);
197
+ if (res && comments) {
198
+ Object.keys(comments).forEach((prop) => {
199
+ res[`${prop}.val`] = {
200
+ value: res[`${prop}.val`],
201
+ context: comments[prop] ? { text: comments[prop] } : null,
202
+ };
203
+ });
204
+ }
205
+ return res
206
+ }
207
+ if (opt.format === 'tmx') {
208
+ const jsonData = await tmx2js(data.toString());
209
+ const tmxJsRes = jsonData.resources[Object.keys(jsonData.resources)[0]];
210
+ const res = {};
211
+ if (tmxJsRes) {
212
+ Object.keys(tmxJsRes).forEach((k) => {
213
+ res[k] = tmxJsRes[k][opt.referenceLanguage];
214
+ });
215
+ }
216
+ return res
217
+ }
218
+ if (opt.format === 'laravel') {
219
+ const res = await laravel2js(data.toString());
220
+ return flatten(res)
221
+ }
222
+ if (opt.format === 'properties') {
223
+ return javaProperties.parseToProperties(data.toString())
224
+ }
225
+ if (opt.format === 'xcstrings') {
226
+ return xcstrings2locize(data.toString())
227
+ }
228
+ throw new Error(`${opt.format} is not a valid format!`)
229
+ };
230
+
231
+ module.exports = convertToFlatFormat;
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+
3
+ var colors = require('colors');
4
+ var request = require('./request.js');
5
+ var getJob = require('./getJob.js');
6
+
7
+ const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
8
+
9
+ const copyVersion = async (opt) => {
10
+ const queryParams = new URLSearchParams();
11
+ if (opt.ignoreIfVersionExists) {
12
+ queryParams.append('ignoreIfVersionExists', 'true');
13
+ }
14
+ const queryString = queryParams.size > 0 ? '?' + queryParams.toString() : '';
15
+ const { res, obj, err } = await request(opt.apiEndpoint + '/copy/' + opt.projectId + '/version/' + opt.fromVersion + '/' + opt.toVersion + queryString, {
16
+ method: 'post',
17
+ headers: {
18
+ Authorization: opt.apiKey
19
+ }
20
+ });
21
+
22
+ if (err || (obj && (obj.errorMessage || obj.message))) {
23
+ console.log(colors.red(`copy failed from ${opt.fromVersion} to ${opt.toVersion}...`));
24
+ if (err) {
25
+ console.error(colors.red(err.message));
26
+ throw err
27
+ }
28
+ if (obj && (obj.errorMessage || obj.message)) {
29
+ console.error(colors.red((obj.errorMessage || obj.message)));
30
+ throw new Error((obj.errorMessage || obj.message))
31
+ }
32
+ }
33
+ if (res.status >= 300) {
34
+ console.error(colors.red(res.statusText + ' (' + res.status + ')'));
35
+ throw new Error(res.statusText + ' (' + res.status + ')')
36
+ }
37
+
38
+ if (!obj || !obj.jobId) {
39
+ console.error(colors.red('No jobId! Something went wrong!'));
40
+ throw new Error('No jobId! Something went wrong!')
41
+ }
42
+
43
+ let job;
44
+ while (true) {
45
+ job = await getJob(opt, obj.jobId);
46
+ if (job && !job.timeouted) {
47
+ await sleep(2000);
48
+ continue
49
+ }
50
+ if (job && job.timeouted) {
51
+ console.error(colors.red('Job timeouted!'));
52
+ throw new Error('Job timeouted!')
53
+ }
54
+ break
55
+ }
56
+ console.log(colors.green(`copy from ${opt.fromVersion} to ${opt.toVersion} succesfully requested`));
57
+ // done
58
+ };
59
+
60
+ module.exports = copyVersion;