@messagevisor/catalog 0.5.0 → 0.6.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/lib/node/index.js CHANGED
@@ -100,6 +100,90 @@ var fs = __importStar(require("fs"));
100
100
  var http = __importStar(require("http"));
101
101
  var path = __importStar(require("path"));
102
102
  var formatExamplePreview_1 = require("./formatExamplePreview");
103
+ var CLI_FORMAT_GREEN = "\x1b[32m%s\x1b[0m";
104
+ var CLI_FORMAT_DIM = "\x1b[2m%s\x1b[0m";
105
+ var CLI_FORMAT_BOLD = "\x1b[1m%s\x1b[0m";
106
+ function colorize(value, colorCode) {
107
+ return "\u001B[".concat(colorCode, "m").concat(value, "\u001B[0m");
108
+ }
109
+ function prettyDuration(diffInMs) {
110
+ var diff = Math.abs(diffInMs);
111
+ if (diff === 0) {
112
+ return "0ms";
113
+ }
114
+ var ms = diff % 1000;
115
+ diff = (diff - ms) / 1000;
116
+ var secs = diff % 60;
117
+ diff = (diff - secs) / 60;
118
+ var mins = diff % 60;
119
+ var hrs = (diff - mins) / 60;
120
+ var result = "";
121
+ if (hrs) {
122
+ result += " ".concat(hrs, "h");
123
+ }
124
+ if (mins) {
125
+ result += " ".concat(mins, "m");
126
+ }
127
+ if (secs) {
128
+ result += " ".concat(secs, "s");
129
+ }
130
+ if (ms) {
131
+ result += " ".concat(ms, "ms");
132
+ }
133
+ return result.trim();
134
+ }
135
+ function pluralize(count, singular, plural) {
136
+ if (plural === void 0) { plural = "".concat(singular, "s"); }
137
+ return "".concat(count, " ").concat(count === 1 ? singular : plural);
138
+ }
139
+ function formatCatalogPath(rootDirectoryPath, filePath) {
140
+ var relativePath = path.relative(rootDirectoryPath, filePath);
141
+ if (relativePath && !relativePath.startsWith("..") && !path.isAbsolute(relativePath)) {
142
+ return relativePath;
143
+ }
144
+ return filePath;
145
+ }
146
+ var CatalogProgressReporter = /** @class */ (function () {
147
+ function CatalogProgressReporter(rootDirectoryPath, outputDirectoryPath) {
148
+ this.rootDirectoryPath = rootDirectoryPath;
149
+ this.outputDirectoryPath = outputDirectoryPath;
150
+ this.startedAt = Date.now();
151
+ }
152
+ CatalogProgressReporter.prototype.start = function (options) {
153
+ console.log("");
154
+ console.log(CLI_FORMAT_BOLD, "Generating Messagevisor catalog");
155
+ console.log(" ".concat(colorize("Output", 36), ": ").concat(formatCatalogPath(this.rootDirectoryPath, this.outputDirectoryPath)));
156
+ console.log(" ".concat(colorize("Router", 36), ": ").concat(options.browserRouter ? "browser" : "hash"));
157
+ console.log(" ".concat(colorize("Sets", 36), ": ").concat(options.sets ? "enabled" : "none"));
158
+ console.log(" ".concat(colorize("Features", 36), ": ").concat(options.features.join(", ") || "none"));
159
+ console.log("");
160
+ };
161
+ CatalogProgressReporter.prototype.step = function (label, detail) {
162
+ var suffix = detail ? ": ".concat(colorize(detail, 2)) : "";
163
+ console.log(" ".concat(colorize("•", 36), " ").concat(label).concat(suffix));
164
+ return Date.now();
165
+ };
166
+ CatalogProgressReporter.prototype.done = function (startedAt, detail) {
167
+ var suffix = detail ? " ".concat(detail) : "";
168
+ console.log(CLI_FORMAT_DIM, " done in ".concat(prettyDuration(Date.now() - startedAt)).concat(suffix));
169
+ };
170
+ CatalogProgressReporter.prototype.setStart = function (set) {
171
+ console.log("");
172
+ if (set) {
173
+ console.log(CLI_FORMAT_BOLD, "Set \"".concat(set, "\""));
174
+ }
175
+ else {
176
+ console.log(CLI_FORMAT_BOLD, "Root catalog");
177
+ }
178
+ return Date.now();
179
+ };
180
+ CatalogProgressReporter.prototype.complete = function () {
181
+ console.log("");
182
+ console.log(CLI_FORMAT_GREEN, "Catalog exported to ".concat(formatCatalogPath(this.rootDirectoryPath, this.outputDirectoryPath)));
183
+ console.log(CLI_FORMAT_BOLD, "Time: ".concat(prettyDuration(Date.now() - this.startedAt)));
184
+ };
185
+ return CatalogProgressReporter;
186
+ }());
103
187
  exports.CATALOG_SCHEMA_VERSION = "1";
104
188
  exports.CATALOG_HISTORY_PAGE_SIZE = 50;
105
189
  function toPosixPath(value) {
@@ -815,12 +899,14 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
815
899
  translationShards[filename][msgKey].add(lower);
816
900
  }
817
901
  }
