@lingui/cli 3.15.0 → 3.16.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.
- package/CHANGELOG.md +482 -0
- package/build/LICENSE +21 -0
- package/build/api/catalog.js +582 -0
- package/build/api/compile.js +89 -0
- package/{api → build/api}/detect.js +23 -9
- package/build/api/extract.js +78 -0
- package/build/api/extractors/babel.js +51 -0
- package/build/api/extractors/index.js +51 -0
- package/build/api/extractors/typescript.js +71 -0
- package/build/api/formats/csv.js +65 -0
- package/{api → build/api}/formats/index.js +8 -5
- package/build/api/formats/lingui.js +67 -0
- package/build/api/formats/minimal.js +63 -0
- package/build/api/formats/po-gettext.js +296 -0
- package/build/api/formats/po.js +122 -0
- package/{api → build/api}/help.js +6 -18
- package/{api → build/api}/index.js +7 -7
- package/build/api/locales.js +45 -0
- package/{api → build/api}/pseudoLocalize.js +13 -13
- package/build/api/stats.js +46 -0
- package/{api → build/api}/utils.js +21 -40
- package/build/lingui-add-locale.js +11 -0
- package/build/lingui-compile.js +192 -0
- package/build/lingui-extract-template.js +64 -0
- package/build/lingui-extract.js +181 -0
- package/{lingui.js → build/lingui.js} +2 -2
- package/{services → build/services}/translationIO.js +78 -94
- package/build/tests.js +78 -0
- package/package.json +18 -12
- package/api/catalog.js +0 -778
- package/api/compile.js +0 -172
- package/api/extract.js +0 -192
- package/api/extractors/babel.js +0 -61
- package/api/extractors/index.js +0 -130
- package/api/extractors/typescript.js +0 -77
- package/api/formats/csv.js +0 -71
- package/api/formats/lingui.js +0 -64
- package/api/formats/minimal.js +0 -63
- package/api/formats/po-gettext.js +0 -331
- package/api/formats/po.js +0 -130
- package/api/locales.js +0 -43
- package/api/stats.js +0 -51
- package/lingui-add-locale.js +0 -11
- package/lingui-compile.js +0 -198
- package/lingui-extract-template.js +0 -123
- package/lingui-extract.js +0 -286
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _dateFns = require("date-fns");
|
|
9
|
+
|
|
10
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
11
|
+
|
|
12
|
+
var _parser = require("@messageformat/parser");
|
|
13
|
+
|
|
14
|
+
var _pluralsCldr = _interopRequireDefault(require("plurals-cldr"));
|
|
15
|
+
|
|
16
|
+
var _pofile = _interopRequireDefault(require("pofile"));
|
|
17
|
+
|
|
18
|
+
var R = _interopRequireWildcard(require("ramda"));
|
|
19
|
+
|
|
20
|
+
var _plurals = _interopRequireDefault(require("node-gettext/lib/plurals"));
|
|
21
|
+
|
|
22
|
+
var _utils = require("../utils");
|
|
23
|
+
|
|
24
|
+
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
|
|
25
|
+
|
|
26
|
+
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (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
|
+
|
|
28
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
29
|
+
|
|
30
|
+
function getCreateHeaders(language = "no") {
|
|
31
|
+
return {
|
|
32
|
+
"POT-Creation-Date": (0, _dateFns.format)(new Date(), "yyyy-MM-dd HH:mmxxxx"),
|
|
33
|
+
"MIME-Version": "1.0",
|
|
34
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
35
|
+
"Content-Transfer-Encoding": "8bit",
|
|
36
|
+
"X-Generator": "@lingui/cli",
|
|
37
|
+
Language: language
|
|
38
|
+
};
|
|
39
|
+
} // Attempts to turn a single tokenized ICU plural case back into a string.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
function stringifyICUCase(icuCase) {
|
|
43
|
+
return icuCase.tokens.map(token => {
|
|
44
|
+
if (token.type === "content") {
|
|
45
|
+
return token.value;
|
|
46
|
+
} else if (token.type === "octothorpe") {
|
|
47
|
+
return "#";
|
|
48
|
+
} else if (token.type === "argument") {
|
|
49
|
+
return "{" + token.arg + "}";
|
|
50
|
+
} else {
|
|
51
|
+
console.warn(`Unexpected token "${token}" while stringifying plural case "${icuCase}". Token will be ignored.`);
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
}).join("");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const ICU_PLURAL_REGEX = /^{.*, plural, .*}$/;
|
|
58
|
+
const ICU_SELECT_REGEX = /^{.*, select(Ordinal)?, .*}$/;
|
|
59
|
+
const LINE_ENDINGS = /\r?\n/g; // Prefix that is used to identitify context information used by this module in PO's "extracted comments".
|
|
60
|
+
|
|
61
|
+
const CTX_PREFIX = "js-lingui:";
|
|
62
|
+
|
|
63
|
+
const serialize = (items, options) => R.compose(R.values, R.mapObjIndexed((message, key) => {
|
|
64
|
+
const item = new _pofile.default.Item();
|
|
65
|
+
item.msgid = key;
|
|
66
|
+
item.comments = message.comments || []; // The extractedComments array may be modified in this method, so create a new array with the message's elements.
|
|
67
|
+
// Destructuring `undefined` is forbidden, so fallback to `[]` if the message has no extracted comments.
|
|
68
|
+
|
|
69
|
+
item.extractedComments = [...(message.extractedComments ?? [])];
|
|
70
|
+
|
|
71
|
+
if (message.context) {
|
|
72
|
+
item.msgctxt = message.context;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (options.origins !== false) {
|
|
76
|
+
if (message.origin && options.lineNumbers === false) {
|
|
77
|
+
item.references = message.origin.map(([path]) => path);
|
|
78
|
+
} else {
|
|
79
|
+
item.references = message.origin ? message.origin.map(_utils.joinOrigin) : [];
|
|
80
|
+
}
|
|
81
|
+
} // @ts-ignore: Figure out how to set this flag
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
item.obsolete = message.obsolete;
|
|
85
|
+
item.flags = message.flags ? R.fromPairs(message.flags.map(flag => [flag, true])) : {}; // Depending on whether custom ids are used by the developer, the (potential plural) "original", untranslated ICU
|
|
86
|
+
// message can be found in `message.message` or in the item's `key` itself.
|
|
87
|
+
|
|
88
|
+
const icuMessage = message.message || key;
|
|
89
|
+
|
|
90
|
+
const _simplifiedMessage = icuMessage.replace(LINE_ENDINGS, " "); // Quick check to see if original message is a plural localization.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if (ICU_PLURAL_REGEX.test(_simplifiedMessage)) {
|
|
94
|
+
try {
|
|
95
|
+
const messageAst = (0, _parser.parse)(icuMessage)[0]; // Check if any of the plural cases contain plurals themselves.
|
|
96
|
+
|
|
97
|
+
if (messageAst.cases.some(icuCase => icuCase.tokens.some(token => token.type === "plural"))) {
|
|
98
|
+
console.warn(`Nested plurals cannot be expressed with gettext plurals. ` + `Message with key "%s" will not be saved correctly.`, key);
|
|
99
|
+
} // Store placeholder that is pluralized upon to allow restoring ICU format later.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
const ctx = new URLSearchParams({
|
|
103
|
+
pluralize_on: messageAst.arg
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (message.message == null) {
|
|
107
|
+
// For messages without developer-set ID, use first case as `msgid` and the last case as `msgid_plural`.
|
|
108
|
+
// This does not necessarily make sense for development languages with more than two numbers, but gettext
|
|
109
|
+
// only supports exactly two plural forms.
|
|
110
|
+
item.msgid = stringifyICUCase(messageAst.cases[0]);
|
|
111
|
+
item.msgid_plural = stringifyICUCase(messageAst.cases[messageAst.cases.length - 1]); // Since the original msgid is overwritten, store ICU message to allow restoring that ID later.
|
|
112
|
+
|
|
113
|
+
ctx.set("icu", key);
|
|
114
|
+
} else {
|
|
115
|
+
// For messages with developer-set ID, append `_plural` to the key to generate `msgid_plural`.
|
|
116
|
+
item.msgid_plural = key + "_plural";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
ctx.sort();
|
|
120
|
+
item.extractedComments.push(CTX_PREFIX + ctx.toString()); // If there is a translated value, parse that instead of the original message to prevent overriding localized
|
|
121
|
+
// content with the original message. If there is no translated value, don't touch msgstr, since marking item as
|
|
122
|
+
// plural (above) already causes `pofile` to automatically generate `msgstr[0]` and `msgstr[1]`.
|
|
123
|
+
|
|
124
|
+
if (message.translation?.length > 0) {
|
|
125
|
+
try {
|
|
126
|
+
const ast = (0, _parser.parse)(message.translation)[0];
|
|
127
|
+
|
|
128
|
+
if (ast.cases == null) {
|
|
129
|
+
console.warn(`Found translation without plural cases for key "${key}". ` + `This likely means that a translated .po file misses multiple msgstr[] entries for the key. ` + `Translation found: "${message.translation}"`);
|
|
130
|
+
item.msgstr = [message.translation];
|
|
131
|
+
} else {
|
|
132
|
+
item.msgstr = ast.cases.map(stringifyICUCase);
|
|
133
|
+
}
|
|
134
|
+
} catch (e) {
|
|
135
|
+
console.error(`Error parsing translation ICU for key "${key}"`, e);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch (e) {
|
|
139
|
+
console.error(`Error parsing message ICU for key "${key}":`, e);
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
if (!options.disableSelectWarning && ICU_SELECT_REGEX.test(_simplifiedMessage)) {
|
|
143
|
+
console.warn(`ICU 'select' and 'selectOrdinal' formats cannot be expressed natively in gettext format. ` + `Item with key "%s" will be included in the catalog as raw ICU message. ` + `To disable this warning, include '{ disableSelectWarning: true }' in the config's 'formatOptions'`, key);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
item.msgstr = [message.translation];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return item;
|
|
150
|
+
}))(items);
|
|
151
|
+
|
|
152
|
+
const getMessageKey = R.prop("msgid");
|
|
153
|
+
const getTranslations = R.prop("msgstr");
|
|
154
|
+
const getExtractedComments = R.prop("extractedComments");
|
|
155
|
+
const getTranslatorComments = R.prop("comments");
|
|
156
|
+
const getMessageContext = R.prop("msgctxt");
|
|
157
|
+
const getOrigins = R.prop("references");
|
|
158
|
+
const getFlags = R.compose(R.map(R.trim), R.keys, R.dissoc("obsolete"), // backward-compatibility, remove in 3.x
|
|
159
|
+
R.prop("flags"));
|
|
160
|
+
const isObsolete = R.either(R.path(["flags", "obsolete"]), R.prop("obsolete"));
|
|
161
|
+
const getTranslationCount = R.compose(R.length, getTranslations);
|
|
162
|
+
const deserialize = R.map(R.applySpec({
|
|
163
|
+
translation: R.compose(R.head, R.defaultTo([]), getTranslations),
|
|
164
|
+
extractedComments: R.compose(R.defaultTo([]), getExtractedComments),
|
|
165
|
+
comments: R.compose(R.defaultTo([]), getTranslatorComments),
|
|
166
|
+
context: R.compose(R.defaultTo(null), getMessageContext),
|
|
167
|
+
obsolete: isObsolete,
|
|
168
|
+
origin: R.compose(R.map(_utils.splitOrigin), R.defaultTo([]), getOrigins),
|
|
169
|
+
flags: getFlags
|
|
170
|
+
}));
|
|
171
|
+
/**
|
|
172
|
+
* Returns ICU case labels in the order that gettext lists localized messages, e.g. 0,1,2 => `["one", "two", "other"]`.
|
|
173
|
+
*
|
|
174
|
+
* Obtaining the ICU case labels for gettext-supported inputs (gettext doesn't support fractions, even though some
|
|
175
|
+
* languages have a separate case for fractional numbers) works by applying the CLDR selector to the example values
|
|
176
|
+
* listed in the node-gettext plurals module.
|
|
177
|
+
*
|
|
178
|
+
* This approach is heavily influenced by
|
|
179
|
+
* https://github.com/LLK/po2icu/blob/9eb97f81f72b2fee02b77f1424702e019647e9b9/lib/po2icu.js#L148.
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
const getPluralCases = lang => {
|
|
183
|
+
// If users uses locale with underscore or slash, es-ES, es_ES, gettextplural is "es" not es-ES.
|
|
184
|
+
const [correctLang] = lang.split(/[-_]/g);
|
|
185
|
+
const gettextPluralsInfo = _plurals.default[correctLang];
|
|
186
|
+
return gettextPluralsInfo?.examples.map(pluralCase => (0, _pluralsCldr.default)(correctLang, pluralCase.sample));
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const convertPluralsToICU = (items, lang) => {
|
|
190
|
+
// .po plurals are numbered 0-N and need to be mapped to ICU plural classes ("one", "few", "many"...). Different
|
|
191
|
+
// languages can have different plural classes (some start with "zero", some with "one"), so read that data from CLDR.
|
|
192
|
+
// `pluralForms` may be `null` if lang is not found. As long as no plural is used, don't report an error.
|
|
193
|
+
let pluralForms = getPluralCases(lang);
|
|
194
|
+
items.forEach(item => {
|
|
195
|
+
const translationCount = getTranslationCount(item);
|
|
196
|
+
const messageKey = getMessageKey(item); // Messages without multiple translations (= plural cases) need no further processing.
|
|
197
|
+
|
|
198
|
+
if (translationCount <= 1 && !item.msgid_plural) {
|
|
199
|
+
return;
|
|
200
|
+
} // msgid_plural must be set, but its actual value is not important.
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
if (!item.msgid_plural) {
|
|
204
|
+
console.warn(`Multiple translations for item with key "%s" but missing 'msgid_plural' in catalog "${lang}". This is not supported and the plural cases will be ignored.`, messageKey);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const contextComment = item.extractedComments.find(comment => comment.startsWith(CTX_PREFIX))?.substr(CTX_PREFIX.length);
|
|
209
|
+
const ctx = new URLSearchParams(contextComment);
|
|
210
|
+
|
|
211
|
+
if (contextComment != null) {
|
|
212
|
+
item.extractedComments = item.extractedComments.filter(comment => !comment.startsWith(CTX_PREFIX));
|
|
213
|
+
} // If an original ICU was stored, use that as `msgid` to match the catalog that was originally exported.
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
const storedICU = ctx.get("icu");
|
|
217
|
+
|
|
218
|
+
if (storedICU != null) {
|
|
219
|
+
item.msgid = storedICU;
|
|
220
|
+
} // If all translations are empty, ignore item.
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
if (item.msgstr.every(str => str.length === 0)) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (pluralForms == null) {
|
|
228
|
+
console.warn(`Multiple translations for item with key "%s" in language "${lang}", but no plural cases were found. ` + `This prohibits the translation of .po plurals into ICU plurals. Pluralization will not work for this key.`, messageKey);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const pluralCount = pluralForms.length;
|
|
233
|
+
|
|
234
|
+
if (translationCount > pluralCount) {
|
|
235
|
+
console.warn(`More translations provided (${translationCount}) for item with key "%s" in language "${lang}" than there are plural cases available (${pluralCount}). ` + `This will result in not all translations getting picked up.`, messageKey);
|
|
236
|
+
} // Map each msgstr to a "<pluralform> {<translated_string>}" entry, joined by one space.
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
const pluralClauses = item.msgstr.map((str, index) => pluralForms[index] + " {" + str + "}").join(" "); // Find out placeholder name from item's message context, defaulting to "count".
|
|
240
|
+
|
|
241
|
+
let pluralizeOn = ctx.get("pluralize_on");
|
|
242
|
+
|
|
243
|
+
if (!pluralizeOn) {
|
|
244
|
+
console.warn(`Unable to determine plural placeholder name for item with key "%s" in language "${lang}" (should be stored in a comment starting with "#. ${CTX_PREFIX}"), assuming "count".`, messageKey);
|
|
245
|
+
pluralizeOn = "count";
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
item.msgstr = ["{" + pluralizeOn + ", plural, " + pluralClauses + "}"];
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const indexItems = R.indexBy(getMessageKey);
|
|
253
|
+
const poGettext = {
|
|
254
|
+
catalogExtension: ".po",
|
|
255
|
+
|
|
256
|
+
write(filename, catalog, options) {
|
|
257
|
+
let po;
|
|
258
|
+
|
|
259
|
+
if (_fs.default.existsSync(filename)) {
|
|
260
|
+
const raw = _fs.default.readFileSync(filename).toString();
|
|
261
|
+
|
|
262
|
+
po = _pofile.default.parse(raw);
|
|
263
|
+
} else {
|
|
264
|
+
po = new _pofile.default();
|
|
265
|
+
po.headers = getCreateHeaders(options.locale);
|
|
266
|
+
|
|
267
|
+
if (options.locale === undefined) {
|
|
268
|
+
delete po.headers.Language;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
po.headerOrder = Object.keys(po.headers);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
po.items = this.serialize(catalog, options);
|
|
275
|
+
(0, _utils.writeFileIfChanged)(filename, po.toString());
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
// Mainly exported for easier testing
|
|
279
|
+
serialize,
|
|
280
|
+
|
|
281
|
+
read(filename) {
|
|
282
|
+
const raw = _fs.default.readFileSync(filename).toString();
|
|
283
|
+
|
|
284
|
+
return this.parse(raw);
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
parse(raw) {
|
|
288
|
+
const po = _pofile.default.parse(raw);
|
|
289
|
+
|
|
290
|
+
convertPluralsToICU(po.items, po.headers.Language);
|
|
291
|
+
return deserialize(indexItems(po.items));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
};
|
|
295
|
+
var _default = poGettext;
|
|
296
|
+
exports.default = _default;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
9
|
+
|
|
10
|
+
var R = _interopRequireWildcard(require("ramda"));
|
|
11
|
+
|
|
12
|
+
var _dateFns = require("date-fns");
|
|
13
|
+
|
|
14
|
+
var _pofile = _interopRequireDefault(require("pofile"));
|
|
15
|
+
|
|
16
|
+
var _utils = require("../utils");
|
|
17
|
+
|
|
18
|
+
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
|
|
19
|
+
|
|
20
|
+
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (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; }
|
|
21
|
+
|
|
22
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
23
|
+
|
|
24
|
+
const getCreateHeaders = (language = "no") => ({
|
|
25
|
+
"POT-Creation-Date": (0, _dateFns.format)(new Date(), "yyyy-MM-dd HH:mmxxxx"),
|
|
26
|
+
"MIME-Version": "1.0",
|
|
27
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
28
|
+
"Content-Transfer-Encoding": "8bit",
|
|
29
|
+
"X-Generator": "@lingui/cli",
|
|
30
|
+
Language: language
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const serialize = (items, options) => R.compose(R.values, R.mapObjIndexed((message, key) => {
|
|
34
|
+
const item = new _pofile.default.Item();
|
|
35
|
+
item.msgid = key;
|
|
36
|
+
item.msgstr = [message.translation];
|
|
37
|
+
item.comments = message.comments || [];
|
|
38
|
+
item.extractedComments = message.extractedComments || [];
|
|
39
|
+
|
|
40
|
+
if (message.context) {
|
|
41
|
+
item.msgctxt = message.context;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (options.origins !== false) {
|
|
45
|
+
if (message.origin && options.lineNumbers === false) {
|
|
46
|
+
item.references = message.origin.map(([path]) => path);
|
|
47
|
+
} else {
|
|
48
|
+
item.references = message.origin ? message.origin.map(_utils.joinOrigin) : [];
|
|
49
|
+
}
|
|
50
|
+
} // @ts-ignore: Figure out how to set this flag
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
item.obsolete = message.obsolete;
|
|
54
|
+
item.flags = message.flags ? R.fromPairs(message.flags.map(flag => [flag, true])) : {};
|
|
55
|
+
return item;
|
|
56
|
+
}))(items);
|
|
57
|
+
|
|
58
|
+
const getMessageKey = R.prop("msgid");
|
|
59
|
+
const getTranslations = R.prop("msgstr");
|
|
60
|
+
const getExtractedComments = R.prop("extractedComments");
|
|
61
|
+
const getTranslatorComments = R.prop("comments");
|
|
62
|
+
const getMessageContext = R.prop("msgctxt");
|
|
63
|
+
const getOrigins = R.prop("references");
|
|
64
|
+
const getFlags = R.compose(R.map(R.trim), R.keys, R.dissoc("obsolete"), // backward-compatibility, remove in 3.x
|
|
65
|
+
R.prop("flags"));
|
|
66
|
+
const isObsolete = R.either(R.path(["flags", "obsolete"]), R.prop("obsolete"));
|
|
67
|
+
const deserialize = R.map(R.applySpec({
|
|
68
|
+
translation: R.compose(R.head, R.defaultTo([]), getTranslations),
|
|
69
|
+
extractedComments: R.compose(R.defaultTo([]), getExtractedComments),
|
|
70
|
+
comments: R.compose(R.defaultTo([]), getTranslatorComments),
|
|
71
|
+
context: R.compose(R.defaultTo(null), getMessageContext),
|
|
72
|
+
obsolete: isObsolete,
|
|
73
|
+
origin: R.compose(R.map(_utils.splitOrigin), R.defaultTo([]), getOrigins),
|
|
74
|
+
flags: getFlags
|
|
75
|
+
}));
|
|
76
|
+
const validateItems = R.forEach(item => {
|
|
77
|
+
if (R.length(getTranslations(item)) > 1) {
|
|
78
|
+
console.warn("Multiple translations for item with key %s is not supported and it will be ignored.", getMessageKey(item));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const indexItems = R.indexBy(getMessageKey);
|
|
82
|
+
const po = {
|
|
83
|
+
catalogExtension: ".po",
|
|
84
|
+
|
|
85
|
+
write(filename, catalog, options) {
|
|
86
|
+
let po;
|
|
87
|
+
|
|
88
|
+
if (_fs.default.existsSync(filename)) {
|
|
89
|
+
const raw = _fs.default.readFileSync(filename).toString();
|
|
90
|
+
|
|
91
|
+
po = _pofile.default.parse(raw);
|
|
92
|
+
} else {
|
|
93
|
+
po = new _pofile.default();
|
|
94
|
+
po.headers = getCreateHeaders(options.locale);
|
|
95
|
+
|
|
96
|
+
if (options.locale === undefined) {
|
|
97
|
+
delete po.headers.Language;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
po.headerOrder = R.keys(po.headers);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
po.items = serialize(catalog, options);
|
|
104
|
+
(0, _utils.writeFileIfChanged)(filename, po.toString());
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
read(filename) {
|
|
108
|
+
const raw = _fs.default.readFileSync(filename).toString();
|
|
109
|
+
|
|
110
|
+
return this.parse(raw);
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
parse(raw) {
|
|
114
|
+
const po = _pofile.default.parse(raw);
|
|
115
|
+
|
|
116
|
+
validateItems(po.items);
|
|
117
|
+
return deserialize(indexItems(po.items));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
};
|
|
121
|
+
var _default = po;
|
|
122
|
+
exports.default = _default;
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
|
|
5
3
|
Object.defineProperty(exports, "__esModule", {
|
|
6
4
|
value: true
|
|
7
5
|
});
|
|
8
6
|
exports.helpRun = helpRun;
|
|
9
7
|
|
|
10
|
-
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
11
|
-
|
|
12
8
|
var _path = require("path");
|
|
13
9
|
|
|
14
10
|
/**
|
|
@@ -30,30 +26,22 @@ var _path = require("path");
|
|
|
30
26
|
* (use "npm run compile" to compile catalogs for production)
|
|
31
27
|
*/
|
|
32
28
|
function helpRun(command) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
var findRootPkgJson;
|
|
29
|
+
let findRootPkgJson;
|
|
36
30
|
|
|
37
31
|
try {
|
|
38
32
|
findRootPkgJson = require((0, _path.resolve)((0, _path.join)(process.cwd(), "package.json")));
|
|
39
33
|
} catch (error) {}
|
|
40
34
|
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
|
|
44
|
-
_ = _ref2[0],
|
|
45
|
-
value = _ref2[1];
|
|
46
|
-
|
|
47
|
-
return value.includes("lingui ".concat(command));
|
|
48
|
-
});
|
|
35
|
+
if (findRootPkgJson?.scripts) {
|
|
36
|
+
const res = Object.entries(findRootPkgJson.scripts).find(([_, value]) => value.includes(`lingui ${command}`));
|
|
49
37
|
|
|
50
38
|
if (res) {
|
|
51
39
|
command = res[0];
|
|
52
40
|
}
|
|
53
41
|
}
|
|
54
42
|
|
|
55
|
-
return
|
|
43
|
+
return `${runCommand} ${command}`;
|
|
56
44
|
}
|
|
57
45
|
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
const isYarn = process.env.npm_config_user_agent && process.env.npm_config_user_agent.includes("yarn");
|
|
47
|
+
const runCommand = isYarn ? "yarn" : "npm run";
|
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
|
|
5
3
|
Object.defineProperty(exports, "__esModule", {
|
|
6
4
|
value: true
|
|
7
5
|
});
|
|
8
6
|
Object.defineProperty(exports, "getFormat", {
|
|
9
7
|
enumerable: true,
|
|
10
|
-
get: function
|
|
8
|
+
get: function () {
|
|
11
9
|
return _formats.default;
|
|
12
10
|
}
|
|
13
11
|
});
|
|
14
12
|
Object.defineProperty(exports, "getCatalogs", {
|
|
15
13
|
enumerable: true,
|
|
16
|
-
get: function
|
|
14
|
+
get: function () {
|
|
17
15
|
return _catalog.getCatalogs;
|
|
18
16
|
}
|
|
19
17
|
});
|
|
20
18
|
Object.defineProperty(exports, "getCatalogForFile", {
|
|
21
19
|
enumerable: true,
|
|
22
|
-
get: function
|
|
20
|
+
get: function () {
|
|
23
21
|
return _catalog.getCatalogForFile;
|
|
24
22
|
}
|
|
25
23
|
});
|
|
26
24
|
Object.defineProperty(exports, "createCompiledCatalog", {
|
|
27
25
|
enumerable: true,
|
|
28
|
-
get: function
|
|
26
|
+
get: function () {
|
|
29
27
|
return _compile.createCompiledCatalog;
|
|
30
28
|
}
|
|
31
29
|
});
|
|
@@ -34,4 +32,6 @@ var _formats = _interopRequireDefault(require("./formats"));
|
|
|
34
32
|
|
|
35
33
|
var _catalog = require("./catalog");
|
|
36
34
|
|
|
37
|
-
var _compile = require("./compile");
|
|
35
|
+
var _compile = require("./compile");
|
|
36
|
+
|
|
37
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isValid = isValid;
|
|
7
|
+
exports.parse = parse;
|
|
8
|
+
|
|
9
|
+
var plurals = _interopRequireWildcard(require("make-plural/plurals"));
|
|
10
|
+
|
|
11
|
+
var _bcp = _interopRequireDefault(require("bcp-47"));
|
|
12
|
+
|
|
13
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
|
+
|
|
15
|
+
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
|
|
16
|
+
|
|
17
|
+
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (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; }
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check that locale is valid according to BCP47 and we have plurals for it
|
|
21
|
+
* @param locale: string - Locale in BCP47 format
|
|
22
|
+
* @return {boolean}
|
|
23
|
+
*/
|
|
24
|
+
function isValid(locale) {
|
|
25
|
+
const localeData = parse(locale);
|
|
26
|
+
return localeData !== null && localeData !== undefined && localeData.language in plurals;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parse locale in BCP47 format and
|
|
30
|
+
* @param locale - Locale in BCP47 format
|
|
31
|
+
* @return {LocaleInfo}
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
function parse(locale) {
|
|
36
|
+
if (typeof locale !== "string") return null;
|
|
37
|
+
|
|
38
|
+
const schema = _bcp.default.parse(locale.replace("_", "-"));
|
|
39
|
+
|
|
40
|
+
if (!schema.language) return null;
|
|
41
|
+
return {
|
|
42
|
+
locale: _bcp.default.stringify(schema),
|
|
43
|
+
language: schema.language
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
|
|
5
3
|
Object.defineProperty(exports, "__esModule", {
|
|
6
4
|
value: true
|
|
7
5
|
});
|
|
@@ -11,7 +9,9 @@ var _ramda = _interopRequireDefault(require("ramda"));
|
|
|
11
9
|
|
|
12
10
|
var _pseudolocale = _interopRequireDefault(require("pseudolocale"));
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
+
|
|
14
|
+
const delimiter = "%&&&%";
|
|
15
15
|
_pseudolocale.default.option.delimiter = delimiter; // We do not want prepending and appending because of Plurals structure
|
|
16
16
|
|
|
17
17
|
_pseudolocale.default.option.prepend = "";
|
|
@@ -22,7 +22,7 @@ _pseudolocale.default.option.append = "";
|
|
|
22
22
|
* Example: https://regex101.com/r/bDHD9z/3
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
const HTMLRegex = /<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/g;
|
|
26
26
|
/**
|
|
27
27
|
* Regex should match js-lingui Plurals, Select and SelectOrdinal components
|
|
28
28
|
* Example:
|
|
@@ -31,33 +31,33 @@ var HTMLRegex = /<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?
|
|
|
31
31
|
* Select https://regex101.com/r/9JnqB9/1
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
const MacroRegex = /({\w*,\s*(plural|selectordinal|select),(.|\n)*?{)|(}\s*\w*\s*{)/gi;
|
|
35
35
|
/**
|
|
36
36
|
* Regex should match js-lingui variables
|
|
37
37
|
* Example: https://regex101.com/r/dw1QHb/2
|
|
38
38
|
*/
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
const VariableRegex = /({\s*[a-zA-Z_$][a-zA-Z_$0-9]*\s*})/g;
|
|
41
41
|
|
|
42
42
|
function addDelimitersHTMLTags(message) {
|
|
43
|
-
return message.replace(HTMLRegex,
|
|
44
|
-
return
|
|
43
|
+
return message.replace(HTMLRegex, matchedString => {
|
|
44
|
+
return `${delimiter}${matchedString}${delimiter}`;
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function addDelimitersMacro(message) {
|
|
49
|
-
return message.replace(MacroRegex,
|
|
50
|
-
return
|
|
49
|
+
return message.replace(MacroRegex, matchedString => {
|
|
50
|
+
return `${delimiter}${matchedString}${delimiter}`;
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function addDelimitersVariables(message) {
|
|
55
|
-
return message.replace(VariableRegex,
|
|
56
|
-
return
|
|
55
|
+
return message.replace(VariableRegex, matchedString => {
|
|
56
|
+
return `${delimiter}${matchedString}${delimiter}`;
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
const addDelimiters = _ramda.default.compose(addDelimitersVariables, addDelimitersMacro, addDelimitersHTMLTags);
|
|
61
61
|
|
|
62
62
|
function removeDelimiters(message) {
|
|
63
63
|
return message.replace(new RegExp(delimiter, "g"), "");
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getStats = getStats;
|
|
7
|
+
exports.printStats = printStats;
|
|
8
|
+
|
|
9
|
+
var _cliTable = _interopRequireDefault(require("cli-table"));
|
|
10
|
+
|
|
11
|
+
var _chalk = _interopRequireDefault(require("chalk"));
|
|
12
|
+
|
|
13
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
|
+
|
|
15
|
+
function getStats(catalog) {
|
|
16
|
+
return [Object.keys(catalog).length, Object.keys(catalog).filter(key => !catalog[key].translation).length];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function printStats(config, catalogs) {
|
|
20
|
+
const table = new _cliTable.default({
|
|
21
|
+
head: ["Language", "Total count", "Missing"],
|
|
22
|
+
colAligns: ["left", "middle", "middle"],
|
|
23
|
+
style: {
|
|
24
|
+
head: ["green"],
|
|
25
|
+
border: [],
|
|
26
|
+
compact: true
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
Object.keys(catalogs).forEach(locale => {
|
|
30
|
+
const catalog = catalogs[locale]; // catalog is null if no catalog exists on disk and the locale
|
|
31
|
+
// was not extracted due to a `--locale` filter
|
|
32
|
+
|
|
33
|
+
const [all, translated] = catalog ? getStats(catalog) : ["-", "-"];
|
|
34
|
+
|
|
35
|
+
if (config.sourceLocale === locale) {
|
|
36
|
+
table.push({
|
|
37
|
+
[`${_chalk.default.bold(locale)} (source)`]: [all, "-"]
|
|
38
|
+
});
|
|
39
|
+
} else {
|
|
40
|
+
table.push({
|
|
41
|
+
[locale]: [all, translated]
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return table;
|
|
46
|
+
}
|