lingo.dev 0.84.0 → 0.85.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/cli.cjs CHANGED
@@ -3088,10 +3088,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3088
3088
  createAndroidLoader(),
3089
3089
  createFlatLoader(),
3090
3090
  createSyncLoader(),
3091
- createUnlocalizableLoader(
3092
- options.isCacheRestore,
3093
- options.returnUnlocalizedKeys
3094
- )
3091
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3095
3092
  );
3096
3093
  case "csv":
3097
3094
  return composeLoaders(
@@ -3099,10 +3096,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3099
3096
  createCsvLoader(),
3100
3097
  createFlatLoader(),
3101
3098
  createSyncLoader(),
3102
- createUnlocalizableLoader(
3103
- options.isCacheRestore,
3104
- options.returnUnlocalizedKeys
3105
- )
3099
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3106
3100
  );
3107
3101
  case "html":
3108
3102
  return composeLoaders(
@@ -3110,10 +3104,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3110
3104
  createPrettierLoader({ parser: "html", bucketPathPattern }),
3111
3105
  createHtmlLoader(),
3112
3106
  createSyncLoader(),
3113
- createUnlocalizableLoader(
3114
- options.isCacheRestore,
3115
- options.returnUnlocalizedKeys
3116
- )
3107
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3117
3108
  );
3118
3109
  case "json":
3119
3110
  return composeLoaders(
@@ -3124,10 +3115,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3124
3115
  createFlatLoader(),
3125
3116
  createLockedKeysLoader(lockedKeys || [], options.isCacheRestore),
3126
3117
  createSyncLoader(),
3127
- createUnlocalizableLoader(
3128
- options.isCacheRestore,
3129
- options.returnUnlocalizedKeys
3130
- )
3118
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3131
3119
  );
3132
3120
  case "markdown":
3133
3121
  return composeLoaders(
@@ -3135,10 +3123,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3135
3123
  createPrettierLoader({ parser: "markdown", bucketPathPattern }),
3136
3124
  createMarkdownLoader(),
3137
3125
  createSyncLoader(),
3138
- createUnlocalizableLoader(
3139
- options.isCacheRestore,
3140
- options.returnUnlocalizedKeys
3141
- )
3126
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3142
3127
  );
3143
3128
  case "mdx":
3144
3129
  return composeLoaders(
@@ -3149,19 +3134,6 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3149
3134
  createFlatLoader(),
3150
3135
  createMdxStructureLoader(),
3151
3136
  createSyncLoader(),
3152
- createUnlocalizableLoader(
3153
- options.isCacheRestore,
3154
- options.returnUnlocalizedKeys
3155
- )
3156
- );
3157
- case "mdx":
3158
- return composeLoaders(
3159
- createTextFileLoader(bucketPathPattern),
3160
- createPrettierLoader({ parser: "mdx", bucketPathPattern }),
3161
- createMdxFormatLoader(),
3162
- createFlatLoader(),
3163
- createMdxStructureLoader(),
3164
- createSyncLoader(),
3165
3137
  createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3166
3138
  );
3167
3139
  case "po":
@@ -3171,30 +3143,21 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3171
3143
  createFlatLoader(),
3172
3144
  createSyncLoader(),
3173
3145
  createVariableLoader({ type: "python" }),
3174
- createUnlocalizableLoader(
3175
- options.isCacheRestore,
3176
- options.returnUnlocalizedKeys
3177
- )
3146
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3178
3147
  );
3179
3148
  case "properties":
3180
3149
  return composeLoaders(
3181
3150
  createTextFileLoader(bucketPathPattern),
3182
3151
  createPropertiesLoader(),
3183
3152
  createSyncLoader(),
3184
- createUnlocalizableLoader(
3185
- options.isCacheRestore,
3186
- options.returnUnlocalizedKeys
3187
- )
3153
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3188
3154
  );
3189
3155
  case "xcode-strings":
3190
3156
  return composeLoaders(
3191
3157
  createTextFileLoader(bucketPathPattern),
3192
3158
  createXcodeStringsLoader(),
3193
3159
  createSyncLoader(),
3194
- createUnlocalizableLoader(
3195
- options.isCacheRestore,
3196
- options.returnUnlocalizedKeys
3197
- )
3160
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3198
3161
  );
3199
3162
  case "xcode-stringsdict":