818
- var outputDirectoryPath, _a, localeKeys, messageKeys, attributeKeys, segmentKeys, targetKeys, _b, locales, messages, attributes, segments, targets, messageTargets, targetMessages, localeTargets, attributeTargets, segmentTargets, attributesUsedInSegments, attributesUsedInMessages, segmentsUsedInMessages, _i, targetKeys_1, targetKey, targetLocaleKeys, _c, targetLocaleKeys_1, localeKey, _d, _e, messageKey, _f, segmentKeys_1, segmentKey, usedAttributes, _g, _h, attributeKey, _j, messageKeys_1, messageKey, message, targetsForMessage, _k, _l, override, usedAttributes, usedSegments, _m, _o, attributeKey, _p, targetsForMessage_1, targetKey, _q, _r, segmentKey, _s, targetsForMessage_2, targetKey, _t, _u, attributeKey, _v, _w, segmentKey, _x, _y, targetKey, history, localeDirections, duplicateResult, duplicatesByLocale, index, evaluatedMessageExamplesByKey, evaluatedLocaleExamplesByKey, _loop_1, _z, localeKeys_1, localeKey, translationShards, _loop_2, _0, messageKeys_2, messageKey, _1, _2, _3, prefix, messageMap, shardData, _4, _5, _6, msgKey, valueSet, _7, attributeKeys_1, attributeKey, attribute, sourceFileInfo, detail, _8, segmentKeys_2, segmentKey, segment, usedAttributes, sourceFileInfo, detail, _9, targetKeys_2, targetKey, target, targetLocaleKeys, formatsByLocale, formatRowsByLocale, _10, targetLocaleKeys_2, localeKey, sourceFileInfo, detail, _11, _12, type;
819
- var _13, _14;
820
- return __generator(this, function (_15) {
821
- switch (_15.label) {
902
+ var outputDirectoryPath, setStartedAt, entitiesStartedAt, _a, localeKeys, messageKeys, attributeKeys, segmentKeys, targetKeys, _b, locales, messages, attributes, segments, targets, relationshipsStartedAt, messageTargets, targetMessages, localeTargets, attributeTargets, segmentTargets, attributesUsedInSegments, attributesUsedInMessages, segmentsUsedInMessages, _i, targetKeys_1, targetKey, targetLocaleKeys, _c, targetLocaleKeys_1, localeKey, _d, _e, messageKey, _f, segmentKeys_1, segmentKey, usedAttributes, _g, _h, attributeKey, _j, messageKeys_1, messageKey, message, targetsForMessage, _k, _l, override, usedAttributes, usedSegments, _m, _o, attributeKey, _p, targetsForMessage_1, targetKey, _q, _r, segmentKey, _s, targetsForMessage_2, targetKey, _t, _u, attributeKey, _v, _w, segmentKey, _x, _y, targetKey, history, localeDirections, duplicateResult, duplicatesByLocale, index, historyStartedAt, examplesStartedAt, evaluatedMessageExamplesByKey, evaluatedLocaleExamplesByKey, localesStartedAt, _loop_1, _z, localeKeys_1, localeKey, duplicatesStartedAt, _0, localeKeys_2, localeKey, translationShards, messagesStartedAt, _loop_2, _1, messageKeys_2, messageKey, translationSearchStartedAt, _2, messageKeys_3, messageKey, message, _3, localeKeys_3, localeKey, row, _4, _5, override, overrideRow, _6, _7, _8, prefix, messageMap, shardData, _9, _10, _11, msgKey, valueSet, attributesStartedAt, _12, attributeKeys_1, attributeKey, attribute, sourceFileInfo, detail, segmentsStartedAt, _13, segmentKeys_2, segmentKey, segment, usedAttributes, sourceFileInfo, detail, targetsStartedAt, _14, targetKeys_2, targetKey, target, targetLocaleKeys, formatsByLocale, formatRowsByLocale, _15, targetLocaleKeys_2, localeKey, sourceFileInfo, detail, indexStartedAt, _16, _17, type;
903
+ var _18, _19;
904
+ return __generator(this, function (_20) {
905
+ switch (_20.label) {
822
906
  case 0:
823
907
  outputDirectoryPath = path.join(context.dataDirectoryPath, outputRelativeDirectory);
908
+ setStartedAt = context.progress.setStart(set);
909
+ entitiesStartedAt = context.progress.step("Processing entities");
824
910
  return [4 /*yield*/, Promise.all([
825
911
  datasource.listLocales(),
826
912
  datasource.listMessages(),
@@ -829,7 +915,7 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
829
915
  datasource.listTargets(),
830
916
  ])];
831
917
  case 1:
832
- _a = _15.sent(), localeKeys = _a[0], messageKeys = _a[1], attributeKeys = _a[2], segmentKeys = _a[3], targetKeys = _a[4];
918
+ _a = _20.sent(), localeKeys = _a[0], messageKeys = _a[1], attributeKeys = _a[2], segmentKeys = _a[3], targetKeys = _a[4];
833
919
  return [4 /*yield*/, Promise.all([
834
920
  readAll(localeKeys, function (key) { return datasource.readLocale(key); }),
835
921
  readAll(messageKeys, function (key) { return datasource.readMessage(key); }),
@@ -838,7 +924,15 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
838
924
  readAll(targetKeys, function (key) { return datasource.readTarget(key); }),
839
925
  ])];
840
926
  case 2:
841
- _b = _15.sent(), locales = _b[0], messages = _b[1], attributes = _b[2], segments = _b[3], targets = _b[4];
927
+ _b = _20.sent(), locales = _b[0], messages = _b[1], attributes = _b[2], segments = _b[3], targets = _b[4];
928
+ context.progress.done(entitiesStartedAt, "(".concat([
929
+ pluralize(localeKeys.length, "locale"),
930
+ pluralize(messageKeys.length, "message"),
931
+ pluralize(attributeKeys.length, "attribute"),
932
+ pluralize(segmentKeys.length, "segment"),
933
+ pluralize(targetKeys.length, "target"),
934
+ ].join(", "), ")"));
935
+ relationshipsStartedAt = context.progress.step("Mapping relationships");
842
936
  messageTargets = {};
843
937
  targetMessages = {};
844
938
  localeTargets = {};
@@ -850,7 +944,7 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
850
944
  for (_i = 0, targetKeys_1 = targetKeys; _i < targetKeys_1.length; _i++) {
851
945
  targetKey = targetKeys_1[_i];
852
946
  targetMessages[targetKey] = getTargetMessageKeys(targets[targetKey], messageKeys);
853
- targetLocaleKeys = ((_13 = targets[targetKey].locales) === null || _13 === void 0 ? void 0 : _13.length)
947
+ targetLocaleKeys = ((_18 = targets[targetKey].locales) === null || _18 === void 0 ? void 0 : _18.length)
854
948
  ? targets[targetKey].locales
855
949
  : localeKeys;
856
950
  for (_c = 0, targetLocaleKeys_1 = targetLocaleKeys; _c < targetLocaleKeys_1.length; _c++) {
@@ -933,6 +1027,7 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
933
1027
  }
934
1028
  }
935
1029
  }
1030
+ context.progress.done(relationshipsStartedAt);
936
1031
  history = set ? context.historyIndex.bySet[set] || [] : context.historyIndex.entries;
937
1032
  localeDirections = getLocaleDirections(locales);
938
1033
  duplicateResult = context.duplicateResultsBySet[getDuplicateSetKey(set)] || {
@@ -957,14 +1052,17 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
957
1052
  target: [],
958
1053
  },
959
1054
  };
1055
+ historyStartedAt = context.progress.step("Writing history pages");
960
1056
  return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history"), history)];
961
1057
  case 3:
962
- _15.sent();
1058
+ _20.sent();
1059
+ context.progress.done(historyStartedAt, "(".concat(pluralize(history.length, "entry", "entries"), ")"));
1060
+ examplesStartedAt = context.progress.step("Evaluating examples");
963
1061
  return [4 /*yield*/, context.runtime.resolveExamples(projectConfig, datasource, {
964
1062
  onlyMessages: true,
965
1063
  })];
966
1064
  case 4:
967
- evaluatedMessageExamplesByKey = (_15.sent()).messages.reduce(function (accumulator, example) {
1065
+ evaluatedMessageExamplesByKey = (_20.sent()).messages.reduce(function (accumulator, example) {
968
1066
  if (!accumulator[example.message]) {
969
1067
  accumulator[example.message] = [];
970
1068
  }
@@ -975,7 +1073,7 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
975
1073
  onlyLocales: true,
976
1074
  })];
977
1075
  case 5:
978
- evaluatedLocaleExamplesByKey = (_15.sent()).locales.reduce(function (accumulator, example) {
1076
+ evaluatedLocaleExamplesByKey = (_20.sent()).locales.reduce(function (accumulator, example) {
979
1077
  var _a;
980
1078
  var originalTranslation = example.message
981
1079
  ? resolveTranslationRow((_a = messages[example.message]) === null || _a === void 0 ? void 0 : _a.translations, example.locale, locales)
@@ -987,10 +1085,12 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
987
1085
  accumulator[example.locale].push(__assign(__assign({}, example), { originalTranslation: originalTranslation || undefined }));
988
1086
  return accumulator;
989
1087
  }, {});
1088
+ context.progress.done(examplesStartedAt, "(".concat(pluralize(Object.values(evaluatedMessageExamplesByKey).reduce(function (total, items) { return total + items.length; }, 0), "message example"), ", ").concat(pluralize(Object.values(evaluatedLocaleExamplesByKey).reduce(function (total, items) { return total + items.length; }, 0), "locale example"), ")"));
1089
+ localesStartedAt = context.progress.step("Writing locales");
990
1090
  _loop_1 = function (localeKey) {
991
1091
  var locale, sourceFileInfo, detail;
992
- return __generator(this, function (_16) {
993
- switch (_16.label) {
1092
+ return __generator(this, function (_21) {
1093
+ switch (_21.label) {
994
1094
  case 0:
995
1095
  locale = locales[localeKey];
996
1096
  sourceFileInfo = getSourceFileInfo(context.repositoryRootDirectoryPath, context.rootDirectoryPath, projectConfig, "locale", localeKey);
@@ -1015,35 +1115,52 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
1015
1115
  }));
1016
1116
  return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "entities", "locale", "".concat(encodeKey(localeKey), ".json")), detail)];
1017
1117
  case 1:
1018
- _16.sent();
1019
- return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "duplicates", "locales", "".concat(encodeKey(localeKey), ".json")), toLocaleDuplicatesFile(localeKey, duplicatesByLocale))];
1020
- case 2:
1021
- _16.sent();
1118
+ _21.sent();
1022
1119
  return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history", "locale", encodeKey(localeKey)), getHistoryForEntity(context.historyIndex, "locale", localeKey, set || undefined))];
1023
- case 3:
1024
- _16.sent();
1120
+ case 2:
1121
+ _21.sent();
1025
1122
  return [2 /*return*/];
1026
1123
  }
1027
1124
  });
1028
1125
  };
1029
1126
  _z = 0, localeKeys_1 = localeKeys;
1030
- _15.label = 6;
1127
+ _20.label = 6;
1031
1128
  case 6:
1032
1129
  if (!(_z < localeKeys_1.length)) return [3 /*break*/, 9];
1033
1130
  localeKey = localeKeys_1[_z];
1034
1131
  return [5 /*yield**/, _loop_1(localeKey)];
1035
1132
  case 7:
1036
- _15.sent();
1037
- _15.label = 8;
1133
+ _20.sent();
1134
+ _20.label = 8;
1038
1135
  case 8:
1039
1136
  _z++;
1040
1137
  return [3 /*break*/, 6];
1041
1138
  case 9:
1139
+ context.progress.done(localesStartedAt, "(".concat(pluralize(localeKeys.length, "locale"), ")"));
1140
+ if (!context.withDuplicates) return [3 /*break*/, 14];
1141
+ duplicatesStartedAt = context.progress.step("Writing duplicate reports");
1142
+ _0 = 0, localeKeys_2 = localeKeys;
1143
+ _20.label = 10;
1144
+ case 10:
1145
+ if (!(_0 < localeKeys_2.length)) return [3 /*break*/, 13];
1146
+ localeKey = localeKeys_2[_0];
1147
+ return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "duplicates", "locales", "".concat(encodeKey(localeKey), ".json")), toLocaleDuplicatesFile(localeKey, duplicatesByLocale))];
1148
+ case 11:
1149
+ _20.sent();
1150
+ _20.label = 12;
1151
+ case 12:
1152
+ _0++;
1153
+ return [3 /*break*/, 10];
1154
+ case 13:
1155
+ context.progress.done(duplicatesStartedAt, "(".concat(pluralize(localeKeys.length, "locale"), ")"));
1156
+ _20.label = 14;
1157
+ case 14:
1042
1158
  translationShards = {};
