@lingui/cli 3.17.2 → 4.0.0-next.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/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 +6 -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 +33 -41
- 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 -19
- package/CHANGELOG.md +0 -530
- package/build/api/detect.js +0 -52
- package/build/api/extract.js +0 -59
- package/build/lingui-add-locale.js +0 -8
|
@@ -4,11 +4,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = extract;
|
|
7
|
-
var
|
|
7
|
+
var _promises = _interopRequireDefault(require("fs/promises"));
|
|
8
8
|
var _babel = _interopRequireDefault(require("./babel"));
|
|
9
9
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
10
|
const DEFAULT_EXTRACTORS = [_babel.default];
|
|
11
|
-
async function extract(filename,
|
|
11
|
+
async function extract(filename, onMessageExtracted, linguiConfig, options) {
|
|
12
12
|
const extractorsToExtract = options.extractors ?? DEFAULT_EXTRACTORS;
|
|
13
13
|
for (let e of extractorsToExtract) {
|
|
14
14
|
let ext = e;
|
|
@@ -20,18 +20,13 @@ async function extract(filename, targetPath, options) {
|
|
|
20
20
|
ext = ext.default;
|
|
21
21
|
}
|
|
22
22
|
if (!ext.match(filename)) continue;
|
|
23
|
-
let spinner;
|
|
24
|
-
if (options.verbose) spinner = (0, _ora.default)().start(filename);
|
|
25
23
|
try {
|
|
26
|
-
await
|
|
27
|
-
|
|
24
|
+
const file = await _promises.default.readFile(filename);
|
|
25
|
+
await ext.extract(filename, file.toString(), onMessageExtracted, linguiConfig);
|
|
28
26
|
return true;
|
|
29
27
|
} catch (e) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} else {
|
|
33
|
-
console.error(`Cannot process file ${e.message}`);
|
|
34
|
-
}
|
|
28
|
+
console.error(`Cannot process file ${filename} ${e.message}`);
|
|
29
|
+
console.error(e.stack);
|
|
35
30
|
return false;
|
|
36
31
|
}
|
|
37
32
|
}
|
|
@@ -4,55 +4,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var _fs = _interopRequireDefault(require("fs"));
|
|
8
|
-
var _core = require("@babel/core");
|
|
9
|
-
var _babelPluginExtractMessages = _interopRequireDefault(require("@lingui/babel-plugin-extract-messages"));
|
|
10
|
-
var _detect = require("../detect");
|
|
11
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
|
-
const typescriptRe = /(^.?|\.[^d]|[^.]d|[^.][^d])\.tsx?$/i;
|
|
13
7
|
const extractor = {
|
|
14
8
|
match(filename) {
|
|
15
|
-
|
|
9
|
+
throw new Error("Typescript extractor was removed. " + "Lingui CLI can parse typescript out of the box. " + "Please remove it from your lingui.config.js");
|
|
16
10
|
},
|
|
17
|
-
extract(
|
|
18
|
-
const ts = require("typescript");
|
|
19
|
-
const content = _fs.default.readFileSync(filename, "utf8");
|
|
20
|
-
const isTsx = filename.endsWith(".tsx");
|
|
21
|
-
// pass jsx to babel untouched
|
|
22
|
-
const jsx = isTsx ? ts.JsxEmit.Preserve : ts.JsxEmit.None;
|
|
23
|
-
const stripped = ts.transpileModule(content, {
|
|
24
|
-
compilerOptions: {
|
|
25
|
-
filename,
|
|
26
|
-
jsx,
|
|
27
|
-
module: ts.ModuleKind.ESNext,
|
|
28
|
-
target: ts.ScriptTarget.ES2016,
|
|
29
|
-
// use ES2015 or ES2016 to preserve tagged template literal
|
|
30
|
-
allowSyntheticDefaultImports: true,
|
|
31
|
-
moduleResolution: ts.ModuleResolutionKind.NodeJs
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
const frameworkOptions = {};
|
|
35
|
-
if (options.projectType === _detect.projectType.CRA) {
|
|
36
|
-
frameworkOptions.presets = ["react-app"];
|
|
37
|
-
}
|
|
38
|
-
const {
|
|
39
|
-
babelOptions = {},
|
|
40
|
-
configPath
|
|
41
|
-
} = options;
|
|
42
|
-
const plugins = ["macros", [_babelPluginExtractMessages.default, {
|
|
43
|
-
localeDir,
|
|
44
|
-
configPath
|
|
45
|
-
}], ...(babelOptions.plugins || [])];
|
|
46
|
-
if (isTsx) {
|
|
47
|
-
plugins.unshift(require.resolve("@babel/plugin-syntax-jsx"));
|
|
48
|
-
}
|
|
49
|
-
(0, _core.transform)(stripped.outputText, {
|
|
50
|
-
...babelOptions,
|
|
51
|
-
...frameworkOptions,
|
|
52
|
-
filename,
|
|
53
|
-
plugins
|
|
54
|
-
});
|
|
55
|
-
}
|
|
11
|
+
extract() {}
|
|
56
12
|
};
|
|
57
13
|
var _default = extractor;
|
|
58
14
|
exports.default = _default;
|
package/build/api/formats/csv.js
CHANGED
|
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var _fs = _interopRequireDefault(require("fs"));
|
|
8
7
|
var _papaparse = _interopRequireDefault(require("papaparse"));
|
|
9
8
|
var _utils = require("../utils");
|
|
10
9
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -35,7 +34,10 @@ const csv = {
|
|
|
35
34
|
(0, _utils.writeFileIfChanged)(filename, messages);
|
|
36
35
|
},
|
|
37
36
|
read(filename) {
|
|
38
|
-
const raw =
|
|
37
|
+
const raw = (0, _utils.readFile)(filename);
|
|
38
|
+
if (!raw) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
39
41
|
try {
|
|
40
42
|
return deserialize(raw);
|
|
41
43
|
} catch (e) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.getFormat = getFormat;
|
|
7
7
|
var _csv = _interopRequireDefault(require("./csv"));
|
|
8
8
|
var _lingui = _interopRequireDefault(require("./lingui"));
|
|
9
9
|
var _minimal = _interopRequireDefault(require("./minimal"));
|
|
@@ -4,12 +4,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var _fs = _interopRequireDefault(require("fs"));
|
|
8
7
|
var R = _interopRequireWildcard(require("ramda"));
|
|
9
8
|
var _utils = require("../utils");
|
|
10
9
|
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); }
|
|
11
10
|
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; }
|
|
12
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
11
|
const removeOrigins = R.map(({
|
|
14
12
|
origin,
|
|
15
13
|
...message
|
|
@@ -36,7 +34,10 @@ const lingui = {
|
|
|
36
34
|
(0, _utils.writeFileIfChanged)(filename, JSON.stringify(outputCatalog, null, 2));
|
|
37
35
|
},
|
|
38
36
|
read(filename) {
|
|
39
|
-
const raw =
|
|
37
|
+
const raw = (0, _utils.readFile)(filename);
|
|
38
|
+
if (!raw) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
40
41
|
try {
|
|
41
42
|
return JSON.parse(raw);
|
|
42
43
|
} catch (e) {
|
|
@@ -4,11 +4,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var _fs = _interopRequireDefault(require("fs"));
|
|
8
7
|
var R = _interopRequireWildcard(require("ramda"));
|
|
8
|
+
var _utils = require("../utils");
|
|
9
9
|
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); }
|
|
10
10
|
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; }
|
|
11
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
11
|
const serialize = R.map(message => message.translation || "");
|
|
13
12
|
const deserialize = R.map(translation => ({
|
|
14
13
|
translation,
|
|
@@ -19,22 +18,17 @@ const deserialize = R.map(translation => ({
|
|
|
19
18
|
const minimal = {
|
|
20
19
|
catalogExtension: ".json",
|
|
21
20
|
write(filename, catalog) {
|
|
22
|
-
var _file;
|
|
23
21
|
const messages = serialize(catalog);
|
|
24
|
-
let file =
|
|
25
|
-
|
|
26
|
-
file = _fs.default.readFileSync(filename, "utf8");
|
|
27
|
-
} catch (error) {
|
|
28
|
-
if (error.code !== "ENOENT") {
|
|
29
|
-
throw error;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
const shouldUseTrailingNewline = file === null || ((_file = file) === null || _file === void 0 ? void 0 : _file.endsWith("\n"));
|
|
22
|
+
let file = (0, _utils.readFile)(filename);
|
|
23
|
+
const shouldUseTrailingNewline = file === null || (file === null || file === void 0 ? void 0 : file.endsWith("\n"));
|
|
33
24
|
const trailingNewLine = shouldUseTrailingNewline ? "\n" : "";
|
|
34
|
-
|
|
25
|
+
(0, _utils.writeFile)(filename, `${JSON.stringify(messages, null, 2)}${trailingNewLine}`);
|
|
35
26
|
},
|
|
36
27
|
read(filename) {
|
|
37
|
-
const raw =
|
|
28
|
+
const raw = (0, _utils.readFile)(filename);
|
|
29
|
+
if (!raw) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
38
32
|
try {
|
|
39
33
|
const rawCatalog = JSON.parse(raw);
|
|
40
34
|
return deserialize(rawCatalog);
|
|
@@ -3,18 +3,17 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = void 0;
|
|
6
|
+
exports.serialize = exports.default = void 0;
|
|
7
7
|
var _dateFns = require("date-fns");
|
|
8
|
-
var _fs = _interopRequireDefault(require("fs"));
|
|
9
8
|
var _parser = require("@messageformat/parser");
|
|
10
9
|
var _pluralsCldr = _interopRequireDefault(require("plurals-cldr"));
|
|
11
10
|
var _pofile = _interopRequireDefault(require("pofile"));
|
|
12
|
-
var R = _interopRequireWildcard(require("ramda"));
|
|
13
11
|
var _plurals = _interopRequireDefault(require("node-gettext/lib/plurals"));
|
|
14
12
|
var _utils = require("../utils");
|
|
15
|
-
|
|
16
|
-
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
|
+
var _po = require("./po");
|
|
17
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
|
|
18
17
|
function getCreateHeaders(language = "no") {
|
|
19
18
|
return {
|
|
20
19
|
"POT-Creation-Date": (0, _dateFns.format)(new Date(), "yyyy-MM-dd HH:mmxxxx"),
|
|
@@ -47,32 +46,13 @@ const LINE_ENDINGS = /\r?\n/g;
|
|
|
47
46
|
|
|
48
47
|
// Prefix that is used to identitify context information used by this module in PO's "extracted comments".
|
|
49
48
|
const CTX_PREFIX = "js-lingui:";
|
|
50
|
-
|
|
51
|
-
const item = new _pofile.default.Item();
|
|
52
|
-
item.msgid = key;
|
|
53
|
-
item.comments = message.comments || [];
|
|
54
|
-
|
|
55
|
-
// The extractedComments array may be modified in this method, so create a new array with the message's elements.
|
|
56
|
-
// Destructuring `undefined` is forbidden, so fallback to `[]` if the message has no extracted comments.
|
|
57
|
-
item.extractedComments = [...(message.extractedComments ?? [])];
|
|
58
|
-
if (message.context) {
|
|
59
|
-
item.msgctxt = message.context;
|
|
60
|
-
}
|
|
61
|
-
if (options.origins !== false) {
|
|
62
|
-
if (message.origin && options.lineNumbers === false) {
|
|
63
|
-
item.references = message.origin.map(([path]) => path);
|
|
64
|
-
} else {
|
|
65
|
-
item.references = message.origin ? message.origin.map(_utils.joinOrigin) : [];
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// @ts-ignore: Figure out how to set this flag
|
|
70
|
-
item.obsolete = message.obsolete;
|
|
71
|
-
item.flags = message.flags ? R.fromPairs(message.flags.map(flag => [flag, true])) : {};
|
|
72
|
-
|
|
49
|
+
function serializePlurals(item, message, id, isGeneratedId, options) {
|
|
73
50
|
// Depending on whether custom ids are used by the developer, the (potential plural) "original", untranslated ICU
|
|
74
51
|
// message can be found in `message.message` or in the item's `key` itself.
|
|
75
|
-
const icuMessage = message.message
|
|
52
|
+
const icuMessage = message.message;
|
|
53
|
+
if (!icuMessage) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
76
56
|
const _simplifiedMessage = icuMessage.replace(LINE_ENDINGS, " ");
|
|
77
57
|
|
|
78
58
|
// Quick check to see if original message is a plural localization.
|
|
@@ -83,14 +63,14 @@ const serialize = (items, options) => R.compose(R.values, R.mapObjIndexed((messa
|
|
|
83
63
|
|
|
84
64
|
// Check if any of the plural cases contain plurals themselves.
|
|
85
65
|
if (messageAst.cases.some(icuCase => icuCase.tokens.some(token => token.type === "plural"))) {
|
|
86
|
-
console.warn(`Nested plurals cannot be expressed with gettext plurals. ` + `Message with key "%s" will not be saved correctly.`,
|
|
66
|
+
console.warn(`Nested plurals cannot be expressed with gettext plurals. ` + `Message with key "%s" will not be saved correctly.`, id);
|
|
87
67
|
}
|
|
88
68
|
|
|
89
69
|
// Store placeholder that is pluralized upon to allow restoring ICU format later.
|
|
90
70
|
const ctx = new URLSearchParams({
|
|
91
71
|
pluralize_on: messageAst.arg
|
|
92
72
|
});
|
|
93
|
-
if (
|
|
73
|
+
if (isGeneratedId) {
|
|
94
74
|
// For messages without developer-set ID, use first case as `msgid` and the last case as `msgid_plural`.
|
|
95
75
|
// This does not necessarily make sense for development languages with more than two numbers, but gettext
|
|
96
76
|
// only supports exactly two plural forms.
|
|
@@ -98,10 +78,10 @@ const serialize = (items, options) => R.compose(R.values, R.mapObjIndexed((messa
|
|
|
98
78
|
item.msgid_plural = stringifyICUCase(messageAst.cases[messageAst.cases.length - 1]);
|
|
99
79
|
|
|
100
80
|
// Since the original msgid is overwritten, store ICU message to allow restoring that ID later.
|
|
101
|
-
ctx.set("icu",
|
|
81
|
+
ctx.set("icu", icuMessage);
|
|
102
82
|
} else {
|
|
103
83
|
// For messages with developer-set ID, append `_plural` to the key to generate `msgid_plural`.
|
|
104
|
-
item.msgid_plural =
|
|
84
|
+
item.msgid_plural = id + "_plural";
|
|
105
85
|
}
|
|
106
86
|
ctx.sort();
|
|
107
87
|
item.extractedComments.push(CTX_PREFIX + ctx.toString());
|
|
@@ -110,49 +90,28 @@ const serialize = (items, options) => R.compose(R.values, R.mapObjIndexed((messa
|
|
|
110
90
|
// content with the original message. If there is no translated value, don't touch msgstr, since marking item as
|
|
111
91
|
// plural (above) already causes `pofile` to automatically generate `msgstr[0]` and `msgstr[1]`.
|
|
112
92
|
if (((_message$translation = message.translation) === null || _message$translation === void 0 ? void 0 : _message$translation.length) > 0) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
item.msgstr = ast.cases.map(stringifyICUCase);
|
|
120
|
-
}
|
|
121
|
-
} catch (e) {
|
|
122
|
-
console.error(`Error parsing translation ICU for key "${key}"`, e);
|
|
93
|
+
const ast = (0, _parser.parse)(message.translation)[0];
|
|
94
|
+
if (ast.cases == null) {
|
|
95
|
+
console.warn(`Found translation without plural cases for key "${id}". ` + `This likely means that a translated .po file misses multiple msgstr[] entries for the key. ` + `Translation found: "${message.translation}"`);
|
|
96
|
+
item.msgstr = [message.translation];
|
|
97
|
+
} else {
|
|
98
|
+
item.msgstr = ast.cases.map(stringifyICUCase);
|
|
123
99
|
}
|
|
124
100
|
}
|
|
125
101
|
} catch (e) {
|
|
126
|
-
console.error(`Error parsing message ICU for key "${
|
|
102
|
+
console.error(`Error parsing message ICU for key "${id}":`, e);
|
|
127
103
|
}
|
|
128
104
|
} else {
|
|
129
105
|
if (!options.disableSelectWarning && ICU_SELECT_REGEX.test(_simplifiedMessage)) {
|
|
130
|
-
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'`,
|
|
106
|
+
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'`, id);
|
|
131
107
|
}
|
|
132
108
|
item.msgstr = [message.translation];
|
|
133
109
|
}
|
|
134
110
|
return item;
|
|
135
|
-
}
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const getTranslatorComments = R.prop("comments");
|
|
140
|
-
const getMessageContext = R.prop("msgctxt");
|
|
141
|
-
const getOrigins = R.prop("references");
|
|
142
|
-
const getFlags = R.compose(R.map(R.trim), R.keys, R.dissoc("obsolete"),
|
|
143
|
-
// backward-compatibility, remove in 3.x
|
|
144
|
-
R.prop("flags"));
|
|
145
|
-
const isObsolete = R.either(R.path(["flags", "obsolete"]), R.prop("obsolete"));
|
|
146
|
-
const getTranslationCount = R.compose(R.length, getTranslations);
|
|
147
|
-
const deserialize = R.map(R.applySpec({
|
|
148
|
-
translation: R.compose(R.head, R.defaultTo([]), getTranslations),
|
|
149
|
-
extractedComments: R.compose(R.defaultTo([]), getExtractedComments),
|
|
150
|
-
comments: R.compose(R.defaultTo([]), getTranslatorComments),
|
|
151
|
-
context: R.compose(R.defaultTo(null), getMessageContext),
|
|
152
|
-
obsolete: isObsolete,
|
|
153
|
-
origin: R.compose(R.map(_utils.splitOrigin), R.defaultTo([]), getOrigins),
|
|
154
|
-
flags: getFlags
|
|
155
|
-
}));
|
|
111
|
+
}
|
|
112
|
+
const serialize = (catalog, options) => {
|
|
113
|
+
return (0, _po.serialize)(catalog, options, (item, message, id, isGeneratedId) => serializePlurals(item, message, id, isGeneratedId, options));
|
|
114
|
+
};
|
|
156
115
|
|
|
157
116
|
/**
|
|
158
117
|
* Returns ICU case labels in the order that gettext lists localized messages, e.g. 0,1,2 => `["one", "two", "other"]`.
|
|
@@ -164,76 +123,71 @@ const deserialize = R.map(R.applySpec({
|
|
|
164
123
|
* This approach is heavily influenced by
|
|
165
124
|
* https://github.com/LLK/po2icu/blob/9eb97f81f72b2fee02b77f1424702e019647e9b9/lib/po2icu.js#L148.
|
|
166
125
|
*/
|
|
126
|
+
exports.serialize = serialize;
|
|
167
127
|
const getPluralCases = lang => {
|
|
168
128
|
// If users uses locale with underscore or slash, es-ES, es_ES, gettextplural is "es" not es-ES.
|
|
169
129
|
const [correctLang] = lang.split(/[-_]/g);
|
|
170
130
|
const gettextPluralsInfo = _plurals.default[correctLang];
|
|
171
131
|
return gettextPluralsInfo === null || gettextPluralsInfo === void 0 ? void 0 : gettextPluralsInfo.examples.map(pluralCase => (0, _pluralsCldr.default)(correctLang, pluralCase.sample));
|
|
172
132
|
};
|
|
173
|
-
const convertPluralsToICU = (
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
// Messages without multiple translations (= plural cases) need no further processing.
|
|
184
|
-
if (translationCount <= 1 && !item.msgid_plural) {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
133
|
+
const convertPluralsToICU = (item, pluralForms, lang) => {
|
|
134
|
+
var _item$extractedCommen;
|
|
135
|
+
const translationCount = item.msgstr.length;
|
|
136
|
+
const messageKey = item.msgid;
|
|
137
|
+
|
|
138
|
+
// Messages without multiple translations (= plural cases) need no further processing.
|
|
139
|
+
if (translationCount <= 1 && !item.msgid_plural) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
187
142
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
143
|
+
// msgid_plural must be set, but its actual value is not important.
|
|
144
|
+
if (!item.msgid_plural) {
|
|
145
|
+
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);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const contextComment = (_item$extractedCommen = item.extractedComments.find(comment => comment.startsWith(CTX_PREFIX))) === null || _item$extractedCommen === void 0 ? void 0 : _item$extractedCommen.substr(CTX_PREFIX.length);
|
|
149
|
+
const ctx = new URLSearchParams(contextComment);
|
|
150
|
+
if (contextComment != null) {
|
|
151
|
+
item.extractedComments = item.extractedComments.filter(comment => !comment.startsWith(CTX_PREFIX));
|
|
152
|
+
}
|
|
198
153
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
154
|
+
// If an original ICU was stored, use that as `msgid` to match the catalog that was originally exported.
|
|
155
|
+
const storedICU = ctx.get("icu");
|
|
156
|
+
if (storedICU != null) {
|
|
157
|
+
item.msgid = storedICU;
|
|
158
|
+
}
|
|
204
159
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
160
|
+
// If all translations are empty, ignore item.
|
|
161
|
+
if (item.msgstr.every(str => str.length === 0)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (pluralForms == null) {
|
|
165
|
+
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);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const pluralCount = pluralForms.length;
|
|
169
|
+
if (translationCount > pluralCount) {
|
|
170
|
+
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);
|
|
171
|
+
}
|
|
217
172
|
|
|
218
|
-
|
|
219
|
-
|
|
173
|
+
// Map each msgstr to a "<pluralform> {<translated_string>}" entry, joined by one space.
|
|
174
|
+
const pluralClauses = item.msgstr.map((str, index) => pluralForms[index] + " {" + str + "}").join(" ");
|
|
220
175
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
});
|
|
176
|
+
// Find out placeholder name from item's message context, defaulting to "count".
|
|
177
|
+
let pluralizeOn = ctx.get("pluralize_on");
|
|
178
|
+
if (!pluralizeOn) {
|
|
179
|
+
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);
|
|
180
|
+
pluralizeOn = "count";
|
|
181
|
+
}
|
|
182
|
+
item.msgstr = ["{" + pluralizeOn + ", plural, " + pluralClauses + "}"];
|
|
229
183
|
};
|
|
230
|
-
const indexItems = R.indexBy(getMessageKey);
|
|
231
184
|
const poGettext = {
|
|
232
185
|
catalogExtension: ".po",
|
|
186
|
+
templateExtension: ".pot",
|
|
233
187
|
write(filename, catalog, options) {
|
|
234
188
|
let po;
|
|
235
|
-
|
|
236
|
-
|
|
189
|
+
const raw = (0, _utils.readFile)(filename);
|
|
190
|
+
if (raw) {
|
|
237
191
|
po = _pofile.default.parse(raw);
|
|
238
192
|
} else {
|
|
239
193
|
po = new _pofile.default();
|
|
@@ -241,21 +195,30 @@ const poGettext = {
|
|
|
241
195
|
if (options.locale === undefined) {
|
|
242
196
|
delete po.headers.Language;
|
|
243
197
|
}
|
|
198
|
+
// accessing private property
|
|
199
|
+
;
|
|
244
200
|
po.headerOrder = Object.keys(po.headers);
|
|
245
201
|
}
|
|
246
|
-
po.items =
|
|
202
|
+
po.items = serialize(catalog, options);
|
|
247
203
|
(0, _utils.writeFileIfChanged)(filename, po.toString());
|
|
248
204
|
},
|
|
249
|
-
// Mainly exported for easier testing
|
|
250
|
-
serialize,
|
|
251
205
|
read(filename) {
|
|
252
|
-
const raw =
|
|
206
|
+
const raw = (0, _utils.readFile)(filename);
|
|
207
|
+
if (!raw) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
253
210
|
return this.parse(raw);
|
|
254
211
|
},
|
|
255
212
|
parse(raw) {
|
|
256
213
|
const po = _pofile.default.parse(raw);
|
|
257
|
-
|
|
258
|
-
|
|
214
|
+
|
|
215
|
+
// .po plurals are numbered 0-N and need to be mapped to ICU plural classes ("one", "few", "many"...). Different
|
|
216
|
+
// languages can have different plural classes (some start with "zero", some with "one"), so read that data from CLDR.
|
|
217
|
+
// `pluralForms` may be `null` if lang is not found. As long as no plural is used, don't report an error.
|
|
218
|
+
let pluralForms = getPluralCases(po.headers.Language);
|
|
219
|
+
return (0, _po.deserialize)(po.items, item => {
|
|
220
|
+
convertPluralsToICU(item, pluralForms, po.headers.Language);
|
|
221
|
+
});
|
|
259
222
|
}
|
|
260
223
|
};
|
|
261
224
|
var _default = poGettext;
|