@lingui/cli 3.17.2 → 4.0.0-next.1
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.
- package/build/api/catalog/extractFromFiles.js +45 -0
- package/build/api/catalog/getCatalogs.js +129 -0
- package/build/api/catalog/getTranslationsForCatalog.js +77 -0
- package/build/api/catalog/mergeCatalog.js +44 -0
- package/build/api/catalog.js +75 -336
- package/build/api/compile.js +0 -12
- package/build/api/extractors/babel.js +67 -24
- package/build/api/extractors/index.js +8 -11
- package/build/api/extractors/typescript.js +2 -46
- package/build/api/formats/csv.js +4 -2
- package/build/api/formats/index.js +1 -1
- package/build/api/formats/lingui.js +4 -3
- package/build/api/formats/minimal.js +8 -14
- package/build/api/formats/po-gettext.js +89 -126
- package/build/api/formats/po.js +92 -61
- package/build/api/generateMessageId.js +12 -0
- package/build/api/help.js +3 -3
- package/build/api/index.js +31 -6
- package/build/api/types.js +5 -0
- package/build/api/utils.js +77 -53
- package/build/lingui-compile.js +32 -50
- package/build/lingui-extract-template.js +17 -26
- package/build/lingui-extract.js +36 -54
- package/build/lingui.js +2 -5
- package/build/services/translationIO.js +7 -3
- package/build/tests.js +55 -10
- package/package.json +16 -21
- package/CHANGELOG.md +0 -530
- package/build/api/detect.js +0 -52
- package/build/api/extract.js +0 -59
- package/build/api/locales.js +0 -36
- package/build/lingui-add-locale.js +0 -8
package/build/api/catalog.js
CHANGED
|
@@ -4,50 +4,44 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.cleanObsolete = exports.Catalog = void 0;
|
|
7
|
-
exports.getCatalogForFile = getCatalogForFile;
|
|
8
|
-
exports.getCatalogForMerge = getCatalogForMerge;
|
|
9
|
-
exports.getCatalogs = getCatalogs;
|
|
10
|
-
exports.normalizeRelativePath = normalizeRelativePath;
|
|
11
7
|
exports.order = order;
|
|
12
8
|
exports.orderByMessageId = orderByMessageId;
|
|
13
9
|
exports.orderByOrigin = orderByOrigin;
|
|
14
|
-
var
|
|
15
|
-
var _fsExtra = _interopRequireDefault(require("fs-extra"));
|
|
10
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
16
11
|
var _path = _interopRequireDefault(require("path"));
|
|
17
12
|
var R = _interopRequireWildcard(require("ramda"));
|
|
18
|
-
var _chalk = _interopRequireDefault(require("chalk"));
|
|
19
13
|
var _glob = _interopRequireDefault(require("glob"));
|
|
20
|
-
var _micromatch = _interopRequireDefault(require("micromatch"));
|
|
21
14
|
var _normalizePath = _interopRequireDefault(require("normalize-path"));
|
|
22
|
-
var _formats =
|
|
23
|
-
var
|
|
15
|
+
var _formats = require("./formats");
|
|
16
|
+
var _getTranslationsForCatalog = require("./catalog/getTranslationsForCatalog");
|
|
17
|
+
var _mergeCatalog = require("./catalog/mergeCatalog");
|
|
18
|
+
var _extractFromFiles = require("./catalog/extractFromFiles");
|
|
24
19
|
var _utils = require("./utils");
|
|
25
20
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
26
21
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
27
22
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
28
|
-
const NAME = "{name}";
|
|
29
|
-
const NAME_REPLACE_RE = /{name}/g;
|
|
30
23
|
const LOCALE = "{locale}";
|
|
31
|
-
const LOCALE_REPLACE_RE = /{locale}/g;
|
|
32
24
|
const LOCALE_SUFFIX_RE = /\{locale\}.*$/;
|
|
33
|
-
const PATHSEP = "/"; // force posix everywhere
|
|
34
|
-
|
|
35
25
|
class Catalog {
|
|
36
26
|
constructor({
|
|
37
27
|
name,
|
|
38
28
|
path,
|
|
39
29
|
include,
|
|
30
|
+
templatePath,
|
|
40
31
|
exclude = []
|
|
41
32
|
}, config) {
|
|
42
|
-
this.name = name;
|
|
43
|
-
this.path = normalizeRelativePath(path);
|
|
44
|
-
this.include = include.map(normalizeRelativePath);
|
|
45
|
-
this.exclude = [this.localeDir, ...exclude.map(normalizeRelativePath)];
|
|
46
33
|
this.config = config;
|
|
47
|
-
this.
|
|
34
|
+
this.name = name;
|
|
35
|
+
this.path = (0, _utils.normalizeRelativePath)(path);
|
|
36
|
+
this.include = include.map(_utils.normalizeRelativePath);
|
|
37
|
+
this.exclude = [this.localeDir, ...exclude.map(_utils.normalizeRelativePath)];
|
|
38
|
+
this.format = (0, _formats.getFormat)(config.format);
|
|
39
|
+
this.templateFile = templatePath || getTemplatePath(this.format, this.path);
|
|
48
40
|
}
|
|
49
41
|
async make(options) {
|
|
50
|
-
const nextCatalog = await this.collect(
|
|
42
|
+
const nextCatalog = await this.collect({
|
|
43
|
+
files: options.files
|
|
44
|
+
});
|
|
51
45
|
if (!nextCatalog) return false;
|
|
52
46
|
const prevCatalogs = this.readAll();
|
|
53
47
|
const catalogs = this.merge(prevCatalogs, nextCatalog, {
|
|
@@ -67,171 +61,64 @@ class Catalog {
|
|
|
67
61
|
} else {
|
|
68
62
|
this.writeAll(sortedCatalogs);
|
|
69
63
|
}
|
|
70
|
-
return
|
|
64
|
+
return sortedCatalogs;
|
|
71
65
|
}
|
|
72
66
|
async makeTemplate(options) {
|
|
73
|
-
const catalog = await this.collect(
|
|
67
|
+
const catalog = await this.collect({
|
|
68
|
+
files: options.files
|
|
69
|
+
});
|
|
74
70
|
if (!catalog) return false;
|
|
75
|
-
const
|
|
76
|
-
this.writeTemplate(
|
|
77
|
-
return
|
|
71
|
+
const sorted = order(options.orderBy)(catalog);
|
|
72
|
+
this.writeTemplate(sorted);
|
|
73
|
+
return sorted;
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
/**
|
|
81
77
|
* Collect messages from source paths. Return a raw message catalog as JSON.
|
|
82
78
|
*/
|
|
83
|
-
async collect(options) {
|
|
84
|
-
|
|
85
|
-
if (
|
|
86
|
-
(0,
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
async collect(options = {}) {
|
|
80
|
+
let paths = this.sourcePaths;
|
|
81
|
+
if (options.files) {
|
|
82
|
+
options.files = options.files.map(p => (0, _normalizePath.default)(p, false));
|
|
83
|
+
const regex = new RegExp(options.files.join("|"), "i");
|
|
84
|
+
paths = paths.filter(path => regex.test(path));
|
|
89
85
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return traverse(filepath);
|
|
114
|
-
}
|
|
115
|
-
if (!filename.endsWith(".json")) return;
|
|
116
|
-
try {
|
|
117
|
-
return JSON.parse(_fsExtra.default.readFileSync(filepath).toString());
|
|
118
|
-
} catch (e) {}
|
|
119
|
-
}).filter(Boolean).reduce((catalog, messages) => R.mergeWithKey(mergeOriginsAndExtractedComments, catalog, messages), {});
|
|
120
|
-
}(tmpDir);
|
|
121
|
-
} catch (e) {
|
|
122
|
-
throw e;
|
|
123
|
-
} finally {
|
|
124
|
-
(0, _utils.removeDirectory)(tmpDir);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
86
|
+
return await (0, _extractFromFiles.extractFromFiles)(paths, this.config);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
*
|
|
91
|
+
* prevCatalogs - map of message catalogs in all available languages with translations
|
|
92
|
+
* nextCatalog - language-agnostic catalog with collected messages
|
|
93
|
+
*
|
|
94
|
+
* Note: if a catalog in prevCatalogs is null it means the language is available, but
|
|
95
|
+
* no previous catalog was generated (usually first run).
|
|
96
|
+
*
|
|
97
|
+
* Orthogonal use-cases
|
|
98
|
+
* --------------------
|
|
99
|
+
*
|
|
100
|
+
* Message IDs:
|
|
101
|
+
* - auto-generated IDs: message is used as a key, `defaults` is not set
|
|
102
|
+
* - custom IDs: message is used as `defaults`, custom ID as a key
|
|
103
|
+
*
|
|
104
|
+
* Source locale (defined by `sourceLocale` in config):
|
|
105
|
+
* - catalog for `sourceLocale`: initially, `translation` is prefilled with `defaults`
|
|
106
|
+
* (for custom IDs) or `key` (for auto-generated IDs)
|
|
107
|
+
* - all other languages: translation is kept empty
|
|
108
|
+
*/
|
|
127
109
|
merge(prevCatalogs, nextCatalog, options) {
|
|
128
|
-
const nextKeys = R.keys(nextCatalog).map(String);
|
|
129
110
|
return R.mapObjIndexed((prevCatalog, locale) => {
|
|
130
|
-
|
|
131
|
-
const newKeys = R.difference(nextKeys, prevKeys);
|
|
132
|
-
const mergeKeys = R.intersection(nextKeys, prevKeys);
|
|
133
|
-
const obsoleteKeys = R.difference(prevKeys, nextKeys);
|
|
134
|
-
|
|
135
|
-
// Initialize new catalog with new keys
|
|
136
|
-
const newMessages = R.mapObjIndexed((message, key) => ({
|
|
137
|
-
translation: this.config.sourceLocale === locale ? message.message || key : "",
|
|
138
|
-
...message
|
|
139
|
-
}), R.pick(newKeys, nextCatalog));
|
|
140
|
-
|
|
141
|
-
// Merge translations from previous catalog
|
|
142
|
-
const mergedMessages = mergeKeys.map(key => {
|
|
143
|
-
const updateFromDefaults = this.config.sourceLocale === locale && (prevCatalog[key].translation === prevCatalog[key].message || options.overwrite);
|
|
144
|
-
const translation = updateFromDefaults ? nextCatalog[key].message || key : prevCatalog[key].translation;
|
|
145
|
-
return {
|
|
146
|
-
[key]: {
|
|
147
|
-
translation,
|
|
148
|
-
...R.omit(["obsolete, translation"], nextCatalog[key])
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
// Mark all remaining translations as obsolete
|
|
154
|
-
// Only if *options.files* is not provided
|
|
155
|
-
const obsoleteMessages = obsoleteKeys.map(key => ({
|
|
156
|
-
[key]: {
|
|
157
|
-
...prevCatalog[key],
|
|
158
|
-
obsolete: options.files ? false : true
|
|
159
|
-
}
|
|
160
|
-
}));
|
|
161
|
-
return R.mergeAll([newMessages, ...mergedMessages, ...obsoleteMessages]);
|
|
111
|
+
return (0, _mergeCatalog.mergeCatalog)(prevCatalog, nextCatalog, this.config.sourceLocale === locale, options);
|
|
162
112
|
}, prevCatalogs);
|
|
163
113
|
}
|
|
164
114
|
getTranslations(locale, options) {
|
|
165
|
-
|
|
166
|
-
const template = this.readTemplate() || {};
|
|
167
|
-
return R.mapObjIndexed((_value, key) => this.getTranslation(catalogs, locale, key, options), {
|
|
168
|
-
...template,
|
|
169
|
-
...catalogs[locale]
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
getTranslation(catalogs, locale, key, {
|
|
173
|
-
fallbackLocales,
|
|
174
|
-
sourceLocale
|
|
175
|
-
}) {
|
|
176
|
-
var _catalog$key;
|
|
177
|
-
const catalog = catalogs[locale] || {};
|
|
178
|
-
const getTranslation = _locale => {
|
|
179
|
-
const configLocales = this.config.locales.join('", "');
|
|
180
|
-
const localeCatalog = catalogs[_locale] || {};
|
|
181
|
-
if (!localeCatalog) {
|
|
182
|
-
console.warn(`
|
|
183
|
-
Catalog "${_locale}" isn't present in locales config parameter
|
|
184
|
-
Add "${_locale}" to your lingui.config.js:
|
|
185
|
-
{
|
|
186
|
-
locales: ["${configLocales}", "${_locale}"]
|
|
187
|
-
}
|
|
188
|
-
`);
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
if (!localeCatalog.hasOwnProperty(key)) {
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
if (catalogs[_locale]) {
|
|
195
|
-
return catalogs[_locale][key].translation;
|
|
196
|
-
}
|
|
197
|
-
return null;
|
|
198
|
-
};
|
|
199
|
-
const getMultipleFallbacks = _locale => {
|
|
200
|
-
const fL = fallbackLocales && fallbackLocales[_locale];
|
|
201
|
-
|
|
202
|
-
// some probably the fallback will be undefined, so just search by locale
|
|
203
|
-
if (!fL) return null;
|
|
204
|
-
if (Array.isArray(fL)) {
|
|
205
|
-
for (const fallbackLocale of fL) {
|
|
206
|
-
if (catalogs[fallbackLocale]) {
|
|
207
|
-
return getTranslation(fallbackLocale);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
} else {
|
|
211
|
-
return getTranslation(fL);
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
return (
|
|
215
|
-
// Get translation in target locale
|
|
216
|
-
getTranslation(locale) ||
|
|
217
|
-
// We search in fallbackLocales as dependent of each locale
|
|
218
|
-
getMultipleFallbacks(locale) ||
|
|
219
|
-
// Get translation in fallbackLocales.default (if any)
|
|
220
|
-
(fallbackLocales === null || fallbackLocales === void 0 ? void 0 : fallbackLocales.default) && getTranslation(fallbackLocales.default) || ( // Get message default
|
|
221
|
-
(_catalog$key = catalog[key]) === null || _catalog$key === void 0 ? void 0 : _catalog$key.defaults) ||
|
|
222
|
-
// If sourceLocale is either target locale of fallback one, use key
|
|
223
|
-
sourceLocale && sourceLocale === locale && key || sourceLocale && (fallbackLocales === null || fallbackLocales === void 0 ? void 0 : fallbackLocales.default) && sourceLocale === fallbackLocales.default && key ||
|
|
224
|
-
// Otherwise no translation is available
|
|
225
|
-
undefined
|
|
226
|
-
);
|
|
115
|
+
return (0, _getTranslationsForCatalog.getTranslationsForCatalog)(this, locale, options);
|
|
227
116
|
}
|
|
228
117
|
write(locale, messages) {
|
|
229
|
-
const filename =
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
_fsExtra.default.mkdirpSync(basedir);
|
|
234
|
-
}
|
|
118
|
+
const filename = (0, _utils.replacePlaceholders)(this.path, {
|
|
119
|
+
locale
|
|
120
|
+
}) + this.format.catalogExtension;
|
|
121
|
+
const created = !_fs.default.existsSync(filename);
|
|
235
122
|
const options = {
|
|
236
123
|
...this.config.formatOptions,
|
|
237
124
|
locale
|
|
@@ -244,10 +131,6 @@ class Catalog {
|
|
|
244
131
|
}
|
|
245
132
|
writeTemplate(messages) {
|
|
246
133
|
const filename = this.templateFile;
|
|
247
|
-
const basedir = _path.default.dirname(filename);
|
|
248
|
-
if (!_fsExtra.default.existsSync(basedir)) {
|
|
249
|
-
_fsExtra.default.mkdirpSync(basedir);
|
|
250
|
-
}
|
|
251
134
|
const options = {
|
|
252
135
|
...this.config.formatOptions,
|
|
253
136
|
locale: undefined
|
|
@@ -263,17 +146,16 @@ class Catalog {
|
|
|
263
146
|
} else {
|
|
264
147
|
ext = "js";
|
|
265
148
|
}
|
|
266
|
-
const filename = `${this.path
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
_fsExtra.default.writeFileSync(filename, compiledCatalog);
|
|
149
|
+
const filename = `${(0, _utils.replacePlaceholders)(this.path, {
|
|
150
|
+
locale
|
|
151
|
+
})}.${ext}`;
|
|
152
|
+
(0, _utils.writeFile)(filename, compiledCatalog);
|
|
272
153
|
return filename;
|
|
273
154
|
}
|
|
274
155
|
read(locale) {
|
|
275
|
-
const filename =
|
|
276
|
-
|
|
156
|
+
const filename = (0, _utils.replacePlaceholders)(this.path, {
|
|
157
|
+
locale
|
|
158
|
+
}) + this.format.catalogExtension;
|
|
277
159
|
return this.format.read(filename);
|
|
278
160
|
}
|
|
279
161
|
readAll() {
|
|
@@ -283,12 +165,11 @@ class Catalog {
|
|
|
283
165
|
}
|
|
284
166
|
readTemplate() {
|
|
285
167
|
const filename = this.templateFile;
|
|
286
|
-
if (!_fsExtra.default.existsSync(filename)) return null;
|
|
287
168
|
return this.format.read(filename);
|
|
288
169
|
}
|
|
289
170
|
get sourcePaths() {
|
|
290
171
|
const includeGlobs = this.include.map(includePath => {
|
|
291
|
-
const isDir =
|
|
172
|
+
const isDir = (0, _utils.isDirectory)(includePath);
|
|
292
173
|
/**
|
|
293
174
|
* glob library results from absolute patterns such as /foo/* are mounted onto the root setting using path.join.
|
|
294
175
|
* On windows, this will by default result in /foo/* matching C:\foo\bar.txt.
|
|
@@ -301,9 +182,6 @@ class Catalog {
|
|
|
301
182
|
mark: true
|
|
302
183
|
});
|
|
303
184
|
}
|
|
304
|
-
get templateFile() {
|
|
305
|
-
return this.path.replace(LOCALE_SUFFIX_RE, "messages.pot");
|
|
306
|
-
}
|
|
307
185
|
get localeDir() {
|
|
308
186
|
const localePatternIndex = this.path.indexOf(LOCALE);
|
|
309
187
|
if (localePatternIndex === -1) {
|
|
@@ -315,150 +193,10 @@ class Catalog {
|
|
|
315
193
|
return this.config.locales;
|
|
316
194
|
}
|
|
317
195
|
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Parse `config.catalogs` and return a list of configured Catalog instances.
|
|
321
|
-
*/
|
|
322
196
|
exports.Catalog = Catalog;
|
|
323
|
-
function
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
catalogsConfig.forEach(catalog => {
|
|
327
|
-
// Validate that `catalogPath` doesn't end with trailing slash
|
|
328
|
-
if (catalog.path.endsWith(PATHSEP)) {
|
|
329
|
-
const extension = (0, _formats.default)(config.format).catalogExtension;
|
|
330
|
-
const correctPath = catalog.path.slice(0, -1);
|
|
331
|
-
const examplePath = correctPath.replace(LOCALE_REPLACE_RE,
|
|
332
|
-
// Show example using one of configured locales (if any)
|
|
333
|
-
(config.locales || [])[0] || "en") + extension;
|
|
334
|
-
throw new Error(
|
|
335
|
-
// prettier-ignore
|
|
336
|
-
`Remove trailing slash from "${catalog.path}". Catalog path isn't a directory,` + ` but translation file without extension. For example, catalog path "${correctPath}"` + ` results in translation file "${examplePath}".`);
|
|
337
|
-
}
|
|
338
|
-
const include = ensureArray(catalog.include).map(normalizeRelativePath);
|
|
339
|
-
const exclude = ensureArray(catalog.exclude).map(normalizeRelativePath);
|
|
340
|
-
|
|
341
|
-
// catalog.path without {name} pattern -> always refers to a single catalog
|
|
342
|
-
if (!catalog.path.includes(NAME)) {
|
|
343
|
-
// Validate that sourcePaths doesn't use {name} pattern either
|
|
344
|
-
const invalidSource = include.find(path => path.includes(NAME));
|
|
345
|
-
if (invalidSource !== undefined) {
|
|
346
|
-
throw new Error(`Catalog with path "${catalog.path}" doesn't have a {name} pattern` + ` in it, but one of source directories uses it: "${invalidSource}".` + ` Either add {name} pattern to "${catalog.path}" or remove it` + ` from all source directories.`);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// catalog name is the last directory of catalog.path.
|
|
350
|
-
// If the last part is {locale}, then catalog doesn't have an explicit name
|
|
351
|
-
const name = function () {
|
|
352
|
-
const _name = catalog.path.split(PATHSEP).slice(-1)[0];
|
|
353
|
-
return _name !== LOCALE ? _name : null;
|
|
354
|
-
}();
|
|
355
|
-
catalogs.push(new Catalog({
|
|
356
|
-
name,
|
|
357
|
-
path: normalizeRelativePath(catalog.path),
|
|
358
|
-
include,
|
|
359
|
-
exclude
|
|
360
|
-
}, config));
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
const patterns = include.map(path => path.replace(NAME_REPLACE_RE, "*"));
|
|
364
|
-
const candidates = _glob.default.sync(patterns.length > 1 ? `{${patterns.join(",")}}` : patterns[0], {
|
|
365
|
-
ignore: exclude,
|
|
366
|
-
mark: true
|
|
367
|
-
});
|
|
368
|
-
candidates.forEach(catalogDir => {
|
|
369
|
-
const name = _path.default.basename(catalogDir);
|
|
370
|
-
catalogs.push(new Catalog({
|
|
371
|
-
name,
|
|
372
|
-
path: normalizeRelativePath(catalog.path.replace(NAME_REPLACE_RE, name)),
|
|
373
|
-
include: include.map(path => path.replace(NAME_REPLACE_RE, name)),
|
|
374
|
-
exclude: exclude.map(path => path.replace(NAME_REPLACE_RE, name))
|
|
375
|
-
}, config));
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
return catalogs;
|
|
379
|
-
}
|
|
380
|
-
function getCatalogForFile(file, catalogs) {
|
|
381
|
-
for (const catalog of catalogs) {
|
|
382
|
-
const catalogFile = `${catalog.path}${catalog.format.catalogExtension}`;
|
|
383
|
-
const catalogGlob = catalogFile.replace(LOCALE_REPLACE_RE, "*");
|
|
384
|
-
const match = _micromatch.default.capture(normalizeRelativePath(_path.default.relative(catalog.config.rootDir, catalogGlob)), normalizeRelativePath(file));
|
|
385
|
-
if (match) {
|
|
386
|
-
return {
|
|
387
|
-
locale: match[0],
|
|
388
|
-
catalog
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
return null;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Create catalog for merged messages.
|
|
397
|
-
*/
|
|
398
|
-
function getCatalogForMerge(config) {
|
|
399
|
-
const catalogConfig = config;
|
|
400
|
-
if (catalogConfig.catalogsMergePath.endsWith(PATHSEP)) {
|
|
401
|
-
const extension = (0, _formats.default)(config.format).catalogExtension;
|
|
402
|
-
const correctPath = catalogConfig.catalogsMergePath.slice(0, -1);
|
|
403
|
-
const examplePath = correctPath.replace(LOCALE_REPLACE_RE,
|
|
404
|
-
// Show example using one of configured locales (if any)
|
|
405
|
-
(config.locales || [])[0] || "en") + extension;
|
|
406
|
-
throw new Error(
|
|
407
|
-
// prettier-ignore
|
|
408
|
-
`Remove trailing slash from "${catalogConfig.catalogsMergePath}". Catalog path isn't a directory,` + ` but translation file without extension. For example, catalog path "${correctPath}"` + ` results in translation file "${examplePath}".`);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// catalog name is the last directory of catalogPath.
|
|
412
|
-
// If the last part is {locale}, then catalog doesn't have an explicit name
|
|
413
|
-
const name = function () {
|
|
414
|
-
const _name = _path.default.basename(normalizeRelativePath(catalogConfig.catalogsMergePath));
|
|
415
|
-
return _name !== LOCALE ? _name : null;
|
|
416
|
-
}();
|
|
417
|
-
const catalog = new Catalog({
|
|
418
|
-
name,
|
|
419
|
-
path: normalizeRelativePath(catalogConfig.catalogsMergePath),
|
|
420
|
-
include: [],
|
|
421
|
-
exclude: []
|
|
422
|
-
}, config);
|
|
423
|
-
return catalog;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Merge origins and extractedComments for messages found in different places. All other attributes
|
|
428
|
-
* should be the same (raise an error if defaults are different).
|
|
429
|
-
*/
|
|
430
|
-
function mergeOriginsAndExtractedComments(msgId, prev, next) {
|
|
431
|
-
if (prev.defaults !== next.defaults) {
|
|
432
|
-
throw new Error(`Encountered different defaults for message ${_chalk.default.yellow(msgId)}` + `\n${_chalk.default.yellow((0, _utils.prettyOrigin)(prev.origin))} ${prev.defaults}` + `\n${_chalk.default.yellow((0, _utils.prettyOrigin)(next.origin))} ${next.defaults}`);
|
|
433
|
-
}
|
|
434
|
-
return {
|
|
435
|
-
...next,
|
|
436
|
-
extractedComments: R.concat(prev.extractedComments, next.extractedComments),
|
|
437
|
-
origin: R.concat(prev.origin, next.origin)
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* Ensure that value is always array. If not, turn it into an array of one element.
|
|
443
|
-
*/
|
|
444
|
-
const ensureArray = value => {
|
|
445
|
-
if (value == null) return [];
|
|
446
|
-
return Array.isArray(value) ? value : [value];
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
/**
|
|
450
|
-
* Remove ./ at the beginning: ./relative => relative
|
|
451
|
-
* relative => relative
|
|
452
|
-
* Preserve directories: ./relative/ => relative/
|
|
453
|
-
* Preserve absolute paths: /absolute/path => /absolute/path
|
|
454
|
-
*/
|
|
455
|
-
function normalizeRelativePath(sourcePath) {
|
|
456
|
-
if (_path.default.isAbsolute(sourcePath)) {
|
|
457
|
-
// absolute path
|
|
458
|
-
return (0, _normalizePath.default)(sourcePath, false);
|
|
459
|
-
}
|
|
460
|
-
const isDir = _fsExtra.default.existsSync(sourcePath) && _fsExtra.default.lstatSync(sourcePath).isDirectory();
|
|
461
|
-
return (0, _normalizePath.default)(_path.default.relative(process.cwd(), sourcePath), false) + (isDir ? "/" : "");
|
|
197
|
+
function getTemplatePath(format, path) {
|
|
198
|
+
const ext = format.templateExtension || format.catalogExtension;
|
|
199
|
+
return path.replace(LOCALE_SUFFIX_RE, "messages" + ext);
|
|
462
200
|
}
|
|
463
201
|
const cleanObsolete = R.filter(message => !message.obsolete);
|
|
464
202
|
exports.cleanObsolete = cleanObsolete;
|
|
@@ -474,11 +212,11 @@ function order(by) {
|
|
|
474
212
|
* https://stackoverflow.com/a/31102605/1535540
|
|
475
213
|
*/
|
|
476
214
|
function orderByMessageId(messages) {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
215
|
+
return Object.keys(messages).sort().reduce((acc, key) => {
|
|
216
|
+
;
|
|
217
|
+
acc[key] = messages[key];
|
|
218
|
+
return acc;
|
|
219
|
+
}, {});
|
|
482
220
|
}
|
|
483
221
|
function orderByOrigin(messages) {
|
|
484
222
|
function getFirstOrigin(messageKey) {
|
|
@@ -489,7 +227,7 @@ function orderByOrigin(messages) {
|
|
|
489
227
|
});
|
|
490
228
|
return sortedOrigins[0];
|
|
491
229
|
}
|
|
492
|
-
return Object.keys(messages).sort(
|
|
230
|
+
return Object.keys(messages).sort((a, b) => {
|
|
493
231
|
const [aFile, aLineNumber] = getFirstOrigin(a);
|
|
494
232
|
const [bFile, bLineNumber] = getFirstOrigin(b);
|
|
495
233
|
if (aFile < bFile) return -1;
|
|
@@ -498,6 +236,7 @@ function orderByOrigin(messages) {
|
|
|
498
236
|
if (aLineNumber > bLineNumber) return 1;
|
|
499
237
|
return 0;
|
|
500
238
|
}).reduce((acc, key) => {
|
|
239
|
+
;
|
|
501
240
|
acc[key] = messages[key];
|
|
502
241
|
return acc;
|
|
503
242
|
}, {});
|
package/build/api/compile.js
CHANGED
|
@@ -21,18 +21,6 @@ function createCompiledCatalog(locale, messages, options) {
|
|
|
21
21
|
} = options;
|
|
22
22
|
const shouldPseudolocalize = locale === pseudoLocale;
|
|
23
23
|
const compiledMessages = Object.keys(messages).reduce((obj, key) => {
|
|
24
|
-
const value = messages[key];
|
|
25
|
-
|
|
26
|
-
// If the current ID's value is a context object, create a nested
|
|
27
|
-
// expression, and assign the current ID to that expression
|
|
28
|
-
if (typeof value === "object") {
|
|
29
|
-
obj[key] = Object.keys(value).reduce((obj, contextKey) => {
|
|
30
|
-
obj[contextKey] = compile(value[contextKey], shouldPseudolocalize);
|
|
31
|
-
return obj;
|
|
32
|
-
}, {});
|
|
33
|
-
return obj;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
24
|
// Don't use `key` as a fallback translation in strict mode.
|
|
37
25
|
const translation = messages[key] || (!strict ? key : "");
|
|
38
26
|
obj[key] = compile(translation, shouldPseudolocalize);
|
|
@@ -6,37 +6,80 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _core = require("@babel/core");
|
|
8
8
|
var _babelPluginExtractMessages = _interopRequireDefault(require("@lingui/babel-plugin-extract-messages"));
|
|
9
|
-
var
|
|
9
|
+
var _sourceMap = require("source-map");
|
|
10
10
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
-
|
|
11
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
12
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
13
|
+
const babelRe = new RegExp("\\.(" + [..._core.DEFAULT_EXTENSIONS, ".ts", ".mts", ".cts", ".tsx"].map(ext => ext.slice(1)).join("|") + ")$", "i");
|
|
14
|
+
const inlineSourceMapsRE = new RegExp(/\/[\/\*][#@]\s+sourceMappingURL=data:application\/json;(?:charset:utf-8;)?base64,/i);
|
|
12
15
|
const extractor = {
|
|
13
16
|
match(filename) {
|
|
14
17
|
return babelRe.test(filename);
|
|
15
18
|
},
|
|
16
|
-
extract(filename,
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
frameworkOptions.presets = ["react-app"];
|
|
19
|
+
async extract(filename, code, onMessageExtracted, ctx) {
|
|
20
|
+
const parserOptions = ctx.linguiConfig.extractorParserOptions;
|
|
21
|
+
const parserPlugins = [
|
|
22
|
+
// https://babeljs.io/docs/en/babel-parser#latest-ecmascript-features
|
|
23
|
+
["decorators", {
|
|
24
|
+
decoratorsBeforeExport: (parserOptions === null || parserOptions === void 0 ? void 0 : parserOptions.decoratorsBeforeExport) || true
|
|
25
|
+
}]];
|
|
26
|
+
if ([/\.ts$/, /\.mts$/, /\.cts$/, /\.tsx$/].some(r => filename.match(r))) {
|
|
27
|
+
parserPlugins.push("typescript");
|
|
28
|
+
} else if (parserOptions !== null && parserOptions !== void 0 && parserOptions.flow) {
|
|
29
|
+
parserPlugins.push("flow");
|
|
28
30
|
}
|
|
29
|
-
(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
31
|
+
if ([/\.jsx$/, /\.tsx$/].some(r => filename.match(r))) {
|
|
32
|
+
parserPlugins.push("jsx");
|
|
33
|
+
}
|
|
34
|
+
let sourceMapsConsumer;
|
|
35
|
+
if (ctx !== null && ctx !== void 0 && ctx.sourceMaps) {
|
|
36
|
+
sourceMapsConsumer = await new _sourceMap.SourceMapConsumer(ctx === null || ctx === void 0 ? void 0 : ctx.sourceMaps);
|
|
37
|
+
} else if (code.search(inlineSourceMapsRE) != -1) {
|
|
38
|
+
const {
|
|
39
|
+
fromSource
|
|
40
|
+
} = await Promise.resolve().then(() => _interopRequireWildcard(require("convert-source-map")));
|
|
41
|
+
sourceMapsConsumer = await new _sourceMap.SourceMapConsumer(fromSource(code).toObject());
|
|
42
|
+
}
|
|
43
|
+
await (0, _core.transformAsync)(code, {
|
|
44
|
+
// don't generate code
|
|
45
|
+
code: false,
|
|
46
|
+
babelrc: false,
|
|
47
|
+
configFile: false,
|
|
48
|
+
filename: filename,
|
|
49
|
+
inputSourceMap: ctx === null || ctx === void 0 ? void 0 : ctx.sourceMaps,
|
|
50
|
+
parserOpts: {
|
|
51
|
+
plugins: parserPlugins
|
|
52
|
+
},
|
|
53
|
+
plugins: [["macros", {
|
|
54
|
+
// macro plugin uses package `resolve` to find a path of macro file
|
|
55
|
+
// this will not follow jest pathMapping and will resolve path from ./build
|
|
56
|
+
// instead of ./src which makes testing & developing hard.
|
|
57
|
+
// here we override resolve and provide correct path for testing
|
|
58
|
+
resolvePath: source => require.resolve(source),
|
|
59
|
+
lingui: {
|
|
60
|
+
extract: true,
|
|
61
|
+
linguiConfig: ctx.linguiConfig
|
|
62
|
+
}
|
|
63
|
+
}], [_babelPluginExtractMessages.default, {
|
|
64
|
+
onMessageExtracted: msg => {
|
|
65
|
+
if (!sourceMapsConsumer) {
|
|
66
|
+
return onMessageExtracted(msg);
|
|
67
|
+
}
|
|
68
|
+
const [_, line, column] = msg.origin;
|
|
69
|
+
const mappedPosition = sourceMapsConsumer.originalPositionFor({
|
|
70
|
+
line,
|
|
71
|
+
column
|
|
72
|
+
});
|
|
73
|
+
return onMessageExtracted({
|
|
74
|
+
...msg,
|
|
75
|
+
origin: [mappedPosition.source, mappedPosition.line, mappedPosition.column]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}]]
|
|
39
79
|
});
|
|
80
|
+
if (sourceMapsConsumer) {
|
|
81
|
+
sourceMapsConsumer.destroy();
|
|
82
|
+
}
|
|
40
83
|
}
|
|
41
84
|
};
|
|
42
85
|
var _default = extractor;
|