1159
+ messagesStartedAt = context.progress.step("Writing messages");
1043
1160
  _loop_2 = function (messageKey) {
1044
- var message, overrides, sourceFileInfo, detail, directLocales, overrideLocalesSet, _17, overrides_1, override, _18, _19, lk, overrideLocalesList, _20, localeKeys_2, localeKey, row, _21, overrides_2, override, overrideRow;
1045
- return __generator(this, function (_22) {
1046
- switch (_22.label) {
1161
+ var message, overrides, sourceFileInfo, detail, directLocales, overrideLocalesSet, _22, overrides_1, override, _23, _24, lk, overrideLocalesList;
1162
+ return __generator(this, function (_25) {
1163
+ switch (_25.label) {
1047
1164
  case 0:
1048
1165
  message = messages[messageKey];
1049
1166
  overrides = (message.overrides || []).map(function (override) {
@@ -1077,79 +1194,86 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
1077
1194
  };
1078
1195
  directLocales = localeKeys.filter(function (lk) { return message.translations && typeof message.translations[lk] === "string"; });
1079
1196
  overrideLocalesSet = new Set();
1080
- for (_17 = 0, overrides_1 = overrides; _17 < overrides_1.length; _17++) {
1081
- override = overrides_1[_17];
1082
- for (_18 = 0, _19 = Object.keys(override.translations || {}); _18 < _19.length; _18++) {
1083
- lk = _19[_18];
1197
+ for (_22 = 0, overrides_1 = overrides; _22 < overrides_1.length; _22++) {
1198
+ override = overrides_1[_22];
1199
+ for (_23 = 0, _24 = Object.keys(override.translations || {}); _23 < _24.length; _23++) {
1200
+ lk = _24[_23];
1084
1201
  overrideLocalesSet.add(lk);
1085
1202
  }
1086
1203
  }
1087
1204
  overrideLocalesList = sortStrings(Array.from(overrideLocalesSet));
1088
- if (context.withTranslationSearch) {
1089
- // Build translation shards (direct + inherited + override, all locales combined)
1090
- for (_20 = 0, localeKeys_2 = localeKeys; _20 < localeKeys_2.length; _20++) {
1091
- localeKey = localeKeys_2[_20];
1092
- row = resolveTranslationRow(message.translations, localeKey, locales);
1093
- if (row.source !== "missing" && row.value) {
1094
- addToTranslationShard(messageKey, row.value);
1095
- }
1096
- for (_21 = 0, overrides_2 = overrides; _21 < overrides_2.length; _21++) {
1097
- override = overrides_2[_21];
1098
- overrideRow = resolveTranslationRow(override.translations, localeKey, locales);
1099
- if (overrideRow.source !== "missing" && overrideRow.value) {
1100
- addToTranslationShard(messageKey, overrideRow.value);
1101
- }
1102
- }
1103
- }
1104
- }
1105
1205
  index.entities.message.push(getEntitySummary(message, "message", messageKey, context.historyIndex, set || undefined, __assign(__assign({ targets: sortStrings(messageTargets[messageKey] || []) }, (directLocales.length > 0 ? { locales: sortStrings(directLocales) } : {})), (overrideLocalesList.length > 0 ? { overrideLocales: overrideLocalesList } : {}))));
1106
1206
  return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "entities", "message", "".concat(encodeKey(messageKey), ".json")), detail)];
1107
1207
  case 1:
1108
- _22.sent();
1208
+ _25.sent();
1109
1209
  return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history", "message", encodeKey(messageKey)), getHistoryForEntity(context.historyIndex, "message", messageKey, set || undefined))];
1110
1210
  case 2:
1111
- _22.sent();
1211
+ _25.sent();
1112
1212
  return [2 /*return*/];
1113
1213
  }
1114
1214
  });
1115
1215
  };
1116
- _0 = 0, messageKeys_2 = messageKeys;
1117
- _15.label = 10;
1118
- case 10:
1119
- if (!(_0 < messageKeys_2.length)) return [3 /*break*/, 13];
1120
- messageKey = messageKeys_2[_0];
1216
+ _1 = 0, messageKeys_2 = messageKeys;
1217
+ _20.label = 15;
1218
+ case 15:
1219
+ if (!(_1 < messageKeys_2.length)) return [3 /*break*/, 18];
1220
+ messageKey = messageKeys_2[_1];
1121
1221
  return [5 /*yield**/, _loop_2(messageKey)];
1122
- case 11:
1123
- _15.sent();
1124
- _15.label = 12;
1125
- case 12:
1126
- _0++;
1127
- return [3 /*break*/, 10];
1128
- case 13:
1129
- if (!context.withTranslationSearch) return [3 /*break*/, 17];
1130
- _1 = 0, _2 = Object.entries(translationShards);
1131
- _15.label = 14;
1132
- case 14:
1133
- if (!(_1 < _2.length)) return [3 /*break*/, 17];
1134
- _3 = _2[_1], prefix = _3[0], messageMap = _3[1];
1222
+ case 16:
1223
+ _20.sent();
1224
+ _20.label = 17;
1225
+ case 17:
1226
+ _1++;
1227
+ return [3 /*break*/, 15];
1228
+ case 18:
1229
+ context.progress.done(messagesStartedAt, "(".concat(pluralize(messageKeys.length, "message"), ")"));
1230
+ if (!context.withTranslationSearch) return [3 /*break*/, 23];
1231
+ translationSearchStartedAt = context.progress.step("Building translation search shards");
1232
+ for (_2 = 0, messageKeys_3 = messageKeys; _2 < messageKeys_3.length; _2++) {
1233
+ messageKey = messageKeys_3[_2];
1234
+ message = messages[messageKey];
1235
+ for (_3 = 0, localeKeys_3 = localeKeys; _3 < localeKeys_3.length; _3++) {
1236
+ localeKey = localeKeys_3[_3];
1237
+ row = resolveTranslationRow(message.translations, localeKey, locales);
1238
+ if (row.source !== "missing" && row.value) {
1239
+ addToTranslationShard(messageKey, row.value);
1240
+ }
1241
+ for (_4 = 0, _5 = message.overrides || []; _4 < _5.length; _4++) {
1242
+ override = _5[_4];
1243
+ overrideRow = resolveTranslationRow(override.translations, localeKey, locales);
1244
+ if (overrideRow.source !== "missing" && overrideRow.value) {
1245
+ addToTranslationShard(messageKey, overrideRow.value);
1246
+ }
1247
+ }
1248
+ }
1249
+ }
1250
+ _6 = 0, _7 = Object.entries(translationShards);
1251
+ _20.label = 19;
1252
+ case 19:
1253
+ if (!(_6 < _7.length)) return [3 /*break*/, 22];
1254
+ _8 = _7[_6], prefix = _8[0], messageMap = _8[1];
1135
1255
  shardData = {};
1136
- for (_4 = 0, _5 = Object.entries(messageMap); _4 < _5.length; _4++) {
1137
- _6 = _5[_4], msgKey = _6[0], valueSet = _6[1];
1256
+ for (_9 = 0, _10 = Object.entries(messageMap); _9 < _10.length; _9++) {
1257
+ _11 = _10[_9], msgKey = _11[0], valueSet = _11[1];
1138
1258
  shardData[msgKey] = Array.from(valueSet);
1139
1259
  }
1140
1260
  return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "translations", "".concat(prefix, ".json")), shardData)];
1141
- case 15:
1142
- _15.sent();
1143
- _15.label = 16;
1144
- case 16:
1145
- _1++;
1146
- return [3 /*break*/, 14];
1147
- case 17:
1148
- _7 = 0, attributeKeys_1 = attributeKeys;
1149
- _15.label = 18;
1150
- case 18:
1151
- if (!(_7 < attributeKeys_1.length)) return [3 /*break*/, 22];
1152
- attributeKey = attributeKeys_1[_7];
1261
+ case 20:
1262
+ _20.sent();
1263
+ _20.label = 21;
1264
+ case 21:
1265
+ _6++;
1266
+ return [3 /*break*/, 19];
1267
+ case 22:
1268
+ context.progress.done(translationSearchStartedAt, "(".concat(pluralize(Object.keys(translationShards).length, "shard"), ")"));
1269
+ _20.label = 23;
1270
+ case 23:
1271
+ attributesStartedAt = context.progress.step("Writing attributes");
1272
+ _12 = 0, attributeKeys_1 = attributeKeys;
1273
+ _20.label = 24;
1274
+ case 24:
1275
+ if (!(_12 < attributeKeys_1.length)) return [3 /*break*/, 28];
1276
+ attributeKey = attributeKeys_1[_12];
1153
1277
  attribute = attributes[attributeKey];
1154
1278
  sourceFileInfo = getSourceFileInfo(context.repositoryRootDirectoryPath, context.rootDirectoryPath, projectConfig, "attribute", attributeKey);
1155
1279
  detail = {
@@ -1168,21 +1292,23 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
1168
1292
  targets: sortStrings(Array.from(attributeTargets[attributeKey] || [])),
1169
1293
  }));