3200
3163
  return composeLoaders(
@@ -3202,10 +3165,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3202
3165
  createXcodeStringsdictLoader(),
3203
3166
  createFlatLoader(),
3204
3167
  createSyncLoader(),
3205
- createUnlocalizableLoader(
3206
- options.isCacheRestore,
3207
- options.returnUnlocalizedKeys
3208
- )
3168
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3209
3169
  );
3210
3170
  case "xcode-xcstrings":
3211
3171
  return composeLoaders(
@@ -3216,10 +3176,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3216
3176
  createFlatLoader(),
3217
3177
  createSyncLoader(),
3218
3178
  createVariableLoader({ type: "ieee" }),
3219
- createUnlocalizableLoader(
3220
- options.isCacheRestore,
3221
- options.returnUnlocalizedKeys
3222
- )
3179
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3223
3180
  );
3224
3181
  case "yaml":
3225
3182
  return composeLoaders(
@@ -3229,10 +3186,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3229
3186
  createFlatLoader(),
3230
3187
  createLockedKeysLoader(lockedKeys || [], options.isCacheRestore),
3231
3188
  createSyncLoader(),
3232
- createUnlocalizableLoader(
3233
- options.isCacheRestore,
3234
- options.returnUnlocalizedKeys
3235
- )
3189
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3236
3190
  );
3237
3191
  case "yaml-root-key":
3238
3192
  return composeLoaders(
@@ -3242,10 +3196,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3242
3196
  createRootKeyLoader(true),
3243
3197
  createFlatLoader(),
3244
3198
  createSyncLoader(),
3245
- createUnlocalizableLoader(
3246
- options.isCacheRestore,
3247
- options.returnUnlocalizedKeys
3248
- )
3199
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3249
3200
  );
3250
3201
  case "flutter":
3251
3202
  return composeLoaders(
@@ -3255,10 +3206,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3255
3206
  createFlutterLoader(),
3256
3207
  createFlatLoader(),
3257
3208
  createSyncLoader(),
3258
- createUnlocalizableLoader(
3259
- options.isCacheRestore,
3260
- options.returnUnlocalizedKeys
3261
- )
3209
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3262
3210
  );
3263
3211
  case "xliff":
3264
3212
  return composeLoaders(
@@ -3266,10 +3214,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3266
3214
  createXliffLoader(),
3267
3215
  createFlatLoader(),
3268
3216
  createSyncLoader(),
3269
- createUnlocalizableLoader(
3270
- options.isCacheRestore,
3271
- options.returnUnlocalizedKeys
3272
- )
3217
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3273
3218
  );
3274
3219
  case "xml":
3275
3220
  return composeLoaders(
@@ -3277,40 +3222,28 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3277
3222
  createXmlLoader(),
3278
3223
  createFlatLoader(),
3279
3224
  createSyncLoader(),
3280
- createUnlocalizableLoader(
3281
- options.isCacheRestore,
3282
- options.returnUnlocalizedKeys
3283
- )
3225
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3284
3226
  );
3285
3227
  case "srt":
3286
3228
  return composeLoaders(
3287
3229
  createTextFileLoader(bucketPathPattern),
3288
3230
  createSrtLoader(),
3289
3231
  createSyncLoader(),
3290
- createUnlocalizableLoader(
3291
- options.isCacheRestore,
3292
- options.returnUnlocalizedKeys
3293
- )
3232
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3294
3233
  );
3295
3234
  case "dato":
3296
3235
  return composeLoaders(
3297
3236
  createDatoLoader(bucketPathPattern),
3298
3237
  createSyncLoader(),
3299
3238
  createFlatLoader(),
3300
- createUnlocalizableLoader(
3301
- options.isCacheRestore,
3302
- options.returnUnlocalizedKeys
3303
- )
3239
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3304
3240
  );
3305
3241
  case "vtt":
3306
3242
  return composeLoaders(
3307
3243
  createTextFileLoader(bucketPathPattern),
3308
3244
  createVttLoader(),
3309
3245
  createSyncLoader(),
3310
- createUnlocalizableLoader(
3311
- options.isCacheRestore,
3312
- options.returnUnlocalizedKeys
3313
- )
3246
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3314
3247
  );
3315
3248
  case "php":
