@lingui/cli 4.0.0-next.4 → 4.0.0-next.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/api/catalog/extractFromFiles.d.ts +5 -0
  2. package/dist/api/catalog/extractFromFiles.js +47 -0
  3. package/dist/api/catalog/getCatalogs.d.ts +14 -0
  4. package/dist/api/catalog/getCatalogs.js +129 -0
  5. package/dist/api/catalog/getTranslationsForCatalog.d.ts +14 -0
  6. package/dist/api/catalog/getTranslationsForCatalog.js +72 -0
  7. package/dist/api/catalog/mergeCatalog.d.ts +3 -0
  8. package/dist/api/catalog/mergeCatalog.js +55 -0
  9. package/dist/api/catalog.d.ts +66 -0
  10. package/dist/api/catalog.js +261 -0
  11. package/dist/api/compile.d.ts +19 -0
  12. package/dist/api/compile.js +88 -0
  13. package/dist/api/extractors/babel.d.ts +3 -0
  14. package/dist/api/extractors/babel.js +101 -0
  15. package/dist/api/extractors/index.d.ts +6 -0
  16. package/dist/api/extractors/index.js +38 -0
  17. package/dist/api/extractors/typescript.d.ts +3 -0
  18. package/dist/api/extractors/typescript.js +11 -0
  19. package/dist/api/formats/formatterWrapper.d.ts +10 -0
  20. package/dist/api/formats/formatterWrapper.js +43 -0
  21. package/dist/api/formats/index.d.ts +5 -0
  22. package/dist/api/formats/index.js +47 -0
  23. package/dist/api/help.d.ts +1 -0
  24. package/dist/api/help.js +40 -0
  25. package/dist/api/index.d.ts +4 -0
  26. package/dist/api/index.js +25 -0
  27. package/dist/api/pseudoLocalize.d.ts +1 -0
  28. package/dist/api/pseudoLocalize.js +56 -0
  29. package/dist/api/rethrownError.d.ts +4 -0
  30. package/dist/api/rethrownError.js +11 -0
  31. package/dist/api/stats.d.ts +6 -0
  32. package/dist/api/stats.js +41 -0
  33. package/dist/api/types.d.ts +5 -0
  34. package/dist/api/types.js +2 -0
  35. package/dist/api/utils.d.ts +22 -0
  36. package/dist/api/utils.js +119 -0
  37. package/dist/extract-experimental/buildExternalizeFilter.d.ts +13 -0
  38. package/dist/extract-experimental/buildExternalizeFilter.js +38 -0
  39. package/dist/extract-experimental/bundleSource.d.ts +2 -0
  40. package/dist/extract-experimental/bundleSource.js +78 -0
  41. package/dist/extract-experimental/constants.d.ts +2 -0
  42. package/dist/extract-experimental/constants.js +5 -0
  43. package/dist/extract-experimental/getEntryPoints.d.ts +1 -0
  44. package/dist/extract-experimental/getEntryPoints.js +12 -0
  45. package/dist/extract-experimental/getExperimentalCatalogs.d.ts +3 -0
  46. package/dist/extract-experimental/getExperimentalCatalogs.js +26 -0
  47. package/dist/extract-experimental/resolveCatalogPath.d.ts +2 -0
  48. package/dist/extract-experimental/resolveCatalogPath.js +23 -0
  49. package/dist/extract-experimental/resolveTemplatePath.d.ts +1 -0
  50. package/dist/extract-experimental/resolveTemplatePath.js +16 -0
  51. package/dist/extract-experimental/writeCatalogs.d.ts +21 -0
  52. package/dist/extract-experimental/writeCatalogs.js +41 -0
  53. package/dist/lingui-compile.d.ts +9 -0
  54. package/dist/lingui-compile.js +170 -0
  55. package/dist/lingui-extract-experimental.d.ts +10 -0
  56. package/dist/lingui-extract-experimental.js +104 -0
  57. package/dist/lingui-extract-template.d.ts +6 -0
  58. package/dist/lingui-extract-template.js +46 -0
  59. package/dist/lingui-extract.d.ts +11 -0
  60. package/dist/lingui-extract.js +156 -0
  61. package/dist/lingui.d.ts +2 -0
  62. package/dist/lingui.js +13 -0
  63. package/dist/services/translationIO.d.ts +3 -0
  64. package/dist/services/translationIO.js +264 -0
  65. package/package.json +29 -14
  66. package/build/LICENSE +0 -21
  67. package/build/api/catalog/extractFromFiles.js +0 -45
  68. package/build/api/catalog/getCatalogs.js +0 -138
  69. package/build/api/catalog/getTranslationsForCatalog.js +0 -77
  70. package/build/api/catalog/mergeCatalog.js +0 -44
  71. package/build/api/catalog.js +0 -245
  72. package/build/api/compile.js +0 -72
  73. package/build/api/extractors/babel.js +0 -92
  74. package/build/api/extractors/index.js +0 -36
  75. package/build/api/extractors/typescript.js +0 -14
  76. package/build/api/formats/formatterWrapper.js +0 -45
  77. package/build/api/formats/index.js +0 -57
  78. package/build/api/generateMessageId.js +0 -12
  79. package/build/api/help.js +0 -42
  80. package/build/api/index.js +0 -58
  81. package/build/api/pseudoLocalize.js +0 -60
  82. package/build/api/rethrownError.js +0 -14
  83. package/build/api/stats.js +0 -40
  84. package/build/api/types.js +0 -5
  85. package/build/api/utils.js +0 -117
  86. package/build/extract-experimental/buildExternalizeFilter.js +0 -39
  87. package/build/extract-experimental/bundleSource.js +0 -69
  88. package/build/extract-experimental/constants.js +0 -10
  89. package/build/extract-experimental/getEntryPoints.js +0 -14
  90. package/build/extract-experimental/getExperimentalCatalogs.js +0 -28
  91. package/build/extract-experimental/resolveCatalogPath.js +0 -23
  92. package/build/extract-experimental/resolveTemplatePath.js +0 -17
  93. package/build/extract-experimental/writeCatalogs.js +0 -59
  94. package/build/lingui-compile.js +0 -156
  95. package/build/lingui-extract-experimental.js +0 -103
  96. package/build/lingui-extract-template.js +0 -46
  97. package/build/lingui-extract.js +0 -143
  98. package/build/lingui.js +0 -6
  99. package/build/services/translationIO.js +0 -266
  100. package/build/tests.js +0 -133