1170
1294
  return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "entities", "attribute", "".concat(encodeKey(attributeKey), ".json")), detail)];
1171
- case 19:
1172
- _15.sent();
1295
+ case 25:
1296
+ _20.sent();
1173
1297
  return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history", "attribute", encodeKey(attributeKey)), getHistoryForEntity(context.historyIndex, "attribute", attributeKey, set || undefined))];
1174
- case 20:
1175
- _15.sent();
1176
- _15.label = 21;
1177
- case 21:
1178
- _7++;
1179
- return [3 /*break*/, 18];
1180
- case 22:
1181
- _8 = 0, segmentKeys_2 = segmentKeys;
1182
- _15.label = 23;
1183
- case 23:
1184
- if (!(_8 < segmentKeys_2.length)) return [3 /*break*/, 27];
1185
- segmentKey = segmentKeys_2[_8];
1298
+ case 26:
1299
+ _20.sent();
1300
+ _20.label = 27;
1301
+ case 27:
1302
+ _12++;
1303
+ return [3 /*break*/, 24];
1304
+ case 28:
1305
+ context.progress.done(attributesStartedAt, "(".concat(pluralize(attributeKeys.length, "attribute"), ")"));
1306
+ segmentsStartedAt = context.progress.step("Writing segments");
1307
+ _13 = 0, segmentKeys_2 = segmentKeys;
1308
+ _20.label = 29;
1309
+ case 29:
1310
+ if (!(_13 < segmentKeys_2.length)) return [3 /*break*/, 33];
1311
+ segmentKey = segmentKeys_2[_13];
1186
1312
  segment = segments[segmentKey];
