pabal-resource-mcp 1.5.4 → 1.5.5

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.
@@ -3432,6 +3432,87 @@ import { GoogleGenAI } from "@google/genai";
3432
3432
  import fs10 from "fs";
3433
3433
  import path10 from "path";
3434
3434
  import sharp from "sharp";
3435
+ var GEMINI_ASPECT_RATIOS = {
3436
+ "1:1": { ratio: 1 / 1, width: 2048, height: 2048 },
3437
+ "2:3": { ratio: 2 / 3, width: 1696, height: 2528 },
3438
+ "3:2": { ratio: 3 / 2, width: 2528, height: 1696 },
3439
+ "3:4": { ratio: 3 / 4, width: 1792, height: 2400 },
3440
+ "4:3": { ratio: 4 / 3, width: 2400, height: 1792 },
3441
+ "4:5": { ratio: 4 / 5, width: 1856, height: 2304 },
3442
+ "5:4": { ratio: 5 / 4, width: 2304, height: 1856 },
3443
+ "9:16": { ratio: 9 / 16, width: 1536, height: 2752 },
3444
+ "16:9": { ratio: 16 / 9, width: 2752, height: 1536 },
3445
+ "21:9": { ratio: 21 / 9, width: 1584, height: 672 }
3446
+ };
3447
+ var DEVICE_ASPECT_RATIOS = {
3448
+ phone: "9:16",
3449
+ tablet: "3:4"
3450
+ };
3451
+ var GEMINI_SUPPORTED_LOCALES = {
3452
+ // English variants
3453
+ "en": "EN",
3454
+ "en-US": "EN",
3455
+ "en-GB": "EN",
3456
+ "en-AU": "EN",
3457
+ "en-CA": "EN",
3458
+ // Arabic
3459
+ "ar": "ar-EG",
3460
+ "ar-EG": "ar-EG",
3461
+ "ar-SA": "ar-EG",
3462
+ // German
3463
+ "de": "de-DE",
3464
+ "de-DE": "de-DE",
3465
+ // Spanish
3466
+ "es": "es-MX",
3467
+ "es-MX": "es-MX",
3468
+ "es-ES": "es-MX",
3469
+ "es-419": "es-MX",
3470
+ // French
3471
+ "fr": "fr-FR",
3472
+ "fr-FR": "fr-FR",
3473
+ "fr-CA": "fr-FR",
3474
+ // Hindi
3475
+ "hi": "hi-IN",
3476
+ "hi-IN": "hi-IN",
3477
+ // Indonesian
3478
+ "id": "id-ID",
3479
+ "id-ID": "id-ID",
3480
+ // Italian
3481
+ "it": "it-IT",
3482
+ "it-IT": "it-IT",
3483
+ // Japanese
3484
+ "ja": "ja-JP",
3485
+ "ja-JP": "ja-JP",
3486
+ // Korean
3487
+ "ko": "ko-KR",
3488
+ "ko-KR": "ko-KR",
3489
+ // Portuguese
3490
+ "pt": "pt-BR",
3491
+ "pt-BR": "pt-BR",
3492
+ "pt-PT": "pt-BR",
3493
+ // Russian
3494
+ "ru": "ru-RU",
3495
+ "ru-RU": "ru-RU",
3496
+ // Ukrainian
3497
+ "uk": "ua-UA",
3498
+ "uk-UA": "ua-UA",
3499
+ "ua-UA": "ua-UA",
3500
+ // Vietnamese
3501
+ "vi": "vi-VN",
3502
+ "vi-VN": "vi-VN",
3503
+ // Chinese
3504
+ "zh": "zh-CN",
3505
+ "zh-CN": "zh-CN",
3506
+ "zh-Hans": "zh-CN",
3507
+ "zh-TW": "zh-CN",
3508
+ "zh-Hant": "zh-CN"
3509
+ };
3510
+ function isGeminiSupportedLocale(locale) {
3511
+ return locale in GEMINI_SUPPORTED_LOCALES;
3512
+ }
3513
+ function getUnsupportedLocales(locales) {
3514
+ return locales.filter((locale) => !isGeminiSupportedLocale(locale));
3515
+ }
3435
3516
  var LANGUAGE_NAMES = {
3436
3517
  "en-US": "English (US)",
3437
3518
  "en-GB": "English (UK)",
@@ -3507,29 +3588,15 @@ function readImageAsBase64(imagePath) {
3507
3588
  }
3508
3589
  return { data: base64, mimeType };
3509
3590
  }