3316
3249
  return composeLoaders(
@@ -3318,10 +3251,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3318
3251
  createPhpLoader(),
3319
3252
  createSyncLoader(),
3320
3253
  createFlatLoader(),
3321
- createUnlocalizableLoader(
3322
- options.isCacheRestore,
3323
- options.returnUnlocalizedKeys
3324
- )
3254
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3325
3255
  );
3326
3256
  case "vue-json":
3327
3257
  return composeLoaders(
@@ -3329,10 +3259,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
3329
3259
  createVueJsonLoader(),
3330
3260
  createSyncLoader(),
3331
3261
  createFlatLoader(),
3332
- createUnlocalizableLoader(
3333
- options.isCacheRestore,
3334
- options.returnUnlocalizedKeys
3335
- )
3262
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3336
3263
  );
3337
3264
  }
3338
3265
  }
@@ -5111,10 +5038,427 @@ var ci_default = new (0, _interactivecommander.Command)().command("ci").descript
5111
5038
  main();
5112
5039
  });
5113
5040
 
5041
+ // src/cli/cmd/status.ts
5042
+
5043
+
5044
+
5045
+
5046
+
5047
+ var _clitable3 = require('cli-table3'); var _clitable32 = _interopRequireDefault(_clitable3);
5048
+ var status_default = new (0, _interactivecommander.Command)().command("status").description("Show the status of the localization process").helpOption("-h, --help", "Show help").option("--locale <locale>", "Locale to process", (val, prev) => prev ? [...prev, val] : [val]).option("--bucket <bucket>", "Bucket to process", (val, prev) => prev ? [...prev, val] : [val]).option(
5049
+ "--file [files...]",
5050
+ "File to process. Process only a specific path, may contain asterisk * to match multiple files."
5051
+ ).option("--force", "Ignore lockfile and process all keys, useful for estimating full re-translation").option("--verbose", "Show detailed output including key-level word counts").option("--api-key <api-key>", "Explicitly set the API key to use, override the default API key from settings").action(async function(options) {
5052
+ const ora = _ora2.default.call(void 0, );
5053
+ const flags = parseFlags2(options);
5054
+ let authId = null;
5055
+ try {
5056
+ ora.start("Loading configuration...");
5057
+ const i18nConfig = getConfig();
5058
+ const settings = getSettings(flags.apiKey);
5059
+ ora.succeed("Configuration loaded");
5060
+ try {
5061
+ ora.start("Checking authentication status...");
5062
+ const auth = await tryAuthenticate(settings);
5063
+ if (auth) {
5064
+ authId = auth.id;
5065
+ ora.succeed(`Authenticated as ${auth.email}`);
5066
+ } else {
5067
+ ora.info(
5068
+ "Not authenticated. Continuing without authentication. (Run `lingo.dev auth --login` to authenticate)"
5069
+ );
5070
+ }
5071
+ } catch (error) {
5072
+ ora.info("Authentication failed. Continuing without authentication.");
5073
+ }
5074
+ ora.start("Validating localization configuration...");
5075
+ validateParams2(i18nConfig, flags);
5076
+ ora.succeed("Localization configuration is valid");
5077
+ trackEvent(authId || "status", "cmd.status.start", {
5078
+ i18nConfig,
5079
+ flags
5080
+ });
5081
+ let buckets = getBuckets(i18nConfig);
5082
+ if (_optionalChain([flags, 'access', _179 => _179.bucket, 'optionalAccess', _180 => _180.length])) {
5083
+ buckets = buckets.filter((bucket) => flags.bucket.includes(bucket.type));
5084
+ }
5085
+ ora.succeed("Buckets retrieved");
5086
+ if (_optionalChain([flags, 'access', _181 => _181.file, 'optionalAccess', _182 => _182.length])) {
5087
+ buckets = buckets.map((bucket) => {
5088
+ const paths = bucket.paths.filter((path18) => flags.file.find((file) => _optionalChain([path18, 'access', _183 => _183.pathPattern, 'optionalAccess', _184 => _184.match, 'call', _185 => _185(file)])));
5089
+ return { ...bucket, paths };
5090
+ }).filter((bucket) => bucket.paths.length > 0);
5091
+ if (buckets.length === 0) {
5092
+ ora.fail("No buckets found. All buckets were filtered out by --file option.");
5093
+ process.exit(1);
5094
+ } else {
5095
+ ora.info(`\x1B[36mProcessing only filtered buckets:\x1B[0m`);
5096
+ buckets.map((bucket) => {
5097
+ ora.info(` ${bucket.type}:`);
5098
+ bucket.paths.forEach((path18) => {
5099
+ ora.info(` - ${path18.pathPattern}`);
5100
+ });
5101
+ });
5102
+ }
5103
+ }
5104
+ const targetLocales = _optionalChain([flags, 'access', _186 => _186.locale, 'optionalAccess', _187 => _187.length]) ? flags.locale : i18nConfig.locale.targets;
5105
+ let totalSourceKeyCount = 0;
5106
+ let uniqueKeysToTranslate = 0;
5107
+ let totalExistingTranslations = 0;
5108
+ const totalWordCount = /* @__PURE__ */ new Map();
5109
+ const languageStats = {};
5110
+ for (const locale of targetLocales) {
5111
+ languageStats[locale] = {
5112
+ complete: 0,
5113
+ missing: 0,
5114
+ updated: 0,
5115
+ words: 0
5116
+ };
5117
+ totalWordCount.set(locale, 0);
5118
+ }
5119
+ const fileStats = {};
5120
+ for (const bucket of buckets) {
5121
+ try {
5122
+ console.log();
5123
+ ora.info(`Analyzing bucket: ${bucket.type}`);
5124
+ for (const bucketPath of bucket.paths) {
5125
+ const bucketOra = _ora2.default.call(void 0, { indent: 2 }).info(`Analyzing path: ${bucketPath.pathPattern}`);
5126
+ const sourceLocale = __spec.resolveOverriddenLocale.call(void 0, i18nConfig.locale.source, bucketPath.delimiter);
5127
+ const bucketLoader = createBucketLoader(
5128
+ bucket.type,
5129
+ bucketPath.pathPattern,
5130
+ {
5131
+ isCacheRestore: false,
5132
+ defaultLocale: sourceLocale,
5133
+ injectLocale: bucket.injectLocale
5134
+ },
5135
+ bucket.lockedKeys
5136
+ );
5137
+ bucketLoader.setDefaultLocale(sourceLocale);
5138
+ await bucketLoader.init();
5139
+ const filePath = bucketPath.pathPattern;
5140
+ if (!fileStats[filePath]) {
5141
+ fileStats[filePath] = {
5142
+ path: filePath,
5143
+ sourceKeys: 0,
5144
+ wordCount: 0,
5145
+ languageStats: {}
5146
+ };
5147
+ for (const locale of targetLocales) {
5148
+ fileStats[filePath].languageStats[locale] = {
5149
+ complete: 0,
5150
+ missing: 0,
5151
+ updated: 0,
5152
+ words: 0
5153
+ };
5154
+ }
5155
+ }
5156
+ const sourceData = await bucketLoader.pull(sourceLocale);
5157
+ const sourceKeys = Object.keys(sourceData);
5158
+ fileStats[filePath].sourceKeys = sourceKeys.length;
5159
+ totalSourceKeyCount += sourceKeys.length;
5160
+ let sourceWordCount = 0;
5161
+ for (const key of sourceKeys) {
5162
+ const value = sourceData[key];
5163
+ if (typeof value === "string") {
5164
+ const words = value.trim().split(/\s+/).length;
5165
+ sourceWordCount += words;
5166
+ }
5167
+ }
5168
+ fileStats[filePath].wordCount = sourceWordCount;
5169
+ for (const _targetLocale of targetLocales) {
5170
+ const targetLocale = __spec.resolveOverriddenLocale.call(void 0, _targetLocale, bucketPath.delimiter);
5171
+ bucketOra.start(`[${sourceLocale} -> ${targetLocale}] Analyzing translation status...`);
5172
+ let targetData = {};
5173
+ let fileExists = true;
5174
+ try {
5175
+ targetData = await bucketLoader.pull(targetLocale);
5176
+ } catch (error) {
5177
+ fileExists = false;
5178
+ bucketOra.info(
5179
+ `[${sourceLocale} -> ${targetLocale}] Target file not found, assuming all keys need translation.`
5180
+ );
5181
+ }
5182
+ if (!fileExists) {
5183
+ fileStats[filePath].languageStats[targetLocale].missing = sourceKeys.length;
5184
+ fileStats[filePath].languageStats[targetLocale].words = sourceWordCount;
5185
+ languageStats[targetLocale].missing += sourceKeys.length;
5186
+ languageStats[targetLocale].words += sourceWordCount;
5187
+ totalWordCount.set(targetLocale, (totalWordCount.get(targetLocale) || 0) + sourceWordCount);
5188
+ bucketOra.succeed(
5189
+ `[${sourceLocale} -> ${targetLocale}] ${_chalk2.default.red(`0% complete`)} (0/${sourceKeys.length} keys) - file not found`
5190
+ );
5191
+ continue;
5192
+ }
5193
+ const deltaProcessor = createDeltaProcessor(bucketPath.pathPattern);
5194
+ const checksums = await deltaProcessor.loadChecksums();
5195
+ const delta = await deltaProcessor.calculateDelta({
5196
+ sourceData,
5197
+ targetData,
5198
+ checksums
5199
+ });
5200
+ const missingKeys = delta.added;
5201
+ const updatedKeys = delta.updated;
5202
+ const completeKeys = sourceKeys.filter((key) => !missingKeys.includes(key) && !updatedKeys.includes(key));
5203
+ let wordsToTranslate = 0;
5204
+ const keysToProcess = flags.force ? sourceKeys : [...missingKeys, ...updatedKeys];
5205
+ for (const key of keysToProcess) {
5206
+ const value = sourceData[String(key)];
5207
+ if (typeof value === "string") {
5208
+ const words = value.trim().split(/\s+/).length;
5209
+ wordsToTranslate += words;
5210
+ }
5211
+ }
5212
+ fileStats[filePath].languageStats[targetLocale].missing = missingKeys.length;
5213
+ fileStats[filePath].languageStats[targetLocale].updated = updatedKeys.length;
5214
+ fileStats[filePath].languageStats[targetLocale].complete = completeKeys.length;
5215
+ fileStats[filePath].languageStats[targetLocale].words = wordsToTranslate;
5216
+ languageStats[targetLocale].missing += missingKeys.length;
5217
+ languageStats[targetLocale].updated += updatedKeys.length;
5218
+ languageStats[targetLocale].complete += completeKeys.length;
5219
+ languageStats[targetLocale].words += wordsToTranslate;
5220
+ totalWordCount.set(targetLocale, (totalWordCount.get(targetLocale) || 0) + wordsToTranslate);
5221
+ const totalKeysInFile = sourceKeys.length;
5222
+ const completionPercent = (completeKeys.length / totalKeysInFile * 100).toFixed(1);
5223
+ if (missingKeys.length === 0 && updatedKeys.length === 0) {
5224
+ bucketOra.succeed(
5225
+ `[${sourceLocale} -> ${targetLocale}] ${_chalk2.default.green(`100% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`
5226
+ );
5227
+ } else {
5228
+ const message = `[${sourceLocale} -> ${targetLocale}] ${parseFloat(completionPercent) > 50 ? _chalk2.default.yellow(`${completionPercent}% complete`) : _chalk2.default.red(`${completionPercent}% complete`)} (${completeKeys.length}/${totalKeysInFile} keys)`;
5229
+ bucketOra.succeed(message);
5230
+ if (flags.verbose) {
5231
+ if (missingKeys.length > 0) {
5232
+ console.log(` ${_chalk2.default.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`);
5233
+ console.log(
5234
+ ` ${_chalk2.default.dim(`Example missing: ${missingKeys.slice(0, 2).join(", ")}${missingKeys.length > 2 ? "..." : ""}`)}`
5235
+ );
5236
+ }
5237
+ if (updatedKeys.length > 0) {
5238
+ console.log(` ${_chalk2.default.yellow(`Updated:`)} ${updatedKeys.length} keys that changed in source`);
5239
+ }
5240
+ }
5241
+ }
5242
+ }
5243
+ }
5244
+ } catch (error) {
5245
+ ora.fail(`Failed to analyze bucket ${bucket.type}: ${error.message}`);
5246
+ }
5247
+ }
5248
+ const totalKeysNeedingTranslation = Object.values(languageStats).reduce((sum, stats) => {
5249
+ return sum + stats.missing + stats.updated;
5250
+ }, 0);
5251
+ const totalCompletedKeys = totalSourceKeyCount - totalKeysNeedingTranslation / targetLocales.length;
5252
+ console.log();
5253
+ ora.succeed(_chalk2.default.green(`Localization status completed.`));
5254
+ console.log(_chalk2.default.bold.cyan(`
5255
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`));
5256
+ console.log(_chalk2.default.bold.cyan(`\u2551 LOCALIZATION STATUS REPORT \u2551`));
5257
+ console.log(_chalk2.default.bold.cyan(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
5258
+ console.log(_chalk2.default.bold(`
5259
+ \u{1F4DD} SOURCE CONTENT:`));
5260
+ console.log(`\u2022 Source language: ${_chalk2.default.green(i18nConfig.locale.source)}`);
5261
+ console.log(`\u2022 Source keys: ${_chalk2.default.yellow(totalSourceKeyCount.toString())} keys across all files`);
5262
+ console.log(_chalk2.default.bold(`
5263
+ \u{1F310} LANGUAGE BY LANGUAGE BREAKDOWN:`));
5264
+ const table = new (0, _clitable32.default)({
5265
+ head: ["Language", "Status", "Complete", "Missing", "Updated", "Total Keys", "Words to Translate"],
5266
+ style: {
5267
+ head: ["white"],
5268
+ // White color for headers
5269
+ border: []
5270
+ // No color for borders
5271
+ },
5272
+ colWidths: [12, 20, 18, 12, 12, 12, 15]
5273
+ // Explicit column widths, making Status column wider
5274
+ });
5275
+ let totalWordsToTranslate = 0;
5276
+ for (const locale of targetLocales) {
5277
+ const stats = languageStats[locale];
5278
+ const percentComplete = (stats.complete / totalSourceKeyCount * 100).toFixed(1);
5279
+ const totalNeeded = stats.missing + stats.updated;
5280
+ let statusText;
5281
+ let statusColor;
5282
+ if (stats.missing === totalSourceKeyCount) {
5283
+ statusText = "\u{1F534} Not started";
5284
+ statusColor = _chalk2.default.red;
5285
+ } else if (stats.missing === 0 && stats.updated === 0) {
5286
+ statusText = "\u2705 Complete";
5287
+ statusColor = _chalk2.default.green;
5288
+ } else if (parseFloat(percentComplete) > 80) {
5289
+ statusText = "\u{1F7E1} Almost done";
5290
+ statusColor = _chalk2.default.yellow;
5291
+ } else if (parseFloat(percentComplete) > 0) {
5292
+ statusText = "\u{1F7E0} In progress";
5293
+ statusColor = _chalk2.default.yellow;
5294
+ } else {
5295
+ statusText = "\u{1F534} Not started";
5296
+ statusColor = _chalk2.default.red;
5297
+ }
5298
+ const words = totalWordCount.get(locale) || 0;
5299
+ totalWordsToTranslate += words;
5300
+ table.push([
5301
+ locale,
5302
+ statusColor(statusText),
5303
+ `${stats.complete}/${totalSourceKeyCount} (${percentComplete}%)`,
5304
+ stats.missing > 0 ? _chalk2.default.red(stats.missing.toString()) : "0",
5305
+ stats.updated > 0 ? _chalk2.default.yellow(stats.updated.toString()) : "0",
5306
+ totalNeeded > 0 ? _chalk2.default.magenta(totalNeeded.toString()) : "0",
5307
+ words > 0 ? `~${words.toLocaleString()}` : "0"
5308
+ ]);
5309
+ }
5310
+ console.log(table.toString());
5311
+ console.log(_chalk2.default.bold(`
5312
+ \u{1F4CA} USAGE ESTIMATE:`));
5313
+ console.log(
5314
+ `\u2022 WORDS TO BE CONSUMED: ~${_chalk2.default.yellow.bold(totalWordsToTranslate.toLocaleString())} words across all languages`
5315
+ );
5316
+ console.log(` (Words are counted from source language for keys that need translation in target languages)`);
5317
+ if (targetLocales.length > 1) {
5318
+ console.log(`\u2022 Per-language breakdown:`);
5319
+ for (const locale of targetLocales) {
5320
+ const words = totalWordCount.get(locale) || 0;
5321
+ const percent = (words / totalWordsToTranslate * 100).toFixed(1);
5322
+ console.log(` - ${locale}: ~${words.toLocaleString()} words (${percent}% of total)`);
5323
+ }
5324
+ }
5325
+ if (flags.confirm && Object.keys(fileStats).length > 0) {
5326
+ console.log(_chalk2.default.bold(`
5327
+ \u{1F4D1} BREAKDOWN BY FILE:`));
5328
+ Object.entries(fileStats).sort((a, b) => b[1].wordCount - a[1].wordCount).forEach(([path18, stats]) => {
5329
+ if (stats.sourceKeys === 0) return;
5330
+ console.log(_chalk2.default.bold(`
5331
+ \u2022 ${path18}:`));
5332
+ console.log(` ${stats.sourceKeys} source keys, ~${stats.wordCount.toLocaleString()} source words`);
5333
+ const fileTable = new (0, _clitable32.default)({
5334
+ head: ["Language", "Status", "Details"],
5335
+ style: {
5336
+ head: ["white"],
5337
+ border: []
5338
+ },
5339
+ colWidths: [12, 20, 50]
5340
+ // Explicit column widths for file detail table
5341
+ });
5342
+ for (const locale of targetLocales) {
5343
+ const langStats = stats.languageStats[locale];
5344
+ const complete = langStats.complete;
5345
+ const total = stats.sourceKeys;
5346
+ const completion = (complete / total * 100).toFixed(1);
5347
+ let status = "\u2705 Complete";
5348
+ let statusColor = _chalk2.default.green;
5349
+ if (langStats.missing === total) {
5350
+ status = "\u274C Not started";
5351
+ statusColor = _chalk2.default.red;
5352
+ } else if (langStats.missing > 0 || langStats.updated > 0) {
5353
+ status = `\u26A0\uFE0F ${completion}% complete`;
5354
+ statusColor = _chalk2.default.yellow;
5355
+ }
5356
+ let details = "";
5357
+ if (langStats.missing > 0 || langStats.updated > 0) {
5358
+ const parts = [];
5359
+ if (langStats.missing > 0) parts.push(`${langStats.missing} missing`);
5360
+ if (langStats.updated > 0) parts.push(`${langStats.updated} changed`);
5361
+ details = `${parts.join(", ")}, ~${langStats.words} words`;
5362
+ } else {
5363
+ details = "All keys translated";
5364
+ }
5365
+ fileTable.push([locale, statusColor(status), details]);
5366
+ }
5367
+ console.log(fileTable.toString());
5368
+ });
5369
+ }
5370
+ const completeLanguages = targetLocales.filter(
5371
+ (locale) => languageStats[locale].missing === 0 && languageStats[locale].updated === 0
5372
+ );
5373
+ const missingLanguages = targetLocales.filter((locale) => languageStats[locale].complete === 0);
5374
+ console.log(_chalk2.default.bold.green(`
5375
+ \u{1F4A1} OPTIMIZATION TIPS:`));
5376
+ if (missingLanguages.length > 0) {
5377
+ console.log(
5378
+ `\u2022 ${_chalk2.default.yellow(missingLanguages.join(", "))} ${missingLanguages.length === 1 ? "has" : "have"} no translations yet`
5379
+ );
5380
+ }
5381
+ if (completeLanguages.length > 0) {
5382
+ console.log(
5383
+ `\u2022 ${_chalk2.default.green(completeLanguages.join(", "))} ${completeLanguages.length === 1 ? "is" : "are"} completely translated`
5384
+ );
5385
+ }
5386
+ if (targetLocales.length > 1) {
5387
+ console.log(`\u2022 Translating one language at a time reduces complexity`);
5388
+ console.log(`\u2022 Try 'lingo.dev@latest i18n --locale ${targetLocales[0]}' to process just one language`);
5389
+ }
5390
+ trackEvent(authId || "status", "cmd.status.success", {
5391
+ i18nConfig,
5392
+ flags,
5393
+ totalSourceKeyCount,
5394
+ languageStats,
5395
+ totalWordsToTranslate,
5396
+ authenticated: !!authId
5397
+ });
5398
+ } catch (error) {
5399
+ ora.fail(error.message);
5400
+ trackEvent(authId || "status", "cmd.status.error", {
5401
+ flags,
5402
+ error: error.message,
5403
+ authenticated: !!authId
5404
+ });
5405
+ process.exit(1);
5406
+ }
5407
+ });
5408
+ function parseFlags2(options) {
5409
+ return _zod2.default.object({
5410
+ locale: _zod2.default.array(__spec.localeCodeSchema).optional(),
5411
+ bucket: _zod2.default.array(__spec.bucketTypeSchema).optional(),
5412
+ force: _zod2.default.boolean().optional(),
5413
+ confirm: _zod2.default.boolean().optional(),
5414
+ verbose: _zod2.default.boolean().optional(),
5415
+ file: _zod2.default.array(_zod2.default.string()).optional(),
5416
+ apiKey: _zod2.default.string().optional()
5417
+ }).parse(options);
5418
+ }
5419
+ async function tryAuthenticate(settings) {
5420
+ if (!settings.auth.apiKey) {
5421
+ return null;
5422
+ }
5423
+ try {
5424
+ const authenticator = createAuthenticator({
5425
+ apiKey: settings.auth.apiKey,
5426
+ apiUrl: settings.auth.apiUrl
5427
+ });
5428
+ const user = await authenticator.whoami();
5429
+ return user;
5430
+ } catch (error) {
5431
+ return null;
5432
+ }
5433
+ }
5434
+ function validateParams2(i18nConfig, flags) {
5435
+ if (!i18nConfig) {
5436
+ throw new CLIError({
5437
+ message: "i18n.json not found. Please run `lingo.dev init` to initialize the project.",
5438
+ docUrl: "i18nNotFound"
5439
+ });
5440
+ } else if (!i18nConfig.buckets || !Object.keys(i18nConfig.buckets).length) {
5441
+ throw new CLIError({
5442
+ message: "No buckets found in i18n.json. Please add at least one bucket containing i18n content.",
5443
+ docUrl: "bucketNotFound"
5444
+ });
5445
+ } else if (_optionalChain([flags, 'access', _188 => _188.locale, 'optionalAccess', _189 => _189.some, 'call', _190 => _190((locale) => !i18nConfig.locale.targets.includes(locale))])) {
5446
+ throw new CLIError({
5447
+ message: `One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list and try again.`,
5448
+ docUrl: "localeTargetNotFound"
5449
+ });
5450
+ } else if (_optionalChain([flags, 'access', _191 => _191.bucket, 'optionalAccess', _192 => _192.some, 'call', _193 => _193((bucket) => !i18nConfig.buckets[bucket])])) {
5451
+ throw new CLIError({
5452
+ message: `One or more specified buckets do not exist in i18n.json. Please add them to the list and try again.`,
5453
+ docUrl: "bucketNotFound"
5454
+ });
5455
+ }
5456
+ }
5457
+
5114
5458
  // package.json
5115
5459
  var package_default = {
5116
5460
  name: "lingo.dev",
5117
- version: "0.84.0",
5461
+ version: "0.85.1",
5118
5462
  description: "Lingo.dev CLI",
5119
5463
  private: false,
5120
5464
  publishConfig: {
@@ -5184,6 +5528,7 @@ var package_default = {
5184
5528
  ai: "^4.3.2",
5185
5529
  bitbucket: "^2.12.0",
5186
5530
  chalk: "^5.4.1",
5531
+ "cli-table3": "^0.6.5",
5187
5532
  cors: "^2.8.5",
5188
5533
  "csv-parse": "^5.6.0",
5189
5534
  "csv-stringify": "^6.5.2",
@@ -5289,7 +5634,7 @@ ${_gradientstring.vice.call(void 0,
5289
5634
 
5290
5635
  Star the the repo :) https://github.com/LingoDotDev/lingo.dev
5291
5636
  `
5292
- ).version(`v${package_default.version}`, "-v, --version", "Show version").addCommand(init_default).interactive("-y, --no-interactive", "Disable interactive mode").addCommand(i18n_default).addCommand(auth_default).addCommand(show_default).addCommand(lockfile_default).addCommand(cleanup_default).addCommand(mcp_default).addCommand(ci_default).exitOverride((err) => {
5637
+ ).version(`v${package_default.version}`, "-v, --version", "Show version").addCommand(init_default).interactive("-y, --no-interactive", "Disable interactive mode").addCommand(i18n_default).addCommand(auth_default).addCommand(show_default).addCommand(lockfile_default).addCommand(cleanup_default).addCommand(mcp_default).addCommand(ci_default).addCommand(status_default).exitOverride((err) => {
5293
5638
  if (err.code === "commander.helpDisplayed" || err.code === "commander.version" || err.code === "commander.help") {
5294
5639
  process.exit(0);
5295
5640
  }