1187
1313
  usedAttributes = new Set();
1188
1314
  collectAttributeKeysFromConditions(segment.conditions, usedAttributes);
@@ -1203,27 +1329,29 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
1203
1329
  targets: sortStrings(Array.from(segmentTargets[segmentKey] || [])),
1204
1330
  }));
1205
1331
  return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "entities", "segment", "".concat(encodeKey(segmentKey), ".json")), detail)];
1206
- case 24:
1207
- _15.sent();
1332
+ case 30:
1333
+ _20.sent();
1208
1334
  return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history", "segment", encodeKey(segmentKey)), getHistoryForEntity(context.historyIndex, "segment", segmentKey, set || undefined))];
1209
- case 25:
1210
- _15.sent();
1211
- _15.label = 26;
1212
- case 26:
1213
- _8++;
1214
- return [3 /*break*/, 23];
1215
- case 27:
1216
- _9 = 0, targetKeys_2 = targetKeys;
1217
- _15.label = 28;
1218
- case 28:
1219
- if (!(_9 < targetKeys_2.length)) return [3 /*break*/, 32];
1220
- targetKey = targetKeys_2[_9];
1335
+ case 31:
1336
+ _20.sent();
1337
+ _20.label = 32;
1338
+ case 32:
1339
+ _13++;
1340
+ return [3 /*break*/, 29];
1341
+ case 33:
1342
+ context.progress.done(segmentsStartedAt, "(".concat(pluralize(segmentKeys.length, "segment"), ")"));
1343
+ targetsStartedAt = context.progress.step("Writing targets");
1344
+ _14 = 0, targetKeys_2 = targetKeys;
1345
+ _20.label = 34;
1346
+ case 34:
1347
+ if (!(_14 < targetKeys_2.length)) return [3 /*break*/, 38];
1348
+ targetKey = targetKeys_2[_14];
1221
1349
  target = targets[targetKey];