3510
- async function getImageDimensions(imagePath) {
3511
- const metadata = await sharp(imagePath).metadata();
3512
- return {
3513
- width: metadata.width || 1080,
3514
- height: metadata.height || 1920
3515
- };
3516
- }
3517
- function calculateAspectRatio(width, height) {
3518
- const ratio = width / height;
3519
- if (Math.abs(ratio - 1) < 0.1) return "1:1";
3520
- if (Math.abs(ratio - 9 / 16) < 0.1) return "9:16";
3521
- if (Math.abs(ratio - 16 / 9) < 0.1) return "16:9";
3522
- if (Math.abs(ratio - 3 / 4) < 0.1) return "3:4";
3523
- if (Math.abs(ratio - 4 / 3) < 0.1) return "4:3";
3524
- return ratio < 1 ? "9:16" : "16:9";
3591
+ function getAspectRatioForDevice(deviceType) {
3592
+ return DEVICE_ASPECT_RATIOS[deviceType];
3525
3593
  }
3526
- async function translateImage(sourcePath, sourceLocale, targetLocale, outputPath, preserveWords) {
3594
+ async function translateImage(sourcePath, sourceLocale, targetLocale, outputPath, deviceType, preserveWords) {
3527
3595
  try {
3528
3596
  const client = getGeminiClient();
3529
3597
  const sourceLanguage = getLanguageName(sourceLocale);
3530
3598
  const targetLanguage = getLanguageName(targetLocale);
3531
- const { width, height } = await getImageDimensions(sourcePath);
3532
- const aspectRatio = calculateAspectRatio(width, height);
3599
+ const aspectRatio = getAspectRatioForDevice(deviceType);
3533
3600
  const { data: imageData, mimeType } = readImageAsBase64(sourcePath);
3534
3601
  const preserveInstruction = preserveWords && preserveWords.length > 0 ? `
3535
3602
  - Do NOT translate these words, keep them exactly as-is: ${preserveWords.join(", ")}` : "";
@@ -3629,6 +3696,7 @@ async function translateImagesWithProgress(translations, onProgress, preserveWor
3629
3696
  translation.sourceLocale,
3630
3697
  translation.targetLocale,
3631
3698
  translation.outputPath,
3699
+ translation.deviceType,
3632
3700
  preserveWords
3633
3701
  );
3634
3702
  if (result.success) {
@@ -3652,7 +3720,7 @@ async function translateImagesWithProgress(translations, onProgress, preserveWor
3652
3720
  // src/tools/aso/utils/localize-screenshots/image-resizer.util.ts
3653
3721
  import sharp2 from "sharp";
3654
3722
  import fs11 from "fs";
3655
- async function getImageDimensions2(imagePath) {
3723
+ async function getImageDimensions(imagePath) {
3656
3724
  const metadata = await sharp2(imagePath).metadata();
3657
3725
  if (!metadata.width || !metadata.height) {
3658
3726
  throw new Error(`Unable to read dimensions from ${imagePath}`);
@@ -3723,14 +3791,16 @@ async function resizeImage(inputPath, outputPath, targetDimensions) {
3723
3791
  // Preserve aspect ratio
3724
3792
  withoutEnlargement: false,
3725
3793
  // Allow enlargement if needed
3726
- background: bgColor
3794
+ background: bgColor,
3727
3795
  // Use detected edge color
3796
+ kernel: "lanczos3"
3797
+ // High-quality downscaling algorithm
3728
3798
  }).flatten({ background: bgColor }).png().toFile(outputPath + ".tmp");
3729
3799
  fs11.renameSync(outputPath + ".tmp", outputPath);
3730
3800
  }
3731
3801
  async function validateAndResizeImage(sourcePath, translatedPath) {
3732
- const sourceDimensions = await getImageDimensions2(sourcePath);
3733
- const translatedDimensions = await getImageDimensions2(translatedPath);
3802
+ const sourceDimensions = await getImageDimensions(sourcePath);
3803
+ const translatedDimensions = await getImageDimensions(translatedPath);
3734
3804
  const needsResize = sourceDimensions.width !== translatedDimensions.width || sourceDimensions.height !== translatedDimensions.height;
3735
3805
  if (needsResize) {
3736
3806
  await resizeImage(translatedPath, translatedPath, sourceDimensions);
@@ -3865,12 +3935,14 @@ function getTargetLocales(allLocales, primaryLocale, requestedTargets) {
3865
3935
  );
3866
3936
  if (invalidTargets.length > 0) {
3867
3937
  console.warn(
3868
- `Warning: Some requested locales are not supported: ${invalidTargets.join(", ")}`
3938
+ `Warning: Some requested locales are not in product: ${invalidTargets.join(", ")}`
3869
3939
  );
3870
3940
  }
3871
3941
  targets = validTargets.filter((t) => t !== primaryLocale);
3872
3942
  }
3873
- return targets;
3943
+ const skippedLocales = getUnsupportedLocales(targets);
3944
+ const supportedTargets = targets.filter((t) => isGeminiSupportedLocale(t));
3945
+ return { targets: supportedTargets, skippedLocales };
3874
3946
  }
3875
3947
  function buildTranslationTasks(slug, screenshots, primaryLocale, targetLocales, skipExisting) {
3876
3948
  const tasks = [];
@@ -3941,22 +4013,26 @@ async function handleLocalizeScreenshots(input) {
3941
4013
  ]
3942
4014
  };
3943
4015
  }
3944
- const targetLocales = getTargetLocales(
4016
+ const { targets: targetLocales, skippedLocales } = getTargetLocales(
3945
4017
  allLocales,
3946
4018
  primaryLocale,
3947
4019
  requestedTargetLocales
3948
4020
  );
3949
4021
  if (targetLocales.length === 0) {
4022
+ const skippedMsg = skippedLocales.length > 0 ? ` (Skipped due to Gemini limitation: ${skippedLocales.join(", ")})` : "";
3950
4023
  return {
3951
4024
  content: [
3952
4025
  {
3953
4026
  type: "text",
3954
- text: `\u274C No target locales to translate to. Primary locale: ${primaryLocale}, Available: ${allLocales.join(", ")}`
4027
+ text: `\u274C No target locales to translate to. Primary locale: ${primaryLocale}, Available: ${allLocales.join(", ")}${skippedMsg}`
3955
4028
  }
3956
4029
  ]
3957
4030
  };
3958
4031
  }
3959
4032
  results.push(`\u{1F3AF} Target locales: ${targetLocales.join(", ")}`);
4033
+ if (skippedLocales.length > 0) {
4034
+ results.push(`\u26A0\uFE0F Skipped locales (not supported by Gemini): ${skippedLocales.join(", ")}`);
4035
+ }
3960
4036
  const sourceScreenshots = scanLocaleScreenshots(appInfo.slug, primaryLocale);
3961
4037
  let filteredScreenshots = sourceScreenshots.filter(
3962
4038
  (s) => deviceTypes.includes(s.type)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pabal-resource-mcp",
3
- "version": "1.5.4",
3
+ "version": "1.5.5",
4
4
  "type": "module",
5
5
  "description": "MCP server for ASO data management with shared types and utilities",
6
6
  "author": "skyu",