@@ -0,0 +1,5 @@
1
+ import type { LinguiConfigNormalized } from "@lingui/conf";
2
+ import { ExtractedCatalogType } from "../types";
3
+ export declare function extractFromFiles(paths: string[], config: LinguiConfigNormalized): Promise<ExtractedCatalogType<{
4
+ [x: string]: unknown;
5
+ }>>;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.extractFromFiles = void 0;
7
+ const extractors_1 = __importDefault(require("../extractors"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const utils_1 = require("../utils");
11
+ async function extractFromFiles(paths, config) {
12
+ const messages = {};
13
+ let catalogSuccess = true;
14
+ for (let filename of paths) {
15
+ const fileSuccess = await (0, extractors_1.default)(filename, (next) => {
16
+ if (!messages[next.id]) {
17
+ messages[next.id] = {
18
+ message: next.message,
19
+ context: next.context,
20
+ comments: [],
21
+ origin: [],
22
+ };
23
+ }
24
+ const prev = messages[next.id];
25
+ // there might be a case when filename was not mapped from sourcemaps
26
+ const filename = next.origin[0]
27
+ ? path_1.default.relative(config.rootDir, next.origin[0]).replace(/\\/g, "/")
28
+ : "";
29
+ const origin = [filename, next.origin[1]];
30
+ if (prev.message && next.message && prev.message !== next.message) {
31
+ throw new Error(`Encountered different default translations for message ${chalk_1.default.yellow(next.id)}` +
32
+ `\n${chalk_1.default.yellow((0, utils_1.prettyOrigin)(prev.origin))} ${prev.message}` +
33
+ `\n${chalk_1.default.yellow((0, utils_1.prettyOrigin)([origin]))} ${next.message}`);
34
+ }
35
+ messages[next.id] = Object.assign(Object.assign({}, prev), { comments: next.comment
36
+ ? [...prev.comments, next.comment]
37
+ : prev.comments, origin: [...prev.origin, [filename, next.origin[1]]] });
38
+ }, config, {
39
+ extractors: config.extractors,
40
+ });
41
+ catalogSuccess && (catalogSuccess = fileSuccess);
42
+ }
43
+ if (!catalogSuccess)
44
+ return undefined;
45
+ return messages;
46
+ }
47
+ exports.extractFromFiles = extractFromFiles;
@@ -0,0 +1,14 @@
1
+ import { LinguiConfigNormalized } from "@lingui/conf";
2
+ import { Catalog } from "../catalog";
3
+ /**
4
+ * Parse `config.catalogs` and return a list of configured Catalog instances.
5
+ */
6
+ export declare function getCatalogs(config: LinguiConfigNormalized): Promise<Catalog[]>;
7
+ /**
8
+ * Create catalog for merged messages.
9
+ */
10
+ export declare function getCatalogForMerge(config: LinguiConfigNormalized): Promise<Catalog>;
11
+ export declare function getCatalogForFile(file: string, catalogs: Catalog[]): {
12
+ locale: string;
13
+ catalog: Catalog;
14
+ };
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getCatalogForFile = exports.getCatalogForMerge = exports.getCatalogs = void 0;
7
+ const glob_1 = __importDefault(require("glob"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const catalog_1 = require("../catalog");
10
+ const utils_1 = require("../utils");
11
+ const micromatch_1 = __importDefault(require("micromatch"));
12
+ const formats_1 = require("../formats");
13
+ const getExperimentalCatalogs_1 = require("../../extract-experimental/getExperimentalCatalogs");
14
+ const NAME_PH = "{name}";
15
+ const LOCALE_PH = "{locale}";
16
+ /**
17
+ * Parse `config.catalogs` and return a list of configured Catalog instances.
18
+ */
19
+ async function getCatalogs(config) {
20
+ var _a, _b;
21
+ const catalogsConfig = config.catalogs;
22
+ const catalogs = [];
23
+ const format = await (0, formats_1.getFormat)(config.format, config.formatOptions, config.sourceLocale);
24
+ catalogsConfig.forEach((catalog) => {
25
+ validateCatalogPath(catalog.path, format.getCatalogExtension());
26
+ const include = ensureArray(catalog.include).map(utils_1.normalizeRelativePath);
27
+ const exclude = ensureArray(catalog.exclude).map(utils_1.normalizeRelativePath);
28
+ // catalog.path without {name} pattern -> always refers to a single catalog
29
+ if (!catalog.path.includes(NAME_PH)) {
30
+ // Validate that sourcePaths doesn't use {name} pattern either
31
+ const invalidSource = include.find((path) => path.includes(NAME_PH));
32
+ if (invalidSource !== undefined) {
33
+ throw new Error(`Catalog with path "${catalog.path}" doesn't have a {name} pattern` +
34
+ ` in it, but one of source directories uses it: "${invalidSource}".` +
35
+ ` Either add {name} pattern to "${catalog.path}" or remove it` +
36
+ ` from all source directories.`);
37
+ }
38
+ catalogs.push(new catalog_1.Catalog({
39
+ name: getCatalogName(catalog.path),
40
+ path: (0, utils_1.normalizeRelativePath)(catalog.path),
41
+ include,
42
+ exclude,
43
+ format,
44
+ }, config));
45
+ return;
46
+ }
47
+ const patterns = include.map((path) => (0, utils_1.replacePlaceholders)(path, { name: "*" }));
48
+ const candidates = glob_1.default.sync(patterns.length > 1 ? `{${patterns.join(",")}}` : patterns[0], {
49
+ ignore: exclude,
50
+ mark: true,
51
+ });
52
+ candidates.forEach((catalogDir) => {
53
+ const name = path_1.default.basename(catalogDir);
54
+ catalogs.push(new catalog_1.Catalog({
55
+ name,
56
+ path: (0, utils_1.normalizeRelativePath)((0, utils_1.replacePlaceholders)(catalog.path, { name })),
57
+ include: include.map((path) => (0, utils_1.replacePlaceholders)(path, { name })),
58
+ exclude: exclude.map((path) => (0, utils_1.replacePlaceholders)(path, { name })),
59
+ format,
60
+ }, config));
61
+ });
62
+ });
63
+ if ((_b = (_a = config.experimental) === null || _a === void 0 ? void 0 : _a.extractor) === null || _b === void 0 ? void 0 : _b.entries.length) {
64
+ catalogs.push(...(await (0, getExperimentalCatalogs_1.getExperimentalCatalogs)(config)));
65
+ }
66
+ return catalogs;
67
+ }
68
+ exports.getCatalogs = getCatalogs;
69
+ /**
70
+ * Ensure that value is always array. If not, turn it into an array of one element.
71
+ */
72
+ const ensureArray = (value) => {
73
+ if (value == null)
74
+ return [];
75
+ return Array.isArray(value) ? value : [value];
76
+ };
77
+ /**
78
+ * Create catalog for merged messages.
79
+ */
80
+ async function getCatalogForMerge(config) {
81
+ const format = await (0, formats_1.getFormat)(config.format, config.formatOptions, config.sourceLocale);
82
+ validateCatalogPath(config.catalogsMergePath, format.getCatalogExtension());
83
+ return new catalog_1.Catalog({
84
+ name: getCatalogName(config.catalogsMergePath),
85
+ path: (0, utils_1.normalizeRelativePath)(config.catalogsMergePath),
86
+ include: [],
87
+ exclude: [],
88
+ format,
89
+ }, config);
90
+ }
91
+ exports.getCatalogForMerge = getCatalogForMerge;
92
+ function getCatalogForFile(file, catalogs) {
93
+ for (const catalog of catalogs) {
94
+ const catalogFile = `${catalog.path}${catalog.format.getCatalogExtension()}`;
95
+ const catalogGlob = (0, utils_1.replacePlaceholders)(catalogFile, { locale: "*" });
96
+ const match = micromatch_1.default.capture((0, utils_1.normalizeRelativePath)(path_1.default.relative(catalog.config.rootDir, catalogGlob)), (0, utils_1.normalizeRelativePath)(file));
97
+ if (match) {
98
+ return {
99
+ locale: match[0],
100
+ catalog,
101
+ };
102
+ }
103
+ }
104
+ return null;
105
+ }
106
+ exports.getCatalogForFile = getCatalogForFile;
107
+ /**
108
+ * Validate that `catalogPath` doesn't end with trailing slash
109
+ */
110
+ function validateCatalogPath(path, extension) {
111
+ if (!path.endsWith(utils_1.PATHSEP)) {
112
+ return;
113
+ }
114
+ const correctPath = path.slice(0, -1);
115
+ const examplePath = (0, utils_1.replacePlaceholders)(correctPath, {
116
+ locale: "en",
117
+ }) + extension;
118
+ throw new Error(
119
+ // prettier-ignore
120
+ `Remove trailing slash from "${path}". Catalog path isn't a directory,` +
121
+ ` but translation file without extension. For example, catalog path "${correctPath}"` +
122
+ ` results in translation file "${examplePath}".`);
123
+ }
124
+ function getCatalogName(filePath) {
125
+ // catalog name is the last directory of catalogPath.
126
+ // If the last part is {locale}, then catalog doesn't have an explicit name
127
+ const _name = path_1.default.basename((0, utils_1.normalizeRelativePath)(filePath));
128
+ return _name !== LOCALE_PH ? _name : null;
129
+ }
@@ -0,0 +1,14 @@
1
+ import { Catalog } from "../catalog";
2
+ import { FallbackLocales } from "@lingui/conf";
3
+ export type TranslationMissingEvent = {
4
+ source: string;
5
+ id: string;
6
+ };
7
+ export type GetTranslationsOptions = {
8
+ sourceLocale: string;
9
+ fallbackLocales: FallbackLocales;
10
+ onMissing?: (message: TranslationMissingEvent) => void;
11
+ };
12
+ export declare function getTranslationsForCatalog(catalog: Catalog, locale: string, options: GetTranslationsOptions): Promise<{
13
+ [id: string]: string;
14
+ }>;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTranslationsForCatalog = void 0;
4
+ async function getTranslationsForCatalog(catalog, locale, options) {
5
+ const catalogs = await catalog.readAll();
6
+ const template = (await catalog.readTemplate()) || {};
7
+ const sourceLocaleCatalog = catalogs[options.sourceLocale] || {};
8
+ const input = Object.assign(Object.assign(Object.assign({}, template), sourceLocaleCatalog), catalogs[locale]);
9
+ return Object.keys(input).reduce((acc, key) => {
10
+ acc[key] = getTranslation(catalogs, input[key], locale, key, options);
11
+ return acc;
12
+ }, {});
13
+ }
14
+ exports.getTranslationsForCatalog = getTranslationsForCatalog;
15
+ function sourceLocaleFallback(catalog, key) {
16
+ if (!(catalog === null || catalog === void 0 ? void 0 : catalog[key])) {
17
+ return null;
18
+ }
19
+ return catalog[key].translation || catalog[key].message;
20
+ }
21
+ function getTranslation(catalogs, msg, locale, key, options) {
22
+ const { fallbackLocales, sourceLocale, onMissing } = options;
23
+ const getTranslation = (_locale) => {
24
+ var _a;
25
+ const localeCatalog = catalogs[_locale];
26
+ return (_a = localeCatalog === null || localeCatalog === void 0 ? void 0 : localeCatalog[key]) === null || _a === void 0 ? void 0 : _a.translation;
27
+ };
28
+ const getMultipleFallbacks = (_locale) => {
29
+ const fL = fallbackLocales && (fallbackLocales === null || fallbackLocales === void 0 ? void 0 : fallbackLocales[_locale]);
30
+ // some probably the fallback will be undefined, so just search by locale
31
+ if (!fL)
32
+ return null;
33
+ if (Array.isArray(fL)) {
34
+ for (const fallbackLocale of fL) {
35
+ if (catalogs[fallbackLocale] && getTranslation(fallbackLocale)) {
36
+ return getTranslation(fallbackLocale);
37
+ }
38
+ }
39
+ }
40
+ else {
41
+ return getTranslation(fL);
42
+ }
43
+ };
44
+ // target locale -> fallback locales -> fallback locales default ->
45
+ // ** (following fallbacks would emit `missing` warning) **
46
+ // -> source locale translation -> source locale message
47
+ // -> template message
48
+ // ** last resort **
49
+ // -> id
50
+ let translation =
51
+ // Get translation in target locale
52
+ getTranslation(locale) ||
53
+ // We search in fallbackLocales as dependent of each locale
54
+ getMultipleFallbacks(locale) ||
55
+ // Get translation in fallbackLocales.default (if any)
56
+ ((fallbackLocales === null || fallbackLocales === void 0 ? void 0 : fallbackLocales.default) && getTranslation(fallbackLocales.default)) ||
57
+ (sourceLocale &&
58
+ sourceLocale === locale &&
59
+ sourceLocaleFallback(catalogs[sourceLocale], key));
60
+ if (!translation) {
61
+ onMissing &&
62
+ onMissing({
63
+ id: key,
64
+ source: msg.message || sourceLocaleFallback(catalogs[sourceLocale], key),
65
+ });
66
+ }
67
+ return (translation ||
68
+ (sourceLocale && sourceLocaleFallback(catalogs[sourceLocale], key)) ||
69
+ // take from template
70
+ msg.message ||
71
+ key);
72
+ }
@@ -0,0 +1,3 @@
1
+ import type { MergeOptions } from "../catalog";
2
+ import { CatalogType, ExtractedCatalogType } from "../types";
3
+ export declare function mergeCatalog(prevCatalog: CatalogType, nextCatalog: ExtractedCatalogType, forSourceLocale: boolean, options: MergeOptions): CatalogType;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.mergeCatalog = void 0;
27
+ const R = __importStar(require("ramda"));
28
+ function mergeCatalog(prevCatalog, nextCatalog, forSourceLocale, options) {
29
+ const nextKeys = Object.keys(nextCatalog);
30
+ const prevKeys = R.keys(prevCatalog).map(String);
31
+ const newKeys = R.difference(nextKeys, prevKeys);
32
+ const mergeKeys = R.intersection(nextKeys, prevKeys);
33
+ const obsoleteKeys = R.difference(prevKeys, nextKeys);
34
+ // Initialize new catalog with new keys
35
+ const newMessages = R.mapObjIndexed((message, key) => (Object.assign({ translation: forSourceLocale ? message.message || key : "" }, message)), R.pick(newKeys, nextCatalog));
36
+ // Merge translations from previous catalog
37
+ const mergedMessages = mergeKeys.map((key) => {
38
+ const updateFromDefaults = forSourceLocale &&
39
+ (prevCatalog[key].translation === prevCatalog[key].message ||
40
+ options.overwrite);
41
+ const translation = updateFromDefaults
42
+ ? nextCatalog[key].message || key
43
+ : prevCatalog[key].translation;
44
+ return {
45
+ [key]: Object.assign({ translation }, R.omit(["obsolete, translation"], nextCatalog[key])),
46
+ };
47
+ });
48
+ // Mark all remaining translations as obsolete
49
+ // Only if *options.files* is not provided
50
+ const obsoleteMessages = obsoleteKeys.map((key) => ({
51
+ [key]: Object.assign(Object.assign({}, prevCatalog[key]), { obsolete: !options.files }),
52
+ }));
53
+ return R.mergeAll([newMessages, ...mergedMessages, ...obsoleteMessages]);
54
+ }
55
+ exports.mergeCatalog = mergeCatalog;
@@ -0,0 +1,66 @@
1
+ import * as R from "ramda";
2
+ import { LinguiConfigNormalized, OrderBy } from "@lingui/conf";
3
+ import { FormatterWrapper } from "./formats";
4
+ import { CliExtractOptions } from "../lingui-extract";
5
+ import { CliExtractTemplateOptions } from "../lingui-extract-template";
6
+ import { CompiledCatalogNamespace } from "./compile";
7
+ import { GetTranslationsOptions } from "./catalog/getTranslationsForCatalog";
8
+ import { AllCatalogsType, CatalogType, ExtractedCatalogType, ExtractedMessageType } from "./types";
9
+ export type MakeOptions = CliExtractOptions & {
10
+ orderBy?: OrderBy;
11
+ };
12
+ export type MakeTemplateOptions = CliExtractTemplateOptions & {
13
+ orderBy?: OrderBy;
14
+ };
15
+ export type MergeOptions = {
16
+ overwrite?: boolean;
17
+ files?: string[];
18
+ };
19
+ export type CatalogProps = {
20
+ name?: string;
21
+ path: string;
22
+ include: Array<string>;
23
+ exclude?: Array<string>;
24
+ templatePath?: string;
25
+ format: FormatterWrapper;
26
+ };
27
+ export declare class Catalog {
28
+ config: LinguiConfigNormalized;
29
+ name?: string;
30
+ path: string;
31
+ include: Array<string>;
32
+ exclude: Array<string>;
33
+ format: FormatterWrapper;
34
+ templateFile?: string;
35
+ constructor({ name, path, include, templatePath, format, exclude }: CatalogProps, config: LinguiConfigNormalized);
36
+ make(options: MakeOptions): Promise<AllCatalogsType | false>;
37
+ makeTemplate(options: MakeTemplateOptions): Promise<CatalogType | false>;
38
+ /**
39
+ * Collect messages from source paths. Return a raw message catalog as JSON.
40
+ */
41
+ collect(options?: {
42
+ files?: string[];
43
+ }): Promise<ExtractedCatalogType | undefined>;
44
+ merge(prevCatalogs: AllCatalogsType, nextCatalog: ExtractedCatalogType, options: MergeOptions): Record<string, CatalogType<{
45
+ [x: string]: unknown;
46
+ }>>;
47
+ getTranslations(locale: string, options: GetTranslationsOptions): Promise<{
48
+ [id: string]: string;
49
+ }>;
50
+ write(locale: string, messages: CatalogType): Promise<[created: boolean, filename: string]>;
51
+ writeTemplate(messages: CatalogType): Promise<void>;
52
+ writeCompiled(locale: string, compiledCatalog: string, namespace?: CompiledCatalogNamespace): Promise<string>;
53
+ read(locale: string): Promise<CatalogType>;
54
+ readAll(): Promise<AllCatalogsType>;
55
+ readTemplate(): Promise<CatalogType>;
56
+ get sourcePaths(): string[];
57
+ get localeDir(): string;
58
+ get locales(): string[];
59
+ }
60
+ export declare const cleanObsolete: <K extends ExtractedMessageType<{
61
+ [x: string]: unknown;
62
+ }>[] | R.Dictionary<ExtractedMessageType<{
63
+ [x: string]: unknown;
64
+ }>>>(source: K) => K extends (infer U)[] ? U[] : K extends R.Dictionary<infer U_1> ? R.Dictionary<U_1> : never;
65
+ export declare function order<T extends ExtractedCatalogType>(by: OrderBy): (catalog: T) => T;
66
+ export declare function orderByMessage<T extends ExtractedCatalogType>(messages: T): T;
@@ -0,0 +1,261 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.orderByMessage = exports.order = exports.cleanObsolete = exports.Catalog = void 0;
30
+ const fs_1 = __importDefault(require("fs"));
31
+ const path_1 = __importDefault(require("path"));
32
+ const R = __importStar(require("ramda"));
33
+ const glob_1 = __importDefault(require("glob"));
34
+ const normalize_path_1 = __importDefault(require("normalize-path"));
35
+ const getTranslationsForCatalog_1 = require("./catalog/getTranslationsForCatalog");
36
+ const mergeCatalog_1 = require("./catalog/mergeCatalog");
37
+ const extractFromFiles_1 = require("./catalog/extractFromFiles");
38
+ const utils_1 = require("./utils");
39
+ const LOCALE = "{locale}";
40
+ const LOCALE_SUFFIX_RE = /\{locale\}.*$/;
41
+ class Catalog {
42
+ constructor({ name, path, include, templatePath, format, exclude = [] }, config) {
43
+ this.config = config;
44
+ this.name = name;
45
+ this.path = (0, utils_1.normalizeRelativePath)(path);
46
+ this.include = include.map(utils_1.normalizeRelativePath);
47
+ this.exclude = [this.localeDir, ...exclude.map(utils_1.normalizeRelativePath)];
48
+ this.format = format;
49
+ this.templateFile =
50
+ templatePath ||
51
+ getTemplatePath(this.format.getTemplateExtension(), this.path);
52
+ }
53
+ async make(options) {
54
+ const nextCatalog = await this.collect({ files: options.files });
55
+ if (!nextCatalog)
56
+ return false;
57
+ const prevCatalogs = await this.readAll();
58
+ const catalogs = this.merge(prevCatalogs, nextCatalog, {
59
+ overwrite: options.overwrite,
60
+ files: options.files,
61
+ });
62
+ // Map over all locales and post-process each catalog
63
+ const cleanAndSort = R.map(R.pipe(
64
+ // Clean obsolete messages
65
+ (options.clean ? exports.cleanObsolete : R.identity),
66
+ // Sort messages
67
+ order(options.orderBy)));
68
+ const sortedCatalogs = cleanAndSort(catalogs);
69
+ const locales = options.locale ? [options.locale] : this.locales;
70
+ await Promise.all(locales.map((locale) => this.write(locale, sortedCatalogs[locale])));
71
+ return sortedCatalogs;
72
+ }
73
+ async makeTemplate(options) {
74
+ const catalog = await this.collect({ files: options.files });
75
+ if (!catalog)
76
+ return false;
77
+ const sorted = order(options.orderBy)(catalog);
78
+ await this.writeTemplate(sorted);
79
+ return sorted;
80
+ }
81
+ /**
82
+ * Collect messages from source paths. Return a raw message catalog as JSON.
83
+ */
84
+ async collect(options = {}) {
85
+ let paths = this.sourcePaths;
86
+ if (options.files) {
87
+ options.files = options.files.map((p) => (0, normalize_path_1.default)(p, false));
88
+ const regex = new RegExp(options.files.join("|"), "i");
89
+ paths = paths.filter((path) => regex.test(path));
90
+ }
91
+ return await (0, extractFromFiles_1.extractFromFiles)(paths, this.config);
92
+ }
93
+ /*
94
+ *
95
+ * prevCatalogs - map of message catalogs in all available languages with translations
96
+ * nextCatalog - language-agnostic catalog with collected messages
97
+ *
98
+ * Note: if a catalog in prevCatalogs is null it means the language is available, but
99
+ * no previous catalog was generated (usually first run).
100
+ *
101
+ * Orthogonal use-cases
102
+ * --------------------
103
+ *
104
+ * Message IDs:
105
+ * - auto-generated IDs: message is used as a key, `defaults` is not set
106
+ * - custom IDs: message is used as `defaults`, custom ID as a key
107
+ *
108
+ * Source locale (defined by `sourceLocale` in config):
109
+ * - catalog for `sourceLocale`: initially, `translation` is prefilled with `defaults`
110
+ * (for custom IDs) or `key` (for auto-generated IDs)
111
+ * - all other languages: translation is kept empty
112
+ */
113
+ merge(prevCatalogs, nextCatalog, options) {
114
+ return R.mapObjIndexed((prevCatalog, locale) => {
115
+ return (0, mergeCatalog_1.mergeCatalog)(prevCatalog, nextCatalog, this.config.sourceLocale === locale, options);
116
+ }, prevCatalogs);
117
+ }
118
+ async getTranslations(locale, options) {
119
+ return await (0, getTranslationsForCatalog_1.getTranslationsForCatalog)(this, locale, options);
120
+ }
121
+ async write(locale, messages) {
122
+ const filename = (0, utils_1.replacePlaceholders)(this.path, { locale }) +
123
+ this.format.getCatalogExtension();
124
+ const created = !fs_1.default.existsSync(filename);
125
+ await this.format.write(filename, messages, locale);
126
+ return [created, filename];
127
+ }
128
+ async writeTemplate(messages) {
129
+ const filename = this.templateFile;
130
+ await this.format.write(filename, messages, undefined);
131
+ }
132
+ async writeCompiled(locale, compiledCatalog, namespace) {
133
+ let ext;
134
+ if (namespace === "es") {
135
+ ext = "mjs";
136
+ }
137
+ else if (namespace === "ts") {
138
+ ext = "ts";
139
+ }
140
+ else {
141
+ ext = "js";
142
+ }
143
+ const filename = `${(0, utils_1.replacePlaceholders)(this.path, { locale })}.${ext}`;
144
+ await (0, utils_1.writeFile)(filename, compiledCatalog);
145
+ return filename;
146
+ }
147
+ async read(locale) {
148
+ const filename = (0, utils_1.replacePlaceholders)(this.path, { locale }) +
149
+ this.format.getCatalogExtension();
150
+ return await this.format.read(filename, locale);
151
+ }
152
+ async readAll() {
153
+ const res = {};
154
+ await Promise.all(this.locales.map(async (locale) => (res[locale] = await this.read(locale))));
155
+ // statement above will save locales in object in undetermined order
156
+ // resort here to have keys order the same as in locales definition
157
+ return this.locales.reduce((acc, locale) => {
158
+ acc[locale] = res[locale];
159
+ return acc;
160
+ }, {});
161
+ }
162
+ async readTemplate() {
163
+ const filename = this.templateFile;
164
+ return await this.format.read(filename, undefined);
165
+ }
166
+ get sourcePaths() {
167
+ const includeGlobs = this.include.map((includePath) => {
168
+ const isDir = (0, utils_1.isDirectory)(includePath);
169
+ /**
170
+ * glob library results from absolute patterns such as /foo/* are mounted onto the root setting using path.join.
171
+ * On windows, this will by default result in /foo/* matching C:\foo\bar.txt.
172
+ */
173
+ return isDir
174
+ ? (0, normalize_path_1.default)(path_1.default.resolve(process.cwd(), includePath === "/" ? "" : includePath, "**/*.*"))
175
+ : includePath;
176
+ });
177
+ const patterns = includeGlobs.length > 1 ? `{${includeGlobs.join(",")}}` : includeGlobs[0];
178
+ return glob_1.default.sync(patterns, { ignore: this.exclude, mark: true });
179
+ }
180
+ get localeDir() {
181
+ const localePatternIndex = this.path.indexOf(LOCALE);
182
+ if (localePatternIndex === -1) {
183
+ throw Error(`Invalid catalog path: ${LOCALE} variable is missing`);
184
+ }
185
+ return this.path.substr(0, localePatternIndex);
186
+ }
187
+ get locales() {
188
+ return this.config.locales;
189
+ }
190
+ }
191
+ exports.Catalog = Catalog;
192
+ function getTemplatePath(ext, path) {
193
+ return path.replace(LOCALE_SUFFIX_RE, "messages" + ext);
194
+ }
195
+ exports.cleanObsolete = R.filter((message) => !message.obsolete);
196
+ function order(by) {
197
+ return {
198
+ messageId: orderByMessageId,
199
+ message: orderByMessage,
200
+ origin: orderByOrigin,
201
+ }[by];
202
+ }
203
+ exports.order = order;
204
+ /**
205
+ * Object keys are in the same order as they were created
206
+ * https://stackoverflow.com/a/31102605/1535540
207
+ */
208
+ function orderByMessageId(messages) {
209
+ return Object.keys(messages)
210
+ .sort()
211
+ .reduce((acc, key) => {
212
+ ;
213
+ acc[key] = messages[key];
214
+ return acc;
215
+ }, {});
216
+ }
217
+ function orderByOrigin(messages) {
218
+ function getFirstOrigin(messageKey) {
219
+ const sortedOrigins = messages[messageKey].origin.sort((a, b) => {
220
+ if (a[0] < b[0])
221
+ return -1;
222
+ if (a[0] > b[0])
223
+ return 1;
224
+ return 0;
225
+ });
226
+ return sortedOrigins[0];
227
+ }
228
+ return Object.keys(messages)
229
+ .sort((a, b) => {
230
+ const [aFile, aLineNumber] = getFirstOrigin(a);
231
+ const [bFile, bLineNumber] = getFirstOrigin(b);
232
+ if (aFile < bFile)
233
+ return -1;
234
+ if (aFile > bFile)
235
+ return 1;
236
+ if (aLineNumber < bLineNumber)
237
+ return -1;
238
+ if (aLineNumber > bLineNumber)
239
+ return 1;
240
+ return 0;
241
+ })
242
+ .reduce((acc, key) => {
243
+ ;
244
+ acc[key] = messages[key];
245
+ return acc;
246
+ }, {});
247
+ }
248
+ function orderByMessage(messages) {
249
+ return Object.keys(messages)
250
+ .sort((a, b) => {
251
+ const aMsg = messages[a].message || "";
252
+ const bMsg = messages[b].message || "";
253
+ return aMsg.localeCompare(bMsg);
254
+ })
255
+ .reduce((acc, key) => {
256
+ ;
257
+ acc[key] = messages[key];
258
+ return acc;
259
+ }, {});
260
+ }
261
+ exports.orderByMessage = orderByMessage;