1222
- targetLocaleKeys = ((_14 = target.locales) === null || _14 === void 0 ? void 0 : _14.length) ? target.locales : localeKeys;
1350
+ targetLocaleKeys = ((_19 = target.locales) === null || _19 === void 0 ? void 0 : _19.length) ? target.locales : localeKeys;
1223
1351
  formatsByLocale = {};
1224
1352
  formatRowsByLocale = {};
1225
- for (_10 = 0, targetLocaleKeys_2 = targetLocaleKeys; _10 < targetLocaleKeys_2.length; _10++) {
1226
- localeKey = targetLocaleKeys_2[_10];
1353
+ for (_15 = 0, targetLocaleKeys_2 = targetLocaleKeys; _15 < targetLocaleKeys_2.length; _15++) {
1354
+ localeKey = targetLocaleKeys_2[_15];
1227
1355
  formatsByLocale[localeKey] = context.runtime.resolveFormats(localeKey, locales, target);
1228
1356
  formatRowsByLocale[localeKey] = getFormatRows(context.runtime, localeKey, locales, target);
1229
1357
  }
@@ -1244,23 +1372,27 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
1244
1372
  messageCount: targetMessages[targetKey].length,
1245
1373
  }));
1246
1374
  return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "entities", "target", "".concat(encodeKey(targetKey), ".json")), detail)];
1247
- case 29:
1248
- _15.sent();
1375
+ case 35:
1376
+ _20.sent();
1249
1377
  return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history", "target", encodeKey(targetKey)), getHistoryForEntity(context.historyIndex, "target", targetKey, set || undefined))];
1250
- case 30:
1251
- _15.sent();
1252
- _15.label = 31;
1253
- case 31:
1254
- _9++;
1255
- return [3 /*break*/, 28];
1256
- case 32:
1257
- for (_11 = 0, _12 = Object.keys(index.entities); _11 < _12.length; _11++) {
1258
- type = _12[_11];
1378
+ case 36:
1379
+ _20.sent();
1380
+ _20.label = 37;
1381
+ case 37:
1382
+ _14++;
1383
+ return [3 /*break*/, 34];
1384
+ case 38:
1385
+ context.progress.done(targetsStartedAt, "(".concat(pluralize(targetKeys.length, "target"), ")"));
1386
+ indexStartedAt = context.progress.step("Writing catalog index");
1387
+ for (_16 = 0, _17 = Object.keys(index.entities); _16 < _17.length; _16++) {
1388
+ type = _17[_16];
1259
1389
  index.entities[type].sort(function (a, b) { return a.key.localeCompare(b.key); });
1260
1390
  }
1261
1391
  return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "index.json"), index)];
1262
- case 33:
1263
- _15.sent();
1392
+ case 39:
1393
+ _20.sent();
1394
+ context.progress.done(indexStartedAt);
1395
+ context.progress.done(setStartedAt, "total");
1264
1396
  return [2 /*return*/, index];
1265
1397
  }
1266
1398
  });
@@ -1292,36 +1424,64 @@ function copyCatalogAssets(outputDirectoryPath) {
1292
1424
  }
1293
1425
  function exportCatalog(runtime_1, rootDirectoryPath_1, projectConfig_1, datasource_1) {
1294
1426
  return __awaiter(this, arguments, void 0, function (runtime, rootDirectoryPath, projectConfig, datasource, options) {
1295
- var outputDirectoryPath, dataDirectoryPath, withTranslationSearch, devEditors, historyIndex, duplicateTranslations, duplicateResultsBySet, context, executions, setIndexes, _i, executions_1, execution, outputRelativeDirectory, _a, _b, manifest;
1427
+ var outputDirectoryPath, dataDirectoryPath, withTranslationSearch, withDuplicates, progress, stepStartedAt, devEditors, historyIndex, links, duplicateResultsBySet, _a, _b, context, executions, setIndexes, _i, executions_1, execution, outputRelativeDirectory, _c, _d, manifest;
1296
1428
  if (options === void 0) { options = {}; }
1297
- return __generator(this, function (_c) {
1298
- switch (_c.label) {
1429
+ return __generator(this, function (_e) {
1430
+ switch (_e.label) {
1299
1431
  case 0:
1300
1432
  outputDirectoryPath = options.outDir
1301
1433
  ? path.resolve(rootDirectoryPath, options.outDir)
1302
1434
  : projectConfig.catalogDirectoryPath;
1303
1435
  dataDirectoryPath = path.join(outputDirectoryPath, "data");
1304
1436
  withTranslationSearch = options.withTranslationSearch === true;
1437
+ withDuplicates = options.withDuplicates === true;
1438
+ progress = new CatalogProgressReporter(rootDirectoryPath, outputDirectoryPath);
1439
+ progress.start({
1440
+ browserRouter: options.browserRouter !== false,
1441
+ sets: projectConfig.sets === true,
1442
+ features: __spreadArray(__spreadArray([], (withTranslationSearch ? ["translation search"] : []), true), (withDuplicates ? ["duplicates"] : []), true),
1443
+ });
1444
+ stepStartedAt = progress.step("Preparing output directory");
1305
1445
  return [4 /*yield*/, fs.promises.rm(outputDirectoryPath, { recursive: true, force: true })];
1306
1446
  case 1:
1307
- _c.sent();
1447
+ _e.sent();
1308
1448
  return [4 /*yield*/, fs.promises.mkdir(dataDirectoryPath, { recursive: true })];
1309
1449
  case 2:
1310
- _c.sent();
1450
+ _e.sent();
1451
+ progress.done(stepStartedAt);
1311
1452
  if (!(options.copyAssets !== false)) return [3 /*break*/, 4];
1453
+ stepStartedAt = progress.step("Copying Catalog UI assets");
1312
1454
  return [4 /*yield*/, copyCatalogAssets(outputDirectoryPath)];
1313
1455
  case 3:
1314
- _c.sent();
1315
- _c.label = 4;
1456
+ _e.sent();
1457
+ progress.done(stepStartedAt);
1458
+ _e.label = 4;
1316
1459
  case 4:
1317
1460
  devEditors = options.dev ? options.devEditors || detectDevEditors() : [];
1461
+ stepStartedAt = progress.step("Reading Git history");
1318
1462
  return [4 /*yield*/, getGitHistoryIndex(rootDirectoryPath, projectConfig)];
1319
1463
  case 5:
1320
- historyIndex = _c.sent();
1464
+ historyIndex = _e.sent();
1465
+ progress.done(stepStartedAt, "(".concat(pluralize(historyIndex.entries.length, "commit"), ")"));
1466
+ stepStartedAt = progress.step("Resolving repository links");
1467
+ links = getRepoLinks(rootDirectoryPath);
1468
+ progress.done(stepStartedAt);
1469
+ duplicateResultsBySet = {};
1470
+ if (!withDuplicates) return [3 /*break*/, 7];
1471
+ stepStartedAt = progress.step("Scanning duplicate translations");
1472
+ _b = (_a = Object).fromEntries;
1321
1473
  return [4 /*yield*/, runtime.findDuplicateTranslations(projectConfig, datasource)];
1322
1474
  case 6:
1323
- duplicateTranslations = _c.sent();
1324
- duplicateResultsBySet = Object.fromEntries(duplicateTranslations.results.map(function (result) { return [getDuplicateSetKey(result.set), result]; }));
1475
+ duplicateResultsBySet = _b.apply(_a, [(_e.sent()).results.map(function (result) { return [
1476
+ getDuplicateSetKey(result.set),
1477
+ result,
1478
+ ]; })]);
1479
+ progress.done(stepStartedAt, "(".concat(pluralize(Object.values(duplicateResultsBySet).reduce(function (total, result) {
1480
+ return total +
1481
+ result.locales.reduce(function (localeTotal, localeResult) { return localeTotal + localeResult.duplicateValues.length; }, 0);
1482
+ }, 0), "duplicate value"), ")"));
1483
+ _e.label = 7;
1484
+ case 7:
1325
1485
  context = {
1326
1486
  rootDirectoryPath: rootDirectoryPath,
1327
1487
  repositoryRootDirectoryPath: getRepositoryRootDirectoryPath(rootDirectoryPath),
@@ -1332,30 +1492,39 @@ function exportCatalog(runtime_1, rootDirectoryPath_1, projectConfig_1, datasour
1332
1492
  devEditors: devEditors,
1333
1493
  duplicateResultsBySet: duplicateResultsBySet,
1334
1494
  withTranslationSearch: withTranslationSearch,
1495
+ withDuplicates: withDuplicates,
1496
+ progress: progress,
1335
1497
  };
1498
+ stepStartedAt = progress.step("Discovering project sets");
1336
1499
  return [4 /*yield*/, runtime.getProjectSetExecutions(projectConfig, datasource)];
1337
- case 7:
1338
- executions = _c.sent();
1500
+ case 8:
1501
+ executions = _e.sent();
1502
+ progress.done(stepStartedAt, projectConfig.sets
1503
+ ? "(".concat(executions.map(function (execution) { return execution.set; }).join(", ") || "none", ")")
1504
+ : "(root)");
1339
1505
  setIndexes = {};
1506
+ stepStartedAt = progress.step("Writing project history");
1340
1507
  return [4 /*yield*/, writeHistoryPages(path.join(dataDirectoryPath, "project", "history"), historyIndex.entries)];
1341
- case 8:
1342
- _c.sent();
1343
- _i = 0, executions_1 = executions;
1344
- _c.label = 9;
1345
1508
  case 9:
1346
- if (!(_i < executions_1.length)) return [3 /*break*/, 12];
1509
+ _e.sent();
1510
+ progress.done(stepStartedAt, "(".concat(pluralize(historyIndex.entries.length, "entry", "entries"), ")"));
1511
+ _i = 0, executions_1 = executions;
1512
+ _e.label = 10;
1513
+ case 10:
1514
+ if (!(_i < executions_1.length)) return [3 /*break*/, 13];
1347
1515
  execution = executions_1[_i];
1348
1516
  outputRelativeDirectory = projectConfig.sets ? path.join("sets", execution.set) : "root";
1349
- _a = setIndexes;
1350
- _b = execution.set || "root";
1517
+ _c = setIndexes;
1518
+ _d = execution.set || "root";
1351
1519
  return [4 /*yield*/, buildSetCatalog(context, execution.set, execution.projectConfig, execution.datasource, outputRelativeDirectory)];
1352
- case 10:
1353
- _a[_b] = _c.sent();
1354
- _c.label = 11;
1355
1520
  case 11:
1356
- _i++;
1357
- return [3 /*break*/, 9];
1521
+ _c[_d] = _e.sent();
1522
+ _e.label = 12;
1358
1523
  case 12:
1524
+ _i++;
1525
+ return [3 /*break*/, 10];
1526
+ case 13:
1527
+ stepStartedAt = progress.step("Writing manifest");
1359
1528
  manifest = {
1360
1529
  schemaVersion: exports.CATALOG_SCHEMA_VERSION,
1361
1530
  generatedAt: new Date().toISOString(),
@@ -1365,8 +1534,9 @@ function exportCatalog(runtime_1, rootDirectoryPath_1, projectConfig_1, datasour
1365
1534
  dev: options.dev ? { editors: devEditors } : undefined,
1366
1535
  features: {
1367
1536
  translationSearch: withTranslationSearch,
1537
+ duplicates: withDuplicates,
1368
1538
  },
1369
- links: getRepoLinks(rootDirectoryPath),
1539
+ links: links,
1370
1540
  paths: {
1371
1541
  projectHistory: "data/project/history/page-1.json",
1372
1542
  root: projectConfig.sets ? undefined : "data/root/index.json",
@@ -1380,9 +1550,10 @@ function exportCatalog(runtime_1, rootDirectoryPath_1, projectConfig_1, datasour
1380
1550
  counts: Object.fromEntries(Object.keys(setIndexes).map(function (key) { return [key, setIndexes[key].counts]; })),
1381
1551
  };
1382
1552
  return [4 /*yield*/, writeJson(path.join(dataDirectoryPath, "manifest.json"), manifest)];
1383
- case 13:
1384
- _c.sent();
1385
- console.log("Catalog exported to ".concat(outputDirectoryPath));
1553
+ case 14:
1554
+ _e.sent();
1555
+ progress.done(stepStartedAt);
1556
+ progress.complete();
1386
1557
  return [2 /*return*/, {
1387
1558
  outputDirectoryPath: outputDirectoryPath,
1388
1559
  manifest: manifest,
@@ -1616,13 +1787,16 @@ function createCatalogApi(runtime) {
1616
1787
  function isWithTranslationSearchEnabled(parsed) {
1617
1788
  return parsed.withTranslationSearch === true || parsed["with-translation-search"] === true;
1618
1789
  }
1790
+ function isWithDuplicatesEnabled(parsed) {
1791
+ return parsed.withDuplicates === true || parsed["with-duplicates"] === true;
1792
+ }
1619
1793
  function createCatalogPlugin(runtime, api) {
1620
1794
  var _this = this;
1621
1795
  if (api === void 0) { api = createCatalogApi(runtime); }
1622
1796
  return {
1623
1797
  command: "catalog [subcommand]",
1624
1798
  handler: function (_a) { return __awaiter(_this, [_a], void 0, function (_b) {
1625
- var allowedSubcommands, browserRouter, withTranslationSearch, server_1, outputDirectoryPath, ignoredDirectoryPaths, exportInFlight_1, exportQueued_1, queuedReason_1, debounceTimer_1, runExportAndReload_1, stopWatchingProject;
1799
+ var allowedSubcommands, browserRouter, withTranslationSearch, withDuplicates, server_1, outputDirectoryPath, ignoredDirectoryPaths, exportInFlight_1, exportQueued_1, queuedReason_1, debounceTimer_1, runExportAndReload_1, stopWatchingProject;
1626
1800
  var _this = this;
1627
1801
  var rootDirectoryPath = _b.rootDirectoryPath, projectConfig = _b.projectConfig, datasource = _b.datasource, parsed = _b.parsed;
1628
1802
  return __generator(this, function (_c) {
@@ -1631,6 +1805,7 @@ function createCatalogPlugin(runtime, api) {
1631
1805
  allowedSubcommands = ["export", "serve"];
1632
1806
  browserRouter = !(parsed.hashRouter || parsed["hash-router"]);
1633
1807
  withTranslationSearch = isWithTranslationSearchEnabled(parsed);
1808
+ withDuplicates = isWithDuplicatesEnabled(parsed);
1634
1809
  if (!!parsed.subcommand) return [3 /*break*/, 3];
1635
1810
  return [4 /*yield*/, api.exportCatalog(rootDirectoryPath, projectConfig, datasource, {
1636
1811
  outDir: parsed.outDir,
@@ -1638,6 +1813,7 @@ function createCatalogPlugin(runtime, api) {
1638
1813
  browserRouter: browserRouter,
1639
1814
  dev: true,
1640
1815
  withTranslationSearch: withTranslationSearch,
1816
+ withDuplicates: withDuplicates,
1641
1817
  })];
1642
1818
  case 1:
1643
1819
  _c.sent();
@@ -1687,6 +1863,7 @@ function createCatalogPlugin(runtime, api) {
1687
1863
  browserRouter: browserRouter,
1688
1864
  dev: true,
1689
1865
  withTranslationSearch: withTranslationSearch,
1866
+ withDuplicates: withDuplicates,
1690
1867
  })];
1691
1868
  case 2:
1692
1869
  _a.sent();
@@ -1733,6 +1910,7 @@ function createCatalogPlugin(runtime, api) {
1733
1910
  copyAssets: !parsed.noAssets,
1734
1911
  browserRouter: browserRouter,
1735
1912
  withTranslationSearch: withTranslationSearch,
1913
+ withDuplicates: withDuplicates,
1736
1914
  })];
1737
1915
  case 4:
1738
1916
  _c.sent();