Skip to content

fix: only load necessary translations#802

Merged
mkilpatrick merged 90 commits intomainfrom
separate-translations
Jan 12, 2026
Merged

fix: only load necessary translations#802
mkilpatrick merged 90 commits intomainfrom
separate-translations

Conversation

@mkilpatrick
Copy link
Collaborator

Before this change all translations were bundled and ultimately loaded on the frontend even though only a single locale is ever needed at a time.

benlife5
benlife5 previously approved these changes Oct 28, 2025
@mkilpatrick mkilpatrick added the create-dev-release Triggers dev release workflow label Oct 28, 2025
@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 28, 2025

commit: 3c55105

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 4, 2025

Walkthrough

Moves i18next scanner resource paths to locales/platform/{{lng}}/{{ns}}.json and splits translations into component and platform scopes (locales/components/ and locales/platform/). Adds many locale JSON resources. Introduces runtime translation loading and injection APIs (loadComponentTranslations, loadPlatformTranslations, injectTranslations) and updates consumers (Editor, VisualEditorProvider, LayoutHeader, Vite templates, starter) to preload/inject translations before changing language. Updates i18n tooling and scripts (generateTranslations, sortTranslations, fixPlurals, copy/delete scripts), adjusts Vite plugin hooks, and updates CI to run i18n:update.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Template as Vite template (transformProps)
  participant Injector as injectTranslations
  participant CompLoader as loadComponentTranslations
  participant PlatLoader as loadPlatformTranslations
  participant FS as File System (locales/components/, locales/platform/)
  participant i18nC as i18nComponentsInstance
  participant i18nP as i18nPlatformInstance
  participant Editor as Editor / VisualEditorProvider
  participant Renderer as Page Renderer

  Template->>Injector: invoke injectTranslations(data, locale)
  Injector->>CompLoader: request component translations for locale
  CompLoader->>FS: read locales/components/<locale>/visual-editor.json
  FS-->>CompLoader: return JSON (or fallback)
  CompLoader->>i18nC: addResourceBundle("components", locale, translations)
  Injector->>PlatLoader: request platform translations for locale
  PlatLoader->>FS: read locales/platform/<locale>/visual-editor.json
  FS-->>PlatLoader: return JSON (or fallback)
  PlatLoader->>i18nP: addResourceBundle("platform", locale, translations)
  Injector-->>Template: return augmented data with translations injected
  Template->>Editor: provide augmented templateProps (preloaded translations)
  Editor->>i18nC: changeLanguage(locale) / i18nP: changeLanguage(locale)
  Editor->>Renderer: render localized UI using i18nComponentsInstance & i18nPlatformInstance
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: optimizing translation loading to only load necessary translations instead of bundling all of them.
Description check ✅ Passed The PR description is directly related to the changeset, explaining the problem (all translations bundled and loaded) and the solution (only load necessary translations for active locale).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fd214b and 3c55105.

📒 Files selected for processing (1)
  • packages/visual-editor/locales/platform/ja/visual-editor.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: call_unit_test / unit_tests (22.x)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: call_unit_test / unit_tests (24.x)
  • GitHub Check: create-dev-release
  • GitHub Check: semgrep/ci
🔇 Additional comments (6)
packages/visual-editor/locales/platform/ja/visual-editor.json (6)

62-66: LGTM!

The translations are accurate and natural:

  • "閉じる" (close action) vs "閉店" (closed state) distinction is appropriate
  • "に閉まります" is correctly formatted for time concatenation patterns like {{time}}に閉まります

539-548: LGTM!

Good adoption of the {{mile}} token pattern for unit display. This approach:

  • Separates the numeric value from the unit for proper formatting
  • Uses the _other suffix appropriately for Japanese (which doesn't have grammatical plural forms)
  • "マイル" is the correct Japanese transliteration

560-560: Good translation fix.

"営業中" is the correct and natural Japanese term for "open now" in a business context. This is a significant improvement over the previous translation.


653-661: LGTM!

The social links translations are well-crafted:

  • Appropriately uses "フォローしてください" (please follow) for most platforms
  • Correctly uses "購読してください" (please subscribe) for YouTube, matching the platform's terminology
  • Platform names are preserved in their recognizable forms

668-668: Good translation improvement.

"一時休業中" is the natural Japanese expression for "temporarily closed" in a business context, improving upon the previous more literal translation.


576-608: LGTM!

Brand names (Apple, Galaxy Store, Uber Eats) are appropriately preserved in their original form, while generic terms like "ハート" (heart) and "再生" (play) are correctly translated to Japanese.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/visual-editor/src/utils/VisualEditorProvider.tsx (1)

44-52: Race condition: translations may not load before language change.

loadComponentTranslations is async but not awaited (line 45). When normalizedTemplateProps?.translations is undefined, the function will asynchronously fetch translations via getTranslations(), but i18nComponentsInstance.changeLanguage() executes immediately on line 49 without waiting. This can cause the language to change before translations are available, resulting in missing or fallback text.

Consider one of these solutions:

Option 1: Await inside useEffect

+ React.useEffect(() => {
    if (normalizedTemplateProps?.document?.locale) {
-     loadComponentTranslations(
+     loadComponentTranslations(
        normalizedTemplateProps.document.locale,
        normalizedTemplateProps?.translations
-     );
-     i18nComponentsInstance.changeLanguage(
-       normalizedTemplateProps.document.locale
-     );
+     ).then(() => {
+       i18nComponentsInstance.changeLanguage(
+         normalizedTemplateProps.document.locale
+       );
+     });
    }
+ }, [normalizedTemplateProps?.document?.locale]);

Option 2: Make the provider async-aware with state

+ const [translationsLoaded, setTranslationsLoaded] = React.useState(false);
+
+ React.useEffect(() => {
    if (normalizedTemplateProps?.document?.locale) {
-     loadComponentTranslations(
+     loadComponentTranslations(
        normalizedTemplateProps.document.locale,
        normalizedTemplateProps?.translations
-     );
-     i18nComponentsInstance.changeLanguage(
-       normalizedTemplateProps.document.locale
-     );
+     ).then(() => {
+       i18nComponentsInstance.changeLanguage(
+         normalizedTemplateProps.document.locale
+       );
+       setTranslationsLoaded(true);
+     });
    }
+ }, [normalizedTemplateProps?.document?.locale]);
🧹 Nitpick comments (5)
packages/visual-editor/src/utils/i18n/fallbacks.ts (1)

31-31: Good refactor: removed redundant double negation.

The truthiness check is cleaner without !! while preserving the same behavior.

starter/src/templates/dev.tsx (1)

131-135: Consider stronger typing for transformProps.

The use of TemplateProps<any> loses type safety. While this works, consider constraining the type parameter if the document structure is known, or at minimum using TemplateProps<unknown> to signal the uncertainty.

Example improvement:

-export const transformProps: TransformProps<TemplateProps<any>> = async (
+export const transformProps: TransformProps<TemplateProps<unknown>> = async (
   data
 ) => {
   return await injectTranslations(data);
 };
packages/visual-editor/src/vite-plugin/templates/locator.tsx (1)

1-1: Autogenerated file - consider updating generator for type safety.

Line 1 indicates this is autogenerated. If TemplateProps<any> at lines 95-99 is a concern, the fix should be applied to the template generator rather than this file.

packages/visual-editor/src/utils/i18n/platform.ts (1)

22-53: Consider documenting race condition potential.

While the hasResourceBundle check in loadPlatformTranslations prevents duplicate loading, concurrent calls with the same locale before the first completes could still cause race conditions. The error handling correctly falls back to "en", but this behavior should be documented.

Consider adding a loading state map to prevent concurrent loads:

const loadingLocales = new Set<string>();

const getTranslations = async (
  locale: string,
  isRetry = false
): Promise<Record<string, string>> => {
  if (!locale) {
    return {};
  }
  
  // Wait if already loading
  while (loadingLocales.has(locale)) {
    await new Promise(resolve => setTimeout(resolve, 50));
  }
  
  loadingLocales.add(locale);
  try {
    const module = await import(
      `../../../locales/platform/${locale}/visual-editor.json`
    );
    return module.default;
  } catch (e) {
    // existing error handling...
  } finally {
    loadingLocales.delete(locale);
  }
};
packages/visual-editor/src/utils/i18n/components.ts (1)

81-94: Allow refreshing an existing resource bundle

Once a bundle for a locale exists, this early return prevents us from merging any new or overridden keys that arrive later (e.g., when transformProps starts supplying additional strings). Dropping the guard and using addResourceBundle(..., true, true) keeps the fast path but still lets us refresh data when needed.

 export const loadComponentTranslations = async (
   locale: string,
   translations?: Record<string, string>
 ) => {
-  if (i18nComponentsInstance.hasResourceBundle(locale, NAMESPACE)) {
-    return;
-  }
-
-  const translationsToInject = translations || (await getTranslations(locale));
-
-  if (translationsToInject && Object.keys(translationsToInject).length > 0) {
-    i18nComponentsInstance.addResourceBundle(
-      locale,
-      NAMESPACE,
-      translationsToInject
-    );
-  }
+  const translationsToInject =
+    translations ?? (await getTranslations(locale));
+
+  if (!translationsToInject || Object.keys(translationsToInject).length === 0) {
+    return;
+  }
+
+  i18nComponentsInstance.addResourceBundle(
+    locale,
+    NAMESPACE,
+    translationsToInject,
+    true,
+    i18nComponentsInstance.hasResourceBundle(locale, NAMESPACE)
+  );
 };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 012c91a and 4b2ee0a.

📒 Files selected for processing (43)
  • packages/visual-editor/i18next-scanner.config.cjs (1 hunks)
  • packages/visual-editor/locales/components/cs/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/da/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/de/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/en-GB/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/en/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/es/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/et/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/fi/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/fr/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/hr/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/hu/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/it/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/ja/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/lt/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/lv/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/nb/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/nl/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/pl/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/pt/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/ro/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/sk/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/sv/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/tr/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/zh-TW/visual-editor.json (1 hunks)
  • packages/visual-editor/locales/components/zh/visual-editor.json (1 hunks)
  • packages/visual-editor/scripts/checkEmptyTranslations.ts (1 hunks)
  • packages/visual-editor/scripts/generateTranslations.ts (5 hunks)
  • packages/visual-editor/scripts/sortTranslations.ts (2 hunks)
  • packages/visual-editor/src/editor/Editor.tsx (2 hunks)
  • packages/visual-editor/src/internal/puck/components/LayoutHeader.tsx (3 hunks)
  • packages/visual-editor/src/utils/VisualEditorProvider.tsx (2 hunks)
  • packages/visual-editor/src/utils/i18n/components.ts (1 hunks)
  • packages/visual-editor/src/utils/i18n/fallbacks.ts (1 hunks)
  • packages/visual-editor/src/utils/i18n/index.ts (1 hunks)
  • packages/visual-editor/src/utils/i18n/platform.ts (3 hunks)
  • packages/visual-editor/src/utils/resolveComponentData.tsx (1 hunks)
  • packages/visual-editor/src/utils/resolveYextEntityField.ts (1 hunks)
  • packages/visual-editor/src/vite-plugin/plugin.ts (1 hunks)
  • packages/visual-editor/src/vite-plugin/templates/directory.tsx (3 hunks)
  • packages/visual-editor/src/vite-plugin/templates/locator.tsx (3 hunks)
  • packages/visual-editor/src/vite-plugin/templates/main.tsx (3 hunks)
  • starter/src/templates/dev.tsx (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
packages/visual-editor/src/internal/puck/components/LayoutHeader.tsx (2)
packages/visual-editor/src/utils/i18n/components.ts (2)
  • loadComponentTranslations (77-94)
  • i18nComponentsInstance (6-6)
packages/visual-editor/src/utils/i18n/platform.ts (2)
  • loadPlatformTranslations (58-73)
  • i18nPlatformInstance (7-7)
packages/visual-editor/src/vite-plugin/templates/locator.tsx (4)
packages/visual-editor/src/vite-plugin/templates/directory.tsx (1)
  • transformProps (96-100)
packages/visual-editor/src/vite-plugin/templates/main.tsx (1)
  • transformProps (97-101)
starter/src/templates/dev.tsx (1)
  • transformProps (131-135)
packages/visual-editor/src/utils/i18n/components.ts (1)
  • injectTranslations (22-34)
packages/visual-editor/scripts/generateTranslations.ts (1)
scripts/releaseUtils.ts (1)
  • isDryRun (17-17)
starter/src/templates/dev.tsx (4)
packages/visual-editor/src/vite-plugin/templates/directory.tsx (1)
  • transformProps (96-100)
packages/visual-editor/src/vite-plugin/templates/locator.tsx (1)
  • transformProps (95-99)
packages/visual-editor/src/vite-plugin/templates/main.tsx (1)
  • transformProps (97-101)
packages/visual-editor/src/utils/i18n/components.ts (1)
  • injectTranslations (22-34)
packages/visual-editor/src/vite-plugin/templates/main.tsx (4)
packages/visual-editor/src/vite-plugin/templates/directory.tsx (1)
  • transformProps (96-100)
packages/visual-editor/src/vite-plugin/templates/locator.tsx (1)
  • transformProps (95-99)
starter/src/templates/dev.tsx (1)
  • transformProps (131-135)
packages/visual-editor/src/utils/i18n/components.ts (1)
  • injectTranslations (22-34)
packages/visual-editor/src/vite-plugin/templates/directory.tsx (4)
packages/visual-editor/src/vite-plugin/templates/locator.tsx (1)
  • transformProps (95-99)
packages/visual-editor/src/vite-plugin/templates/main.tsx (1)
  • transformProps (97-101)
starter/src/templates/dev.tsx (1)
  • transformProps (131-135)
packages/visual-editor/src/utils/i18n/components.ts (1)
  • injectTranslations (22-34)
packages/visual-editor/src/utils/VisualEditorProvider.tsx (1)
packages/visual-editor/src/utils/i18n/components.ts (1)
  • loadComponentTranslations (77-94)
packages/visual-editor/src/editor/Editor.tsx (1)
packages/visual-editor/src/utils/i18n/platform.ts (2)
  • loadPlatformTranslations (58-73)
  • i18nPlatformInstance (7-7)
packages/visual-editor/src/utils/i18n/platform.ts (1)
packages/visual-editor/src/utils/i18n/fallbacks.ts (2)
  • applyI18nFallbacks (26-39)
  • defaultI18nFallbacks (6-15)
🔇 Additional comments (30)
packages/visual-editor/locales/components/sk/visual-editor.json (1)

1-14: JSON structure and Slovak translations look good.

The file is properly formatted and contains semantically appropriate translations for location/business hours status indicators and weekday names. The nested directory structure (locales/components/sk/) aligns with the PR's goal of supporting granular, dynamic translation loading.

Verify that this locale file is properly integrated into the new loadComponentTranslations() runtime loading function so it's only bundled and loaded when the Slovak locale is needed.

packages/visual-editor/locales/components/zh-TW/visual-editor.json (1)

1-14: Locale resource file looks good.

The JSON structure is valid, all keys are present with non-empty translations, and the camelCase naming convention is consistent with typical i18n patterns. The translations cover status states and day-of-week labels as expected.

One minor observation: "closed" and "closesAt" both map to the same translation "關閉". This is likely intentional, but if they should convey slightly different meanings in the UI, consider whether separate translations are needed.

packages/visual-editor/locales/components/da/visual-editor.json (1)

1-14: JSON structure is valid and properly integrated into the new locale hierarchy.

The file is well-formed, contains 12 UI translation keys for Danish, and correctly uses the new locales/components/{locale}/visual-editor.json path structure as part of the runtime translation loading system. Danish characters are properly encoded.

packages/visual-editor/locales/components/nl/visual-editor.json (1)

1-14: JSON structure and formatting are valid.

The translation file is properly formatted JSON with no syntax errors. However, I cannot verify the accuracy of Dutch translations or completeness of the key set without examining reference locale files or domain expertise.

Consider running the following to verify consistency with other locale files:

  • Ensure all keys in this file match those in companion locale files (e.g., en/visual-editor.json)
  • Have Dutch translations reviewed by a native speaker or translation expert to confirm accuracy
  • Verify that the key order is intentional or apply consistent alphabetical sorting across all locale files
packages/visual-editor/src/utils/resolveComponentData.tsx (1)

4-7: LGTM! Type-only imports correctly applied.

Converting these imports to type-only is a good TypeScript practice that reduces the runtime bundle size. All four types (RichText, TranslatableRichText, TranslatableString, YextEntityField) are used exclusively as type annotations throughout the file, making this change appropriate and aligned with the PR's goal of loading only necessary code.

packages/visual-editor/src/utils/resolveYextEntityField.ts (1)

1-3: LGTM! Consistent type-only imports.

The conversion to type-only imports is correct and follows the same pattern as the changes in resolveComponentData.tsx. All three types (StreamDocument, YextEntityField, YextStructEntityField) are used exclusively for type annotations, making this refactor safe and beneficial for reducing the runtime bundle size.

packages/visual-editor/locales/components/hu/visual-editor.json (1)

1-14: All required keys are present and consistent across locale files.

The Hungarian locale file contains all required keys matching the English baseline (13 keys: location status and weekdays). The file is properly structured and integrated into the new two-tier locale hierarchy supporting granular component translation loading.

To complete validation, verify:

  1. Hungarian translations are accurate for the intended use context (location/hours display).
  2. The runtime loadComponentTranslations() function properly loads and references this file.
packages/visual-editor/locales/components/ro/visual-editor.json (1)

1-14: ✓ Translation key consistency verified.

All 13 translation keys in the Romanian locale (closed, closesAt, friday, monday, open24Hours, openNow, saturday, sunday, temporarilyClosed, thursday, tuesday, wednesday) match exactly across all 25 locale files in the components namespace. The JSON structure is valid and properly formatted. No issues detected.

packages/visual-editor/locales/components/hr/visual-editor.json (1)

1-14: Translation content is accurate and properly integrated into the dynamic loading system.

The Croatian locale file is correctly positioned at packages/visual-editor/locales/components/hr/visual-editor.json, following the naming convention of all 24 other component translations. The loadComponentTranslations() function dynamically imports component locales via import(\../../../locales/components/${locale}/visual-editor.json`)`, which correctly resolves to this file. The system uses runtime lazy loading—component translations are not processed by the i18next-scanner (which handles platform translations separately) but are loaded on-demand when the Croatian locale is selected, with automatic fallback to English if needed.

packages/visual-editor/locales/components/nb/visual-editor.json (1)

1-14: Norwegian translations look reasonable.

The translation keys and values are consistent with other locale files. The use of "Lukket" for closed status and "Åpent 24 timer" for 24-hour operation appears appropriate.

packages/visual-editor/locales/components/en/visual-editor.json (1)

1-14: English baseline translations are correct.

All translations use appropriate grammatical forms for status indicators and are consistent across keys.

packages/visual-editor/src/vite-plugin/templates/directory.tsx (3)

12-12: LGTM: Required import added.

The TransformProps type import is necessary for the new transformProps function.


27-27: LGTM: Translation injection utility imported.

The injectTranslations import enables runtime translation loading based on document locale.


96-100: LGTM: Translation injection follows established pattern.

The transformProps implementation correctly delegates to injectTranslations and matches the pattern used in other templates (locator.tsx, main.tsx). This enables dynamic loading of only the necessary translations for each locale, aligning with the PR objective.

packages/visual-editor/locales/components/cs/visual-editor.json (1)

1-14: Czech translations look reasonable.

The translations use appropriate grammatical forms. Note that "ZAVŘENO" (line 2) is in all caps, which may be intentional for emphasis on closed status.

packages/visual-editor/locales/components/et/visual-editor.json (1)

1-14: LGTM! Estonian translations are complete and properly localized.

All required keys are translated and the structure is correct.

packages/visual-editor/locales/components/es/visual-editor.json (1)

1-14: LGTM! Spanish translations are complete and properly localized.

All required keys are translated correctly.

packages/visual-editor/src/utils/i18n/index.ts (1)

1-1: LGTM! Export addition supports runtime translation injection.

Adding injectTranslations to the public API aligns with the PR objective to load translations dynamically.

packages/visual-editor/locales/components/pt/visual-editor.json (1)

1-14: LGTM! Portuguese translations are complete and properly localized.

All required keys including weekdays are fully translated.

packages/visual-editor/locales/components/lv/visual-editor.json (1)

1-14: LGTM! Latvian translations are complete and properly localized.

All required keys are fully translated.

packages/visual-editor/locales/components/en-GB/visual-editor.json (1)

1-14: LGTM!

The UK English translations are correct and consistent with standard localization practices.

packages/visual-editor/src/vite-plugin/templates/main.tsx (1)

97-101: LGTM!

The transformProps implementation correctly awaits the async injectTranslations call and follows the same pattern used in other template files (directory.tsx, locator.tsx, dev.tsx). This ensures translations are loaded before template rendering.

packages/visual-editor/locales/components/lt/visual-editor.json (1)

1-14: Structure looks good; verify translation accuracy.

The JSON structure and formatting are correct. However, translation accuracy should be verified by a Lithuanian speaker to ensure the translations are natural and appropriate for the UI context.

packages/visual-editor/i18next-scanner.config.cjs (1)

55-56: LGTM! Path restructuring aligns with the two-tier locale hierarchy.

The updated resource paths correctly scope translations under locales/platform/, enabling separate handling of platform vs. component translations.

packages/visual-editor/src/vite-plugin/plugin.ts (1)

154-158: LGTM! Cleanup now properly scoped to build mode.

The conditional cleanup ensures generated files persist during dev mode while being cleaned up after builds. This aligns with the discussion in past review comments about buildStart running for both server and build modes.

packages/visual-editor/scripts/checkEmptyTranslations.ts (2)

15-50: LGTM! Improved resilience with two-tier structure support.

The updated logic correctly handles the nested locale structure (locales/<subfolder>/<locale>/visual-editor.json) and gracefully warns about missing files instead of failing. The two-tier traversal aligns with the PR's restructuring goals.


52-60: Good addition of explicit exit code handling.

The post-processing check ensures the script exits with the correct code based on validation results, improving CI/CD integration.

packages/visual-editor/src/utils/i18n/platform.ts (2)

7-7: LGTM! Instance now exported for direct usage.

Exporting i18nPlatformInstance enables components like LayoutHeader to directly control language changes after loading translations.


9-17: Good shift to runtime translation loading.

Starting with empty resources and loading on-demand reduces bundle size and enables locale-specific loading, directly addressing the PR objective.

packages/visual-editor/scripts/sortTranslations.ts (1)

39-71: Add error handling around file I/O operations.

The original review comment correctly identified missing error handling, but its structural assumptions do not match the actual directory layout. The locale structure is strictly locales/{components,platform}/{language}/visual-editor.json with no mixed-content directories or nested JSON files at intermediate levels. The algorithm handles this structure correctly.

However, error handling is genuinely missing from file operations. Add try-catch blocks around JSON.parse, fs.readFile, and fs.writeFile:

           if (file.isFile() && file.name.endsWith(".json")) {
             const filePath = path.join(fullPath, file.name);
-            const data = JSON.parse(await fs.readFile(filePath, "utf8"));
-            const sorted = sortObject(data);
-            await fs.writeFile(
-              filePath,
-              JSON.stringify(sorted, null, 2) + "\n"
-            );
+            try {
+              const data = JSON.parse(await fs.readFile(filePath, "utf8"));
+              const sorted = sortObject(data);
+              await fs.writeFile(
+                filePath,
+                JSON.stringify(sorted, null, 2) + "\n"
+              );
+            } catch (error) {
+              console.error(`Error processing ${filePath}:`, error);
+              throw error;
+            }
           }

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/visual-editor/src/vite-plugin/plugin.ts (1)

151-158: Update the comment to reflect the new cleanup behavior.

The comment at lines 95-96 states "Created files will be marked for deletion on buildEnd," but with the new conditional logic, files are only cleaned up on buildEnd during build mode. In dev mode, cleanup is deferred to the SIGINT/SIGTERM handlers (lines 136-144).

Apply this diff to update the comment:

   /**
    * generateFiles generates the template files and .temlpate-manifest.json file
    *
    * Does not overwrite files that already exists
    *
-   * Created files will be marked for deletion on buildEnd
+   * Created files will be marked for deletion on buildEnd (build mode) or process termination (dev mode)
    */
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b2ee0a and ff7610a.

📒 Files selected for processing (6)
  • packages/visual-editor/src/utils/resolveYextEntityField.ts (1 hunks)
  • packages/visual-editor/src/vite-plugin/plugin.ts (1 hunks)
  • packages/visual-editor/src/vite-plugin/templates/directory.tsx (3 hunks)
  • packages/visual-editor/src/vite-plugin/templates/locator.tsx (3 hunks)
  • packages/visual-editor/src/vite-plugin/templates/main.tsx (3 hunks)
  • starter/src/templates/dev.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • starter/src/templates/dev.tsx
  • packages/visual-editor/src/vite-plugin/templates/main.tsx
  • packages/visual-editor/src/utils/resolveYextEntityField.ts
  • packages/visual-editor/src/vite-plugin/templates/locator.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
packages/visual-editor/src/vite-plugin/templates/directory.tsx (4)
packages/visual-editor/src/vite-plugin/templates/main.tsx (1)
  • transformProps (97-101)
starter/src/templates/dev.tsx (1)
  • transformProps (133-137)
packages/visual-editor/src/vite-plugin/templates/locator.tsx (1)
  • transformProps (95-99)
packages/visual-editor/src/utils/i18n/components.ts (1)
  • injectTranslations (22-34)
🔇 Additional comments (1)
packages/visual-editor/src/vite-plugin/templates/directory.tsx (1)

96-100: LGTM! Translation injection is correctly implemented.

The transformProps implementation correctly delegates to injectTranslations and is consistent with the pattern used in other template files (main.tsx, locator.tsx). This aligns with the PR objective to load only necessary translations based on the document locale.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
packages/visual-editor/scripts/generateTranslations.ts (4)

64-84: Update TSDoc to document the type parameter.

The ENOENT handling from the previous review has been correctly implemented. However, the function documentation doesn't describe the new type parameter.

Apply this diff to complete the documentation:

 /**
- * Reads all directories under localesDir and returns them as target languages.
+ * Reads all directories under localesDir/{type} and returns them as target languages.
+ * @param type - The translation type to get languages for ("components" or "platform")
+ * @returns Array of language directory names, or empty array if type directory doesn't exist
  */
 async function getTargetLanguages(
   type: "components" | "platform"
 ): Promise<string[]> {

194-198: Update TSDoc to document the type parameter.

The function documentation doesn't describe the new type parameter.

Apply this diff:

 /**
  * Loads the base English translation file, compares it to each target language file,
  * translates missing or empty keys using Google Translate, and optionally writes them back.
+ * @param type - The translation type to process ("components" or "platform")
  */
 async function translateFile(type: "components" | "platform"): Promise<void> {

256-256: Make error logging consistent with other log messages.

All other log messages in this function use the [${type}/${lng}] format, but this error log uses only [${lng}].

Apply this diff:

         } catch (e) {
           failCount++;
-          console.error(`[${lng}] ❌ Failed to translate key "${key}":`, e);
+          console.error(`[${type}/${lng}] ❌ Failed to translate key "${key}":`, e);
         }

273-274: Await translation promises to ensure proper completion and error handling.

The script invokes both translateFile calls without awaiting them. While Node.js keeps the process alive for pending async operations, this pattern has drawbacks: if either translation fails, the error is logged but the process may exit with code 0, causing CI/CD pipelines to incorrectly report success.

Apply this diff to run both in parallel with proper exit code handling:

-// Kick off the translation process
-translateFile("components").catch(console.error);
-translateFile("platform").catch(console.error);
+(async () => {
+  try {
+    await Promise.all([
+      translateFile("components"),
+      translateFile("platform")
+    ]);
+  } catch (error) {
+    console.error(error);
+    process.exit(1);
+  }
+})();

Or, if you prefer sequential execution:

-// Kick off the translation process
-translateFile("components").catch(console.error);
-translateFile("platform").catch(console.error);
+(async () => {
+  try {
+    await translateFile("components");
+    await translateFile("platform");
+  } catch (error) {
+    console.error(error);
+    process.exit(1);
+  }
+})();
packages/visual-editor/src/internal/puck/components/LayoutHeader.tsx (1)

224-239: Consider adding error handling for translation loading.

The race conditions from previous reviews have been correctly addressed by making the handlers async and awaiting the translation loaders. However, for improved developer experience in local dev mode, consider adding error handling:

      <Button
        onClick={async () => {
          const locale = prompt("Enter components locale:") || "en";
-         await loadComponentTranslations(locale);
-         i18nComponentsInstance.changeLanguage(locale);
+         try {
+           await loadComponentTranslations(locale);
+           i18nComponentsInstance.changeLanguage(locale);
+         } catch (error) {
+           console.error('Failed to load component translations:', error);
+           alert(`Failed to load translations for locale: ${locale}`);
+         }
        }}
        variant="outline"
        className="ve-ml-4"
      >
        Set Components Locale
      </Button>
      <Button
        onClick={async () => {
          const locale = prompt("Enter platform locale:") || "en";
-         await loadPlatformTranslations(locale);
-         i18nPlatformInstance.changeLanguage(locale);
+         try {
+           await loadPlatformTranslations(locale);
+           i18nPlatformInstance.changeLanguage(locale);
+         } catch (error) {
+           console.error('Failed to load platform translations:', error);
+           alert(`Failed to load translations for locale: ${locale}`);
+         }
        }}
        variant="outline"
        className="ve-ml-4"
      >
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff7610a and 0dddf27.

📒 Files selected for processing (3)
  • packages/visual-editor/scripts/generateTranslations.ts (5 hunks)
  • packages/visual-editor/src/editor/Editor.tsx (2 hunks)
  • packages/visual-editor/src/internal/puck/components/LayoutHeader.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/visual-editor/src/editor/Editor.tsx (1)
packages/visual-editor/src/utils/i18n/platform.ts (2)
  • loadPlatformTranslations (58-73)
  • i18nPlatformInstance (7-7)
packages/visual-editor/src/internal/puck/components/LayoutHeader.tsx (2)
packages/visual-editor/src/utils/i18n/components.ts (2)
  • loadComponentTranslations (77-94)
  • i18nComponentsInstance (6-6)
packages/visual-editor/src/utils/i18n/platform.ts (2)
  • loadPlatformTranslations (58-73)
  • i18nPlatformInstance (7-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: call_unit_test / unit_tests (18.x)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: semgrep/ci
  • GitHub Check: create-dev-release
🔇 Additional comments (2)
packages/visual-editor/src/editor/Editor.tsx (1)

19-22: LGTM! Imports correctly support the new translation loading flow.

The addition of loadPlatformTranslations enables the pre-loading of platform translations before language changes.

packages/visual-editor/src/internal/puck/components/LayoutHeader.tsx (1)

16-19: LGTM! Imports correctly support the dev override buttons.

The translation loading utilities are properly imported and enable the locale-switching functionality in LocalDevOverrideButtons.

Also applies to: 24-24

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🤖 Fix all issues with AI agents
In @packages/visual-editor/locales/components/cs/visual-editor.json:
- Line 22: The "heroHeader" localization value is still in English; update the
"heroHeader" key in the Czech locale to a proper Czech translation (e.g.,
"Hlavní záhlaví") to match the translated style of sibling keys like
"heroBanner" and "heroImage" so the UI is consistent for Czech users.
- Around line 60-61: Update the Czech translations for the arrow keys: change
the value for "arrowRight" from "Šipka správně" to "Šipka vpravo" and change the
value for "arrowUp" from "Šipky" to "Šipka nahoru" so both keys ("arrowRight"
and "arrowUp") use the correct singular directional phrases consistent with
other arrow entries.

In @packages/visual-editor/locales/components/hr/visual-editor.json:
- Line 73: Replace the incorrect restaurant-related translation for the JSON key
"menu" (currently "Jelovnik") with the UI-consistent term used elsewhere
("izbornik") so the "menu" entry matches other menu translations in this locale
file.

In @packages/visual-editor/locales/components/it/visual-editor.json:
- Around line 35-36: The Italian pluralization for "mile" is incorrect: the key
"mile_other" currently maps to "miglio" but should be "miglia"; update the
translation so "mile_one" stays "miglio" and "mile_other" becomes "miglia" to
reflect correct singular/plural forms for the "mile" translation keys.
- Line 7: The translation for the key "buttonWithIcon" mixes English and
Italian; update its value to fully Italian by replacing "Button con icon
{{presetImageType}}" with "Pulsante con icona {{presetImageType}}" so the
localization string is consistent.

In @packages/visual-editor/locales/components/lt/visual-editor.json:
- Around line 31-36: The translation entries use inconsistent quotation marks
and one inconsistent word: update the keys locationsNear_one and
locationsWithinDistanceOf_{one,few,other} to use Lithuanian quotation marks
(„{{name}}“) to match locationsNear_few/other, and change locationsNear_one and
locationsWithinDistanceOf_one to use "šalia" instead of "prie" so all lines
consistently read e.g. "{{count}} vieta šalia „{{name}}“" and "{{count}} vieta
per {{distance}} {{mile}} nuo „{{name}}“" respectively.
- Around line 39-41: Add the missing Lithuanian plural key "mile_many" so the
plural set for "mile" includes mile_one, mile_few, mile_many, and mile_other;
add "mile_many": "mylių" to the JSON near the other mile_* entries and ensure
the surrounding commas keep the file valid JSON.
- Around line 31-33: Add the missing Lithuanian plural form key
"locationsNear_many" alongside the existing "locationsNear_one",
"locationsNear_few", and "locationsNear_other"; implement "locationsNear_many"
with the correct Lithuanian phrasing for counts ending in 0 or 10–19 (e.g., a
string like "{{count}} vietų šalia „{{name}}“") so all four plural variants are
present for the locationsNear message.
- Around line 34-36: Add the missing Lithuanian plural form key
"locationsWithinDistanceOf_many" to the JSON and set its value to the
appropriate string for numbers ending in 0 or 10–19 (use the same plural noun
form as in "locationsWithinDistanceOf_few/other"), e.g. "{{count}} vietovių per
{{distance}} {{mile}} nuo \"{{name}}\""; update the object to include this key
so all pluralization cases (one, few, many, other) are covered.
- Around line 112-114: Add the missing plural form by adding a
"totalReviews_many" key alongside the existing "totalReviews_one",
"totalReviews_few", and "totalReviews_other" entries; set its value to the same
Lithuanian pluralization string as "totalReviews_other" (i.e., use the same
"{{count}} atsiliepimų" text) so all required plural categories are present.

In @packages/visual-editor/locales/components/nb/visual-editor.json:
- Line 59: The JSON value for the "email" key contains an incorrect space ("E
-post"); update the value for the "email" key in visual-editor.json to the
correct Norwegian convention "e-post" (all lowercase, no space before the
hyphen) to fix the spacing and casing.

In @packages/visual-editor/locales/components/pt/visual-editor.json:
- Line 24: Update the three loading-state translation values to use the gerund
form for consistency: change the value for "loadingMap" from "Mapa de
carregamento ..." to a gerund phrase like "Carregando mapa...", ensure
"loadingNearby" remains or is changed to "Carregando locais próximos" (if not
already), and change "loadingRevs" / "loadingRevisions" from "Revisões de
carregamento" to "Carregando revisões" (use the actual key present in the file),
preserving punctuation and ellipses as used elsewhere.

In @packages/visual-editor/locales/platform/es/visual-editor.json:
- Around line 747-748: The singular translation key totalReviews_one uses the
wrong noun "revisión"; update totalReviews_one to use "reseña" to match
totalReviews_other (i.e., make it "{{count}} reseña") so singular and plural use
consistent terminology for user reviews; verify the plural form
totalReviews_other remains "{{count}} reseñas".

In @packages/visual-editor/locales/platform/hr/visual-editor.json:
- Line 598: Replace the incorrect Croatian translation for the "menu" key in the
visual-editor locale JSON (the "menu" entry currently set to "Jelovnik") with
the UI-appropriate term "Izbornik" so it matches other menu-related translations
in this locale file (e.g., the same "menu" concept used elsewhere on lines
referenced in the review).

In @packages/visual-editor/locales/platform/it/visual-editor.json:
- Around line 541-542: The Italian plural is incorrect: update the localization
entry for the keys "mile_one" and "mile_other" in visual-editor.json so that
"mile_one" remains "miglio" (singular) and "mile_other" is changed from "miglio"
to the correct plural "miglia".

In @packages/visual-editor/locales/platform/sv/visual-editor.json:
- Around line 536-537: The Swedish translation keys
locationsWithinDistanceOf_one and locationsWithinDistanceOf_other currently use
"mil" which in Swedish denotes 10 km; update these strings to either use "miles"
or "engelska mil" (e.g., replace "{{mile}}" with "miles" or "engelska mil") to
avoid ambiguity, or switch to metric wording (e.g., "{{distance}} km") and if
choosing metric update the distance unit conversion in the code paths that
populate the {{distance}}/{{mile}} variables (where these keys are used) so the
displayed number matches the unit change; apply the same change to the other
affected keys called out in the comment.
🧹 Nitpick comments (5)
packages/visual-editor/locales/components/cs/visual-editor.json (1)

14-14: Consider refining these translations for native fluency.

A few translations could be more natural in Czech:

  • Line 14: "Uzavírá" is incomplete for "closesAt"; consider "Uzavírá v" (closes at)
  • Line 51: "Otevírá se na" is awkward; "Otevírá v" reads more naturally
  • Line 97: "Rozbalovací vstup pro vyhledávání" doesn't quite match "Search dropdown here"; consider "Hledejte v rozbalovacím seznamu"

Also applies to: 51-51, 97-97

packages/visual-editor/locales/platform/ro/visual-editor.json (1)

655-663: Social media translations look good.

The new socialLinks object provides Romanian translations for social media platform CTAs. The imperative mood ("Urmărește-ne pe..." / "Follow us on...") is appropriate, and the YouTube translation correctly uses "Abonați-vă" (Subscribe) rather than "Follow."

Optional: Consider verification by a native Romanian speaker

Since these are new public-facing translations for social media CTAs, consider having them reviewed by a native Romanian speaker to ensure they align with your brand voice and local conventions, though they appear grammatically correct.

packages/visual-editor/locales/components/nb/visual-editor.json (1)

46-46: Consider reviewing "disposisjon" translation with a native speaker.

The term "disposisjon" (used for "outline") typically means "disposition" or "arrangement" in Norwegian. In the context of icon variants, "kontur" or "omriss" might be more idiomatic. Consider having a native Norwegian speaker verify this translation choice.

Also applies to: 48-48, 63-63, 66-66

packages/visual-editor/src/components/footer/Footer.tsx (1)

126-131: Consider aligning the property name with the translation key.

The name property is "twitter" but the translation key is "socialLinks.xLink". While this may reflect the platform's rebrand to X, the inconsistency could cause confusion. Consider either:

  • Updating the name to "x" to match the key, or
  • Using "socialLinks.twitter" as the key to match the property name
packages/visual-editor/src/utils/i18n/components.ts (1)

18-24: Consider tightening the TemplateProps interface type.

The translations property allows both Record<string, string> and Record<string, any>. If nested translation objects (like socialLinks or presetImages) are expected, the type is appropriate. Otherwise, narrowing to Record<string, string> would provide better type safety.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d98d6f and bb1659e.

⛔ Files ignored due to path filters (40)
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] default props with data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] default props with data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] default props with data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] latest version default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] latest version non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 10 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 10 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 21 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 21 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 22 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 22 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 24 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] latest version non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] version 10 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] version 21 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] version 22 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] version 22 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] default props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 10 props with French locale.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 10 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 10 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 39 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 39 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] default props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 10 props with French locale (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 10 props with French locale.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 10 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 10 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 39 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 39 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] default props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 10 props with French locale (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 10 props with French locale.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 10 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 10 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 39 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 39 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (57)
  • packages/visual-editor/locales/components/cs/visual-editor.json
  • packages/visual-editor/locales/components/da/visual-editor.json
  • packages/visual-editor/locales/components/de/visual-editor.json
  • packages/visual-editor/locales/components/en-GB/visual-editor.json
  • packages/visual-editor/locales/components/en/visual-editor.json
  • packages/visual-editor/locales/components/es/visual-editor.json
  • packages/visual-editor/locales/components/et/visual-editor.json
  • packages/visual-editor/locales/components/fi/visual-editor.json
  • packages/visual-editor/locales/components/fr/visual-editor.json
  • packages/visual-editor/locales/components/hr/visual-editor.json
  • packages/visual-editor/locales/components/hu/visual-editor.json
  • packages/visual-editor/locales/components/it/visual-editor.json
  • packages/visual-editor/locales/components/ja/visual-editor.json
  • packages/visual-editor/locales/components/lt/visual-editor.json
  • packages/visual-editor/locales/components/lv/visual-editor.json
  • packages/visual-editor/locales/components/nb/visual-editor.json
  • packages/visual-editor/locales/components/nl/visual-editor.json
  • packages/visual-editor/locales/components/pl/visual-editor.json
  • packages/visual-editor/locales/components/pt/visual-editor.json
  • packages/visual-editor/locales/components/ro/visual-editor.json
  • packages/visual-editor/locales/components/sk/visual-editor.json
  • packages/visual-editor/locales/components/sv/visual-editor.json
  • packages/visual-editor/locales/components/tr/visual-editor.json
  • packages/visual-editor/locales/components/zh-TW/visual-editor.json
  • packages/visual-editor/locales/components/zh/visual-editor.json
  • packages/visual-editor/locales/platform/cs/visual-editor.json
  • packages/visual-editor/locales/platform/da/visual-editor.json
  • packages/visual-editor/locales/platform/de/visual-editor.json
  • packages/visual-editor/locales/platform/en-GB/visual-editor.json
  • packages/visual-editor/locales/platform/en/visual-editor.json
  • packages/visual-editor/locales/platform/es/visual-editor.json
  • packages/visual-editor/locales/platform/et/visual-editor.json
  • packages/visual-editor/locales/platform/fi/visual-editor.json
  • packages/visual-editor/locales/platform/fr/visual-editor.json
  • packages/visual-editor/locales/platform/hr/visual-editor.json
  • packages/visual-editor/locales/platform/hu/visual-editor.json
  • packages/visual-editor/locales/platform/it/visual-editor.json
  • packages/visual-editor/locales/platform/ja/visual-editor.json
  • packages/visual-editor/locales/platform/lt/visual-editor.json
  • packages/visual-editor/locales/platform/lv/visual-editor.json
  • packages/visual-editor/locales/platform/nb/visual-editor.json
  • packages/visual-editor/locales/platform/nl/visual-editor.json
  • packages/visual-editor/locales/platform/pl/visual-editor.json
  • packages/visual-editor/locales/platform/pt/visual-editor.json
  • packages/visual-editor/locales/platform/ro/visual-editor.json
  • packages/visual-editor/locales/platform/sk/visual-editor.json
  • packages/visual-editor/locales/platform/sv/visual-editor.json
  • packages/visual-editor/locales/platform/tr/visual-editor.json
  • packages/visual-editor/locales/platform/zh-TW/visual-editor.json
  • packages/visual-editor/locales/platform/zh/visual-editor.json
  • packages/visual-editor/scripts/sortTranslations.ts
  • packages/visual-editor/src/components/Locator.tsx
  • packages/visual-editor/src/components/atoms/reviewStars.tsx
  • packages/visual-editor/src/components/footer/Footer.tsx
  • packages/visual-editor/src/components/pageSections/NearbyLocations/NearbyLocationsCardsWrapper.tsx
  • packages/visual-editor/src/editor/Editor.tsx
  • packages/visual-editor/src/utils/i18n/components.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/visual-editor/locales/components/ja/visual-editor.json
  • packages/visual-editor/locales/components/nl/visual-editor.json
🚧 Files skipped from review as they are similar to previous changes (19)
  • packages/visual-editor/locales/components/sk/visual-editor.json
  • packages/visual-editor/locales/components/fi/visual-editor.json
  • packages/visual-editor/locales/components/pl/visual-editor.json
  • packages/visual-editor/scripts/sortTranslations.ts
  • packages/visual-editor/locales/components/en-GB/visual-editor.json
  • packages/visual-editor/src/components/atoms/reviewStars.tsx
  • packages/visual-editor/src/components/pageSections/NearbyLocations/NearbyLocationsCardsWrapper.tsx
  • packages/visual-editor/locales/components/ro/visual-editor.json
  • packages/visual-editor/locales/components/da/visual-editor.json
  • packages/visual-editor/locales/components/es/visual-editor.json
  • packages/visual-editor/locales/platform/nl/visual-editor.json
  • packages/visual-editor/locales/components/lv/visual-editor.json
  • packages/visual-editor/locales/platform/da/visual-editor.json
  • packages/visual-editor/locales/components/sv/visual-editor.json
  • packages/visual-editor/locales/components/fr/visual-editor.json
  • packages/visual-editor/locales/components/et/visual-editor.json
  • packages/visual-editor/locales/platform/pt/visual-editor.json
  • packages/visual-editor/locales/components/zh/visual-editor.json
  • packages/visual-editor/locales/components/zh-TW/visual-editor.json
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-12-23T16:36:42.670Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/locales/ro/visual-editor.json:394-394
Timestamp: 2025-12-23T16:36:42.670Z
Learning: In Romanian locale files (ro), "Top" is an acceptable translation for directional terms like "top_direction" rather than the native Romanian "Sus". The maintainer benlife5 confirmed this is the preferred translation in packages/visual-editor/locales/ro/visual-editor.json.

Applied to files:

  • packages/visual-editor/locales/components/tr/visual-editor.json
  • packages/visual-editor/locales/components/lt/visual-editor.json
  • packages/visual-editor/locales/components/hr/visual-editor.json
  • packages/visual-editor/locales/components/hu/visual-editor.json
  • packages/visual-editor/locales/platform/hr/visual-editor.json
  • packages/visual-editor/locales/platform/cs/visual-editor.json
  • packages/visual-editor/locales/platform/ro/visual-editor.json
📚 Learning: 2025-12-15T20:59:25.571Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 943
File: packages/visual-editor/src/components/contentBlocks/image/Image.tsx:175-191
Timestamp: 2025-12-15T20:59:25.571Z
Learning: In the ImageWrapper component at packages/visual-editor/src/components/contentBlocks/image/Image.tsx, when an Image is wrapped in a MaybeLink, the aria-label should be omitted because the image's alt text serves as the accessible name for the link. Adding an aria-label would override the image's alt text.

Applied to files:

  • packages/visual-editor/src/components/footer/Footer.tsx
  • packages/visual-editor/locales/platform/en/visual-editor.json
📚 Learning: 2025-10-29T22:00:03.843Z
Learnt from: mkouzel-yext
Repo: yext/visual-editor PR: 833
File: packages/visual-editor/src/components/Locator.tsx:1050-1057
Timestamp: 2025-10-29T22:00:03.843Z
Learning: In packages/visual-editor/src/components/Locator.tsx, the AppliedFilters component is intentionally rendered in two locations (inside the filter modal and outside it) as per the design requirements. This dual rendering should not be flagged as a duplicate issue.

Applied to files:

  • packages/visual-editor/src/components/Locator.tsx
📚 Learning: 2025-12-23T16:30:55.497Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:55.497Z
Learning: In CompactHero and CompactPromo components, duplicate IDs on image elements are intentional: conditional visibility classes (hidden/sm:block/sm:hidden) ensure only one image renders at a time based on responsive breakpoints and desktopImagePosition/mobileImagePosition settings.

Applied to files:

  • packages/visual-editor/locales/platform/en/visual-editor.json
📚 Learning: 2025-12-12T20:17:37.033Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 946
File: packages/visual-editor/src/components/migrations/migrationRegistry.ts:47-48
Timestamp: 2025-12-12T20:17:37.033Z
Learning: In the yext/visual-editor repository, empty object placeholders (`{}`) in the migrationRegistry array are sometimes intentionally used to reserve migration slots for parallel PRs to prevent merge conflicts and maintain consistent migration indices.

Applied to files:

  • packages/visual-editor/locales/platform/en-GB/visual-editor.json
🧬 Code graph analysis (1)
packages/visual-editor/src/utils/i18n/components.ts (1)
packages/visual-editor/src/utils/i18n/index.ts (2)
  • i18nComponentsInstance (1-1)
  • injectTranslations (1-1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: call_unit_test / unit_tests (24.x)
  • GitHub Check: call_unit_test / unit_tests (22.x)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: create-dev-release
  • GitHub Check: semgrep/ci

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @packages/visual-editor/locales/components/nb/visual-editor.json:
- Around line 35-36: The Norwegian locale has inconsistent plural keys
"mile_one" and "mile_other" using different terms; make them consistent by
setting both values to the same Norwegian unit ("mil") to match the Swedish
pattern, and add/update the corresponding translation keys "mile_one" and
"mile_other" so both read "mil"; if the backend actually uses English miles,
instead choose the Anglicized term for both keys ("mile") and coordinate with
the backend to clarify unit conversion to avoid user confusion.

In @packages/visual-editor/locales/platform/cs/visual-editor.json:
- Line 311: The JSON key "base_text size" contains a space; rename it to a
consistent identifier (e.g., "base_text_size" or "baseTextSize") in this locale
file and update every other locale file and any code references that access this
key (lookup, i18n keys, tests) to use the new name so usages remain consistent
across the app; ensure pluralization/namespace patterns remain intact and run
the i18n/lint/tests to verify no missing-key errors.

In @packages/visual-editor/locales/platform/es/visual-editor.json:
- Around line 747-748: The Spanish translations mix "revisión/revisiones" and
"reseña/reseñas"; standardize to "reseña" for user reviews by replacing any
occurrences of "revisión" with "reseña" and "revisiones" with "reseñas"
throughout the file, and ensure pluralized keys (e.g., "totalReviews_one" and
"totalReviews_other") use "{{count}} reseña" and "{{count}} reseñas"
respectively while preserving existing ICU/placeholder syntax.

In @packages/visual-editor/locales/platform/nb/visual-editor.json:
- Around line 541-542: The pluralization keys "mile_one" and "mile_other" are
inconsistent; update them so both use the same Norwegian term—change the value
for "mile_other" to "mil" to match "mile_one" (i.e., set both "mile_one" and
"mile_other" to "mil") so the UI shows a consistent unit for miles in Norwegian
Bokmål.
🧹 Nitpick comments (2)
packages/visual-editor/locales/components/pt/visual-editor.json (1)

1-113: LGTM! Translation file structure is correct.

The JSON structure, i18next interpolation syntax, and pluralization patterns are all technically correct. The file provides comprehensive Portuguese translations for the visual-editor component.

One minor observation: there's a capitalization variation between "Banner do Herói" (line 21) and "Cabeçalho do herói" (line 22). This may be intentional based on Portuguese grammar or UI component naming conventions, but you may want to verify consistency if it wasn't deliberate.

packages/visual-editor/package.json (1)

57-57: Consider consolidating the sort calls for efficiency.

The current setup causes i18n:sort to run multiple times when executing i18n:update (once after i18n:copy-platform-translations and again after i18n:fix-plurals). While sorting is typically fast and this doesn't break functionality, you could optimize by having i18n:update call sort just once at the end:

-"i18n:update": "pnpm run i18n:scan && pnpm run i18n:translate && pnpm run i18n:copy-platform-translations && pnpm run i18n:fix-plurals",
+"i18n:update": "pnpm run i18n:scan && pnpm run i18n:translate && pnpm exec tsx scripts/copyPlatformTranslations.ts && pnpm exec tsx scripts/fixPlurals.ts && pnpm run i18n:sort",

This would require i18n:copy-platform-translations and i18n:fix-plurals to be used both standalone (when they need to sort) and as part of the larger workflow (when they don't). The current approach is simpler and more maintainable, so this optimization is entirely optional.

Also applies to: 59-59, 61-61

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb1659e and 8a45b49.

⛔ Files ignored due to path filters (69)
  • packages/visual-editor/src/components/testing/screenshots/BreadcrumbsSection/[tablet] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/BreadcrumbsSection/[tablet] version 4 props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/BreadcrumbsSection/[tablet] version 8 with non-default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [classic] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [classic] version 17 props using entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [classic] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [classic] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [compact] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [compact] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [compact] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [immersive] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [immersive] version 31 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [immersive] version 31 props using entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [spotlight] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [spotlight] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] default props with data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] version 16 props using entity values with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] version 16 props with missing ctaType.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] version 16 props with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] version 9 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [classic] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [classic] version 17 props using entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [classic] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [classic] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [compact] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [compact] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [compact] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [immersive] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [immersive] version 31 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [immersive] version 31 props using entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [spotlight] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] [spotlight] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] default props with data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] version 16 props using entity values with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] version 16 props with missing ctaType.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] version 16 props with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[mobile] version 9 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [classic] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [classic] version 17 props using entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [classic] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [classic] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [compact] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [compact] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [compact] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [immersive] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [immersive] version 31 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [immersive] version 31 props using entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [spotlight] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [spotlight] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] default props with data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] version 16 props using entity values with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] version 16 props with missing ctaType.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] version 16 props with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] version 9 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[xl] [compact] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] default props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 10 props with French locale.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 10 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 39 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] default props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 10 props with French locale.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 10 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 39 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 10 props with French locale.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 10 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 39 props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (15)
  • packages/visual-editor/locales/components/cs/visual-editor.json
  • packages/visual-editor/locales/components/es/visual-editor.json
  • packages/visual-editor/locales/components/hr/visual-editor.json
  • packages/visual-editor/locales/components/it/visual-editor.json
  • packages/visual-editor/locales/components/lt/visual-editor.json
  • packages/visual-editor/locales/components/nb/visual-editor.json
  • packages/visual-editor/locales/components/pt/visual-editor.json
  • packages/visual-editor/locales/platform/cs/visual-editor.json
  • packages/visual-editor/locales/platform/es/visual-editor.json
  • packages/visual-editor/locales/platform/hr/visual-editor.json
  • packages/visual-editor/locales/platform/it/visual-editor.json
  • packages/visual-editor/locales/platform/lt/visual-editor.json
  • packages/visual-editor/locales/platform/nb/visual-editor.json
  • packages/visual-editor/locales/platform/pt/visual-editor.json
  • packages/visual-editor/package.json
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/visual-editor/locales/components/es/visual-editor.json
  • packages/visual-editor/locales/components/it/visual-editor.json
  • packages/visual-editor/locales/components/lt/visual-editor.json
  • packages/visual-editor/locales/components/hr/visual-editor.json
  • packages/visual-editor/locales/components/cs/visual-editor.json
  • packages/visual-editor/locales/platform/hr/visual-editor.json
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-23T16:36:42.670Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/locales/ro/visual-editor.json:394-394
Timestamp: 2025-12-23T16:36:42.670Z
Learning: In Romanian locale files (ro), "Top" is an acceptable translation for directional terms like "top_direction" rather than the native Romanian "Sus". The maintainer benlife5 confirmed this is the preferred translation in packages/visual-editor/locales/ro/visual-editor.json.

Applied to files:

  • packages/visual-editor/locales/platform/cs/visual-editor.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: call_unit_test / unit_tests (24.x)
  • GitHub Check: call_unit_test / unit_tests (22.x)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: create-dev-release
  • GitHub Check: semgrep/ci
🔇 Additional comments (30)
packages/visual-editor/locales/platform/it/visual-editor.json (4)

535-536: LGTM! Pluralization structure is correct for Italian.

The localisations correctly use _one and _other suffixes for Italian pluralization, and the {{mile}} placeholder will dynamically resolve to the appropriate singular or plural form at runtime.


541-542: LGTM! Mile pluralization keys added correctly.

The singular and plural forms for "mile" are properly defined and will be used to populate the {{mile}} placeholder in distance-related strings.


651-659: LGTM! Social links structure is well-organized.

The new socialLinks object provides accessible labels for social media platforms, following a consistent pattern suitable for aria-labels.


748-749: LGTM! Reviews pluralization implemented correctly.

The Italian singular and plural forms for reviews are properly configured with the {{count}} placeholder.

packages/visual-editor/package.json (2)

57-61: Well-structured i18n workflow scripts.

The new script structure effectively supports the PR's objective of separating platform and component translations. The workflow is logical (scan → translate → copy-platform-translations → fix-plurals), and each script has a clear, single responsibility. The use of command chaining with && ensures proper error propagation.


57-61: The referenced TypeScript script files exist in the correct location.

All three scripts (copyPlatformTranslations.ts, deleteTranslationKeys.ts, fixPlurals.ts) are present in packages/visual-editor/scripts/ and will be found correctly when the npm scripts execute from the packages/visual-editor/package.json context.

Likely an incorrect or invalid review comment.

packages/visual-editor/locales/platform/nb/visual-editor.json (6)

80-80: LGTM! Email translations are consistent.

The Norwegian translations for email-related fields are accurate and consistent throughout the file.

Also applies to: 160-160, 222-225, 521-521, 584-584


535-536: LGTM! Distance string translations use correct placeholders.

The translations properly incorporate the {{mile}} placeholder and follow correct Norwegian grammar for singular/plural entity counts.

Also applies to: 546-546


651-659: LGTM! Social media ARIA labels are well-translated.

The social links object provides appropriate Norwegian translations with consistent formatting. The variation for YouTube ("Abonner på vår YouTube-kanal") is contextually appropriate for a video platform.


748-749: LGTM! Review count pluralization is correct for Norwegian.

Norwegian Bokmål correctly uses only "one" and "other" plural forms. The translations are accurate: "vurdering" (singular) and "anmeldelser" (plural).


503-503: LGTM! Hero banner translation is appropriate.

"Heltebanner" is a correct and natural Norwegian translation.


574-574: LGTM! Preset image labels are correctly translated.

Brand names are appropriately preserved in English while UI terms like "next" are properly translated to "Neste".

Also applies to: 596-596, 606-606

packages/visual-editor/locales/platform/es/visual-editor.json (4)

534-535: LGTM! Proper pluralization and consistent distance terminology.

The addition of mile_one and mile_other keys with proper Spanish translations ("milla"/"millas") and their integration using {{mile}} placeholders in distance-related strings follows i18n best practices and maintains grammatical correctness.

Also applies to: 540-541, 545-545


554-554: LGTM! Proper grammar and brand name corrections.

These updates fix grammatical errors and properly handle brand names:

  • "Abierto 24 horas" corrects the grammar (adjective vs. verb)
  • Brand names (App Store, Apple, Galaxy Store, Uber Eats) are correctly kept in English
  • "Flecha arriba" fixes the obvious translation error

Also applies to: 571-571, 573-573, 577-577, 586-586, 605-605


650-658: LGTM! Well-structured social links translations.

The new socialLinks object provides consistent, grammatically correct Spanish translations with appropriate context for each social media platform.


747-748: AI summary mentions missing totalReviews_zero key.

The AI-generated summary states that totalReviews_zero was added with value "{{count}} reseñas", but this key is not present in the code. Only totalReviews_one and totalReviews_other are defined.

packages/visual-editor/locales/platform/lt/visual-editor.json (4)

659-667: LGTM! Social links translations are well-structured.

The socialLinks object provides clear, contextually appropriate Lithuanian labels for social media platforms. The translations maintain consistency in tone and follow proper Lithuanian grammar.


582-582: LGTM! Brand name updates follow best practices.

Keeping brand names in their original form ("Apple", "Uber Eats") rather than translating them is the correct approach for international brand recognition and consistency.

Also applies to: 614-614


756-759: LGTM! Review count pluralization is complete.

The addition of missing plural forms (_few, _many, _other) for totalReviews ensures proper grammatical agreement for all numeric values in Lithuanian. This completes the pluralization pattern used throughout the file.


539-542: LGTM! Pluralization implementation is correctly configured.

The distance and mile pluralization forms follow i18next conventions with appropriate Lithuanian translations. The {{mile}} token integration allows for grammatically correct distance expressions.

i18next v25.x automatically handles Lithuanian pluralization rules via the ECMAScript Intl.PluralRules standard, which supports the four forms required for Lithuanian: _one, _few, _many, and _other. The codebase includes a fixPlurals.ts script that validates plural forms for each locale (including special handling for Lithuanian's fractional plural rule), ensuring the configuration is correct.

packages/visual-editor/locales/components/nb/visual-editor.json (1)

1-113: LGTM - Structure and i18next patterns are correct.

The JSON structure is valid, interpolation variables are properly formatted, and pluralization patterns follow i18next conventions for Norwegian Bokmål locale. The nested objects (presetImages, socialLinks) are correctly structured for the new component-scoped translation loading approach.

packages/visual-editor/locales/platform/cs/visual-editor.json (4)

666-674: LGTM: Social links translations look correct.

The new socialLinks object is properly structured with consistent Czech translations. The variation in the YouTube translation ("Přihlaste se k odběru" vs "Sledujte nás") appropriately reflects the difference between subscribing to a channel versus following on social media.


763-766: Total reviews pluralization follows Czech grammar rules.

The plural forms for totalReviews correctly follow Czech CLDR plural rules and use appropriate grammatical forms. The structure mirrors the established pattern used elsewhere in the file.


546-549: Mile-based distance translations are properly structured.

The distance-related translations correctly interpolate the new {{mile}} variable alongside {{distance}}, {{count}}, and {{name}} variables. The implementation aligns with the PR's objective to introduce mile-based framing across locales.

Also applies to: 561-561


554-557: No changes needed; Czech plural forms for "mile" are correct.

The translations follow proper Czech grammar rules: nominative singular "míle" for one (1), nominative plural "míle" for few (2-4), and genitive plural "mil" for many (fractional) and other (0, 5+) counts.

packages/visual-editor/locales/platform/pt/visual-editor.json (5)

574-574: LGTM! Brand names correctly preserved.

The brand names "Apple" and "Uber Eats" are appropriately not translated, maintaining brand identity and international recognition.

Also applies to: 606-606


651-659: LGTM! Social links enhance accessibility.

The new socialLinks section provides Portuguese aria-labels for social media platforms, improving accessibility for Portuguese-speaking users. The translations appropriately use "Siga-nos" (Follow us) for most platforms and "Inscreva-se" (Subscribe) for YouTube, which is contextually appropriate.


748-749: LGTM! Review count pluralization correctly implemented.

The totalReviews pluralization correctly distinguishes between singular "comentário" and plural "comentários", following i18next conventions and proper Portuguese grammar.


503-503: Translation updates look appropriate.

The updated translations for heroBanner, loadingMap, and loadingReviews appear grammatically correct. If these translations haven't been reviewed by a native Portuguese speaker or through your established localization process, consider doing so to ensure linguistic accuracy and cultural appropriateness.

Also applies to: 526-526, 528-528


535-536: Mile pluralization correctly implemented with proper Portuguese rules.

The addition of mile_one ("milha") and mile_other ("milhas") keys and their integration into distance-related strings using the {{mile}} token follows i18next pluralization conventions correctly. Portuguese uses only the "one" and "other" plural forms (verified via Intl.PluralRules), which are properly provided. The refactoring enables correct grammatical agreement based on distance values.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/visual-editor/locales/platform/ja/visual-editor.json (1)

647-655: New social links translations look good, with minor spacing inconsistency.

The social media follow prompts are natural and appropriate. However, there's a minor spacing inconsistency worth noting for polish:

  • Lines 649 and 652 (instagram, tiktok) are missing a space before "で", while other entries have it
✨ Optional fix for spacing consistency
   "socialLinks": {
     "facebook": "Facebook でフォローしてください",
-    "instagram": "Instagramでフォローしてください",
+    "instagram": "Instagram でフォローしてください",
     "linkedIn": "LinkedIn でフォローしてください",
     "pinterest": "Pinterest でフォローしてください",
-    "tiktok": "TikTokでフォローしてください",
+    "tiktok": "TikTok でフォローしてください",
     "xLink": "X (Twitter) でフォローしてください",
     "youtube": "YouTube チャンネルを購読してください"
   },
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a45b49 and 8ab199a.

⛔ Files ignored due to path filters (12)
  • packages/visual-editor/src/components/testing/screenshots/NearbyLocationsSection/[mobile] default props with multiple nearby locations.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/NearbyLocationsSection/[mobile] version 10 with multiple nearby locations.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/NearbyLocationsSection/[mobile] version 36 with multiple nearby locations.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 10 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 39 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 10 props with French locale (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 10 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[mobile] version 39 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] default props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 10 props with French locale (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 10 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[tablet] version 39 props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (5)
  • packages/visual-editor/locales/components/it/visual-editor.json
  • packages/visual-editor/locales/components/ja/visual-editor.json
  • packages/visual-editor/locales/platform/it/visual-editor.json
  • packages/visual-editor/locales/platform/ja/visual-editor.json
  • packages/visual-editor/locales/platform/tr/visual-editor.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/visual-editor/locales/components/ja/visual-editor.json
  • packages/visual-editor/locales/components/it/visual-editor.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: semgrep/ci
🔇 Additional comments (10)
packages/visual-editor/locales/platform/ja/visual-editor.json (3)

61-61: Excellent translation improvements for business status terms!

The updated translations are much more natural and appropriate for business contexts:

  • "閉じる" (close) is the correct verb form
  • "閉店" (closed) and "営業中" (openNow) are standard business terms
  • "一時休業中" (temporarilyClosed) accurately conveys temporary closure status

Also applies to: 64-65, 554-554, 662-662


570-570: Great corrections to preset image names!

The preset image translations are now accurate:

  • Brand names (Apple, Galaxy Store, Uber Eats) now use proper English names as is standard practice
  • "ハート" (heart symbol) is more appropriate than "心臓" (anatomical heart) for an icon
  • "再生" (play media) correctly replaces "遊ぶ" (play/have fun) for media controls

Also applies to: 583-584, 588-588, 595-595, 602-602


533-533: LGTM: Placeholder refactoring aligns well with Japanese grammar!

The new placeholder pattern for distance units and counts is well-designed:

  • Using {{mile}} placeholder allows dynamic unit display and better i18n flexibility
  • Japanese grammatically doesn't distinguish plural forms, so using only _other suffix is appropriate
  • The removal of _few/_many/_one/_zero variants (mentioned in the summary) correctly reflects Japanese language rules

Also applies to: 538-538, 542-542, 744-744

packages/visual-editor/locales/platform/it/visual-editor.json (1)

47-47: Italian translations look correct.

The translations follow proper Italian grammar and i18next conventions:

  • Mile pluralization: "miglio" (singular) and "miglia" (plural) are correct
  • Location counts: "posizione" (singular) and "località" (plural) properly pluralized
  • All distance-related strings correctly use the {{mile}} placeholder which is populated at runtime with the appropriate singular/plural form based on the distance value

The runtime code in Locator.tsx correctly passes the distance value as the count parameter when translating the mile key, ensuring proper pluralization by distance.

packages/visual-editor/locales/platform/tr/visual-editor.json (6)

555-555: LGTM: Hours status translations are correct.

The Turkish translations for open24Hours and openNow are grammatically correct and appropriate for UI context.

Also applies to: 558-558


574-578: LGTM: Brand names and icon labels are correctly handled.

Brand names (Apple, Figma, Galaxy Store) are appropriately kept in their original form, and arrow direction translations follow correct Turkish grammar.

Also applies to: 586-587


617-617: LGTM: Search refinement translation is appropriate.

The Turkish translation "Aramanızı hassaslaştırın" is grammatically correct and uses appropriate phrasing for UI context.


651-659: LGTM: socialLinks section and additional translations are well-structured.

The new socialLinks object at the platform level aligns with the PR's objective to restructure i18n. The Turkish translations correctly use locative case suffixes ('ta/'de) and appropriate verbs for each platform. The totalReviews_other and visitPage translations are also grammatically correct.

Also applies to: 749-749, 758-758


304-304: No action needed for this key.

The key "base_text size" is used consistently across all 26+ locale files in the platform scope (en, de, fr, es, zh, ja, tr, pt, it, nl, and others). The embedded space is intentional and supported by i18next. This is not a translation issue.

Likely an incorrect or invalid review comment.


531-536: Turkish pluralization pattern is correctly implemented for i18next.

The _one/_other suffix pattern is the correct approach for Turkish in i18next, which recognizes two plural categories (one and other). The implementation correctly uses {{count}} placeholders, and the translation entries for mile_one and mile_other both use "mil" as expected—Turkish does not modify this word for plural forms. The {{distance}} {{mile}} placeholder pattern is consistent across related keys.

@benlife5 benlife5 mentioned this pull request Jan 9, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Fix all issues with AI agents
In @packages/visual-editor/locales/platform/ja/visual-editor.json:
- Around line 653-661: The socialLinks JSON has inconsistent spacing around the
brand name and the Japanese particle/phrase; update the values under the
"socialLinks" object (keys: "instagram", "tiktok") to include a space after the
brand name to match others (e.g., change "Instagramでフォローしてください" → "Instagram
でフォローしてください" and "TikTokでフォローしてください" → "TikTok でフォローしてください"), leaving the
already-correct entries ("facebook", "linkedIn", "pinterest", "xLink",
"youtube") unchanged.
- Around line 539-548: The two translation keys
"locationsWithinDistanceOf_other" and "nearbyLocationsEmptyState" have
inconsistent spacing around the {{mile}} placeholder; update the value of
"nearbyLocationsEmptyState" to include the same space before "以内" (i.e., change
`{{mile}}以内` to `{{mile}} 以内`) so both strings render consistently.

In @packages/visual-editor/locales/platform/nb/visual-editor.json:
- Around line 754-755: The singular/plural pair for totalReviews is
inconsistent: "totalReviews_one" uses "vurdering" while "totalReviews_other"
uses "anmeldelser"; choose one base word and make both forms consistent (either
"anmeldelse" → set totalReviews_one to "{{count}} anmeldelse" and
totalReviews_other to "{{count}} anmeldelser", or "vurdering" → set
totalReviews_one to "{{count}} vurdering" and totalReviews_other to "{{count}}
vurderinger"), and update the values for the keys totalReviews_one and
totalReviews_other accordingly.
- Around line 547-548: Replace the incorrect English plural "mile" with the
Norwegian invariable "mil" for the JSON key "mile_other" (keeping "mile_one" as
"mil") in both nb visual-editor locale files (platform and components); update
the "mile_other" value from "mile" to "mil" so both "mile_one" and "mile_other"
are "mil".

In @packages/visual-editor/locales/platform/nl/visual-editor.json:
- Around line 507-515: Remove the "twitter" property from the footerSocialLinks
object in visual-editor.json so it matches other locales; locate the
"footerSocialLinks" block (containing keys like "facebook", "instagram",
"linkedIn", "pinterest", "tiktok", "youtube") and delete the "twitter" entry,
then verify the X/Twitter link is handled via the "socialLinks" block using
"xLink" elsewhere in the locale file.

In @packages/visual-editor/locales/platform/sk/visual-editor.json:
- Around line 554-557: The locale entry "mile_many" uses the wrong diacritic (L
with acute "ĺ") causing inconsistency; update the value for the "mile_many" key
to use the correct Slovak character (L with caron "ľ") so it matches the
genitive plural form used by "mile_other" and is consistent with
"mile_one"/"mile_few".

In @packages/visual-editor/locales/platform/tr/visual-editor.json:
- Around line 754-755: The singular/plural Turkish translations for totalReviews
are inconsistent: change the value of "totalReviews_other" to use the same term
as "totalReviews_one" so both use "inceleme" (i.e., update the
"totalReviews_other" entry currently set to "{{count}} yorum" to "{{count}}
inceleme") so singular and plural use consistent terminology; keep the keys
"totalReviews_one" and "totalReviews_other" unchanged.
🧹 Nitpick comments (3)
packages/visual-editor/locales/platform/hu/visual-editor.json (2)

541-542: Inconsistent sentence structure between singular and plural forms.

The _one and _other variants have different word order and structure:

  • _one: places {{name}} before the distance phrase
  • _other: places {{name}} at the end after "a következőtől:"

This inconsistency may cause a jarring user experience when the count changes between 1 and other values. Consider aligning the sentence structure for both variants.


657-665: Minor inconsistency: missing article for Instagram.

Line 659 uses "Instagramon" without an article, while other social platforms include the article (e.g., "a Facebookon", "a LinkedIn-en", "az X-en"). For consistency, consider using "az Instagramon".

Suggested fix
 "socialLinks": {
   "facebook": "Kövess minket a Facebookon",
-  "instagram": "Kövess minket Instagramon",
+  "instagram": "Kövess minket az Instagramon",
   "linkedIn": "Kövess minket a LinkedIn-en",
packages/visual-editor/src/editor/Editor.tsx (1)

169-171: Consider propagating translation load errors.

The current error handling logs to console but the failure is otherwise silent. While the editor can likely function with fallback translations, consider whether translation load failures should:

  • Be reported via sendError for observability in the platform
  • Update some state to potentially show a subtle warning

This is a low-priority consideration since fallback behavior is likely acceptable.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0e687f and 2cf01f0.

📒 Files selected for processing (29)
  • packages/visual-editor/locales/platform/cs/visual-editor.json
  • packages/visual-editor/locales/platform/da/visual-editor.json
  • packages/visual-editor/locales/platform/de/visual-editor.json
  • packages/visual-editor/locales/platform/en-GB/visual-editor.json
  • packages/visual-editor/locales/platform/en/visual-editor.json
  • packages/visual-editor/locales/platform/es/visual-editor.json
  • packages/visual-editor/locales/platform/et/visual-editor.json
  • packages/visual-editor/locales/platform/fi/visual-editor.json
  • packages/visual-editor/locales/platform/fr/visual-editor.json
  • packages/visual-editor/locales/platform/hr/visual-editor.json
  • packages/visual-editor/locales/platform/hu/visual-editor.json
  • packages/visual-editor/locales/platform/it/visual-editor.json
  • packages/visual-editor/locales/platform/ja/visual-editor.json
  • packages/visual-editor/locales/platform/lt/visual-editor.json
  • packages/visual-editor/locales/platform/lv/visual-editor.json
  • packages/visual-editor/locales/platform/nb/visual-editor.json
  • packages/visual-editor/locales/platform/nl/visual-editor.json
  • packages/visual-editor/locales/platform/pl/visual-editor.json
  • packages/visual-editor/locales/platform/pt/visual-editor.json
  • packages/visual-editor/locales/platform/ro/visual-editor.json
  • packages/visual-editor/locales/platform/sk/visual-editor.json
  • packages/visual-editor/locales/platform/sv/visual-editor.json
  • packages/visual-editor/locales/platform/tr/visual-editor.json
  • packages/visual-editor/locales/platform/zh-TW/visual-editor.json
  • packages/visual-editor/locales/platform/zh/visual-editor.json
  • packages/visual-editor/package.json
  • packages/visual-editor/src/editor/Editor.tsx
  • packages/visual-editor/src/internal/puck/components/LayoutHeader.tsx
  • packages/visual-editor/src/utils/VisualEditorProvider.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/visual-editor/package.json
  • packages/visual-editor/src/internal/puck/components/LayoutHeader.tsx
  • packages/visual-editor/locales/platform/hr/visual-editor.json
  • packages/visual-editor/locales/platform/cs/visual-editor.json
  • packages/visual-editor/src/utils/VisualEditorProvider.tsx
  • packages/visual-editor/locales/platform/it/visual-editor.json
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-12-23T16:36:42.670Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/locales/ro/visual-editor.json:394-394
Timestamp: 2025-12-23T16:36:42.670Z
Learning: In Romanian locale files (ro), "Top" is an acceptable translation for directional terms like "top_direction" rather than the native Romanian "Sus". The maintainer benlife5 confirmed this is the preferred translation in packages/visual-editor/locales/ro/visual-editor.json.

Applied to files:

  • packages/visual-editor/locales/platform/ro/visual-editor.json
  • packages/visual-editor/locales/platform/hu/visual-editor.json
📚 Learning: 2025-12-12T20:17:37.033Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 946
File: packages/visual-editor/src/components/migrations/migrationRegistry.ts:47-48
Timestamp: 2025-12-12T20:17:37.033Z
Learning: In the yext/visual-editor repository, empty object placeholders (`{}`) in the migrationRegistry array are sometimes intentionally used to reserve migration slots for parallel PRs to prevent merge conflicts and maintain consistent migration indices.

Applied to files:

  • packages/visual-editor/locales/platform/en-GB/visual-editor.json
📚 Learning: 2025-12-15T20:59:25.571Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 943
File: packages/visual-editor/src/components/contentBlocks/image/Image.tsx:175-191
Timestamp: 2025-12-15T20:59:25.571Z
Learning: In the ImageWrapper component at packages/visual-editor/src/components/contentBlocks/image/Image.tsx, when an Image is wrapped in a MaybeLink, the aria-label should be omitted because the image's alt text serves as the accessible name for the link. Adding an aria-label would override the image's alt text.

Applied to files:

  • packages/visual-editor/locales/platform/en/visual-editor.json
📚 Learning: 2025-12-23T16:30:55.497Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:55.497Z
Learning: In CompactHero and CompactPromo components, duplicate IDs on image elements are intentional: conditional visibility classes (hidden/sm:block/sm:hidden) ensure only one image renders at a time based on responsive breakpoints and desktopImagePosition/mobileImagePosition settings.

Applied to files:

  • packages/visual-editor/locales/platform/en/visual-editor.json
🧬 Code graph analysis (1)
packages/visual-editor/src/editor/Editor.tsx (2)
packages/visual-editor/src/utils/i18n/platform.ts (2)
  • loadPlatformTranslations (58-73)
  • i18nPlatformInstance (7-7)
packages/visual-editor/src/utils/i18n/index.ts (1)
  • i18nPlatformInstance (3-3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: call_unit_test / unit_tests (22.x)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: call_unit_test / unit_tests (24.x)
  • GitHub Check: semgrep/ci
  • GitHub Check: create-dev-release
🔇 Additional comments (67)
packages/visual-editor/locales/platform/pl/visual-editor.json (1)

1-777: LGTM! Polish translation file with proper pluralization.

The translation updates are well-structured:

  • The mile_* and totalReviews_* plural forms correctly follow Polish CLDR pluralization rules (one/few/many/other)
  • The {{mile}} token pattern in distance-related strings properly enables nested pluralization
  • The new socialLinks section provides consistent platform labels
packages/visual-editor/locales/platform/ro/visual-editor.json (3)

543-556: LGTM! Distance-based pluralization pattern is correctly implemented.

The approach of using {{mile}} as a separate interpolation variable in locationsWithinDistanceOf_* and nearbyLocationsEmptyState allows proper dual-dimension pluralization—count for locations and a separately resolved mile_* key for distance. Romanian plural forms (one/few/other) are correctly applied.


661-669: LGTM! Social links accessibility labels.

The new socialLinks object provides clear, screen-reader-friendly labels for each social platform. Translations are appropriate for Romanian.


758-760: LGTM! Review count pluralization.

The totalReviews_* keys correctly use Romanian plural forms (one/few/other) and the translations are accurate.

packages/visual-editor/locales/platform/lv/visual-editor.json (4)

543-545: LGTM! Proper plural handling with externalized unit.

The refactoring to use {{mile}} interpolation alongside the new mile_one, mile_other, and mile_zero keys enables correct grammatical agreement in Latvian. The plural forms follow Latvian grammar rules correctly (singular "jūdze" vs. plural "jūdzes").

Also applies to: 550-552, 556-556


584-584: LGTM! Brand names correctly kept in English.

Proper translation practice to use original brand names ("Apple", "Galaxy Store", "Uber Eats") rather than translated versions. This ensures brand consistency and recognition.

Also applies to: 597-597, 616-616


661-669: LGTM! Social links labels added.

The socialLinks object provides appropriate Latvian call-to-action text for each social platform, maintaining consistency with the platform-level translation structure across other locales.


758-760: LGTM! Review count pluralization is correct.

The totalReviews_* keys properly handle Latvian plural forms with singular "atsauksme" and plural "atsauksmes" for both zero and other cases.

packages/visual-editor/locales/platform/pt/visual-editor.json (5)

580-580: LGTM! Brand names correctly preserved.

Keeping "Apple" and "Uber Eats" as untranslated brand names is the correct approach. The previous translations ("Maçã" and "Uber come") were incorrect.

Also applies to: 612-612


541-542: LGTM! Mile pluralization implemented correctly.

The Portuguese plural forms (milha/milhas) follow CLDR rules, and the {{mile}} placeholder integration enables proper grammatical agreement in distance-related strings.

Also applies to: 547-548, 552-552


657-665: LGTM! Social links translations are well-crafted.

The Portuguese translations correctly use "Siga-nos no [Platform]" (Follow us on) pattern, with an appropriate variation for YouTube ("Inscreva-se em nosso canal" - Subscribe to our channel).


754-755: Verify translation choice for "reviews".

The translation uses "comentário(s)" (comment/comments) rather than "avaliação/avaliações" (review/evaluation). While "comentário" is commonly used in Brazilian Portuguese for reviews, you may want to confirm this aligns with the intended terminology across the application.


509-509: LGTM!

Translation updates for hero banner and loading states are correct.

Also applies to: 532-534

packages/visual-editor/locales/platform/zh/visual-editor.json (4)

539-548: LGTM! Correct pluralization for Chinese locale.

The Chinese locale correctly uses only mile_other since Chinese follows CLDR "no plural" rules — i18next will use this form for all count values. The {{mile}} token pattern properly delegates pluralization handling to the runtime.


574-608: LGTM! Brand names correctly kept in English.

Trademarked names (App Store, Apple, Figma, Galaxy Store, Uber Eats) are appropriately preserved in English across locales for brand consistency — this is standard i18n practice.


653-661: LGTM! Natural and idiomatic translations for social links.

The social media follow prompts use appropriate Chinese phrasing patterns while correctly preserving platform brand names in English. The structure is consistent with the broader platform locale changes described in the PR.


750-750: LGTM!

The translation correctly uses the Chinese measure word for counting reviews, and appropriately provides only the _other form for this no-plural-distinction locale.

packages/visual-editor/locales/platform/de/visual-editor.json (5)

25-26: LGTM!

The translations are accurate. "ALT-Text" correctly maintains the technical acronym format, and "Für alle Standorte anwenden" properly conveys the apply-all action.


546-557: LGTM!

The tokenized {{mile}} approach with mile_one/mile_other is a clean pattern for handling German singular/plural agreement. This correctly replaces the redundant _few/_many/_zero forms that aren't needed for German pluralization rules.


662-670: LGTM!

The social links translations are well done. The "(Twitter)" clarification on xLink aids user recognition, and using "Abonnieren" for YouTube appropriately reflects the platform's subscription model versus the "Folgen Sie uns" pattern used for other social platforms.


759-760: LGTM!

Correct German singular/plural forms: "Rezension" (singular) and "Rezensionen" (plural) following proper i18next pluralization patterns.


79-79: Translation is acceptable.

"Brotkrumen" is a valid literal translation. Note that in German technical/UI contexts, the English loanword "Breadcrumbs" is also commonly used. Either is acceptable depending on the desired localization style (fully translated vs. technical terminology).

packages/visual-editor/locales/platform/hu/visual-editor.json (3)

547-548: LGTM!

Using the same form "mérföld" for both singular and plural is correct for Hungarian grammar, as nouns remain in singular form when preceded by numbers.


587-612: LGTM!

Good corrections to the preset image labels:

  • Brand names now properly preserved (Dribbble, Figma, Uber Eats)
  • UI action terms corrected to appropriate Hungarian translations (Bezárás, Lejátszás, Megosztás)

754-755: LGTM!

Correctly uses the same form "vélemény" for both singular and plural, consistent with Hungarian grammar rules.

packages/visual-editor/locales/platform/zh-TW/visual-editor.json (4)

575-577: Good improvement: Brand names now use original form instead of translations.

Keeping brand names like "App Store", "Apple", "Figma", "Galaxy Store", and "Uber Eats" in their original form is the correct localization practice. The previous translations (e.g., "無花果" for Figma, which means "fig fruit") were incorrect.

Also applies to: 589-591, 609-609


654-662: LGTM!

The social links translations are appropriate for Traditional Chinese and follow the established pattern for social media follow prompts.


751-751: LGTM!

The totalReviews_other translation "{{count}} 條評論" correctly uses the Chinese counter word "條" for reviews.


540-549: No action needed—the mile_other key-only pattern is correct for Chinese.

Chinese (both Simplified and Traditional) and Japanese only define _other plural forms because these languages lack grammatical singular/plural distinction. This is consistent with CLDR plural rules and matches the implementation in all other Chinese and Japanese locales. i18next correctly handles this language-specific behavior without requiring a _one variant.

packages/visual-editor/locales/platform/fr/visual-editor.json (2)

540-541: Consistent mile tokenization applied.

The distance-related strings now use {{mile}} token with proper French pluralization (mile_one: "mile", mile_other: "miles"). This approach correctly handles singular/plural forms and aligns with the broader refactoring to support runtime translation loading.

Also applies to: 547-548, 552-552


657-665: socialLinks object provides accessible labels.

The new socialLinks object with French translations for social media platforms (Facebook, Instagram, LinkedIn, Pinterest, TikTok, X, YouTube) provides proper aria-label content for accessibility.

packages/visual-editor/locales/platform/en-GB/visual-editor.json (1)

540-541: LGTM!

The en-GB locale correctly mirrors the structural changes with mile tokenization (mile_one, mile_other), updated distance strings using {{mile}}, and the new socialLinks object. The English phrasing is appropriate for British English locale.

Also applies to: 546-547, 551-551, 656-664, 754-754

packages/visual-editor/locales/platform/lt/visual-editor.json (2)

553-556: Proper Lithuanian pluralization with four forms.

Lithuanian correctly uses all four plural forms (_one, _few, _many, _other) per CLDR pluralization rules. The mile_* and totalReviews_* keys properly handle Lithuanian's complex plural system:

  • mile_one: "mylia" (singular)
  • mile_few: "mylios" (few)
  • mile_many: "mylios" (many)
  • mile_other: "mylių" (other/genitive plural)

Also applies to: 762-765


588-588: Brand names correctly preserved.

Good update to keep brand names as proper nouns (apple: "Apple", uberEats: "„Uber" Eats") rather than translated terms.

Also applies to: 620-620

packages/visual-editor/locales/platform/fi/visual-editor.json (3)

62-62: Quality improvements to Finnish translations.

Several corrections improve translation accuracy:

  • close: "Lähellä" → "Sulje" (fixed meaning: "Near" → "Close")
  • closeMenu: "Suljevalikko" → "Sulje valikko" (added missing space)
  • closesAt: "Sulkeutuu jhk" → "Sulkeutuu klo" (fixed typo)
  • opensAt: "Avata" → "Avautuu klo" (fixed meaning: "Open" → "Opens at")

Also applies to: 64-64, 66-66, 565-565


580-580: Brand names properly corrected.

Good fixes to preserve brand names as proper nouns:

  • apple: "Omena" → "Apple" (was translated as "Apple" the fruit)
  • figma: "Viikuna" → "Figma" (was translated as "Fig" the fruit)
  • galaxyStore: "Galaksikauppa" → "Galaxy Store"
  • uberEats: "Uber syö" → "Uber Eats"

Also applies to: 592-593, 612-612


625-625: Typo fix in responseFrom template.

Fixed "Vastaus {{businessName}}}""Vastaus {{businessName}}" (removed extra closing brace).

packages/visual-editor/src/editor/Editor.tsx (2)

19-22: LGTM!

Clean import of loadPlatformTranslations alongside i18nPlatformInstance for the async translation loading flow.


154-180: Well-implemented async pattern with race condition guards.

The async translation loading correctly handles:

  1. Unmount safety: isCurrent flag prevents state updates after component unmount
  2. Rapid locale change protection: expectedLocale capture and re-check at lines 163-166 prevents applying stale translations if locale changes during the async load
  3. Proper cleanup: Effect cleanup sets isCurrent = false

The double-guard pattern (isCurrent && templateMetadata?.platformLocale === expectedLocale) is a robust approach for handling async operations in React effects.

packages/visual-editor/locales/platform/da/visual-editor.json (1)

664-672: LGTM!

The socialLinks block with Danish translations for social media platform accessibility labels looks good.

packages/visual-editor/locales/platform/en/visual-editor.json (2)

540-547: LGTM!

The English pluralization for miles (mile_one: "mile", mile_other: "miles") and the updated distance strings using the {{mile}} token are correct. This provides a clean interpolation pattern for distance-related messages.


656-664: LGTM!

The new socialLinks block with accessibility labels for social media platforms is well-structured and provides clear, user-friendly text.

packages/visual-editor/locales/platform/es/visual-editor.json (2)

546-551: LGTM!

Spanish pluralization for miles (milla/millas) is correct, and the distance-related strings properly use the {{mile}} token for interpolation.


656-664: LGTM!

The Spanish socialLinks translations are appropriate and follow natural phrasing for the language.

packages/visual-editor/locales/platform/et/visual-editor.json (2)

547-552: LGTM!

Estonian pluralization for miles (miil/miili) correctly uses the partitive plural form, and the distance strings properly utilize the {{mile}} token.


657-665: LGTM!

The Estonian socialLinks translations are well-formed and use appropriate phrasing for the language.

packages/visual-editor/locales/platform/sv/visual-editor.json (2)

548-553: LGTM!

Swedish pluralization (mil/mil being identical) is grammatically correct. Note that the Swedish "mil" traditionally refers to the Scandinavian mile (~10 km) rather than the English mile (~1.6 km), but this is a localization decision that may be intentional depending on the target audience.


658-666: LGTM!

The Swedish socialLinks translations are well-formed and use natural phrasing.

packages/visual-editor/locales/platform/ja/visual-editor.json (3)

62-66: LGTM!

The translations for close, closed, and closesAt are appropriate Japanese translations for these status-related terms.


560-560: LGTM!

The status translations (openNow, temporarilyClosed) and preset image labels are correctly translated. Brand names (Apple, Galaxy Store, Uber Eats) are appropriately kept in their original English form, which is the standard practice for brand recognition in Japanese localization.

Also applies to: 576-608, 668-668


750-750: LGTM!

The totalReviews_other translation is correct. Using the _other suffix is the appropriate i18next pattern for Japanese, which doesn't have grammatical number distinctions.

packages/visual-editor/locales/platform/sk/visual-editor.json (4)

546-549: LGTM - Mile token integration follows proper i18n patterns.

The separation of {{distance}} and {{mile}} tokens allows for proper pluralization of distance units independently from location counts. This correctly handles Slovak's complex plural forms.


666-674: LGTM - Social links translations are well structured.

The translations appropriately use "Sledujte nás na" (Follow us on) for social platforms and "Prihláste sa na odber" (Subscribe to) for YouTube, reflecting the different user actions expected on each platform.


763-766: LGTM - Slovak plural forms for reviews are correct.

The pluralization follows proper Slovak grammar: "recenzia" (singular), "recenzie" (2-4), and "recenzií" (5+/genitive plural).


551-551: LGTM - Corrected "map" translation.

Changed from "Mapovať" (verb form meaning "to map") to "Mapa" (noun meaning "map"), which is the correct translation for a map UI element label.

packages/visual-editor/locales/platform/nb/visual-editor.json (5)

85-85: LGTM!

Email-related translations are consistent and use the correct Norwegian term "E-post" throughout.

Also applies to: 165-165, 227-230


541-542: LGTM!

The {{mile}} token integration for distance-based strings is implemented correctly, allowing dynamic plural form injection.

Also applies to: 552-552


580-580: LGTM!

Brand names are correctly capitalized ("Apple", "Uber Eats") following proper trademark conventions.

Also applies to: 612-612


657-665: LGTM!

Social links translations are well-structured with appropriate Norwegian phrasing. The YouTube entry correctly uses "Abonner" (Subscribe) rather than "Følg" (Follow), and the X/Twitter branding is properly handled.


509-509: LGTM!

Translations for "heroBanner" → "Heltebanner" and "next" → "Neste" are correct.

Also applies to: 602-602

packages/visual-editor/locales/platform/tr/visual-editor.json (4)

541-548: LGTM!

The new mile_one/mile_other tokens and updated locationsWithinDistanceOf translations correctly preserve placeholders. Using "mil" for both singular and plural forms is appropriate for Turkish, as quantity is expressed via the numeral rather than noun inflection.


552-552: LGTM!

The {{mile}} placeholder is correctly integrated, allowing dynamic unit display consistent with the i18n refactor.


657-665: LGTM!

The new socialLinks block provides appropriate Turkish translations with correct locative suffixes for each platform. The YouTube entry appropriately uses "subscribe" phrasing rather than "follow."


561-623: LGTM!

UI string updates are correct. Arrow direction translations, brand name preservation (Figma, Galaxy Store), and Turkish phrasing for status messages and actions are appropriate.

packages/visual-editor/locales/platform/nl/visual-editor.json (3)

550-551: LGTM! Pluralization and template variable usage is correct.

The mile_one/mile_other keys correctly handle Dutch pluralization ("mijl" vs. "mijlen"), and the {{mile}} interpolation in locationsWithinDistanceOf_* and nearbyLocationsEmptyState is properly consistent.

Also applies to: 556-557, 561-561


763-764: LGTM! Review count pluralization is correct.

The totalReviews_one ("recensie") and totalReviews_other ("recensies") properly handle Dutch singular/plural forms.


1-775: Overall translation file looks good.

The Dutch translations appear linguistically appropriate, JSON structure is valid, and the i18next pluralization conventions (_one, _other) are correctly applied for the Dutch locale.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @packages/visual-editor/locales/platform/ja/visual-editor.json:
- Line 548: The translation key nearbyLocationsEmptyState contains a malformed
placeholder "{{mile} }" which breaks i18n interpolation; update the value so
each mustache placeholder is properly formed (e.g., change "{{mile} }" to
"{{mile}}") and verify the other placeholders ("{{radius}}", "{{entityType}}")
are correctly formatted with no extra spaces.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2cf01f0 and 9fd214b.

⛔ Files ignored due to path filters (9)
  • packages/visual-editor/src/components/testing/screenshots/BreadcrumbsSection/[tablet] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/BreadcrumbsSection/[tablet] version 4 props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/BreadcrumbsSection/[tablet] version 8 with non-default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [compact] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[xl] [compact] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] default props with document data (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 10 props with French locale (after interactions).png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/ReviewsSection/[desktop] version 10 props with French locale.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (11)
  • packages/visual-editor/locales/components/da/visual-editor.json
  • packages/visual-editor/locales/components/ja/visual-editor.json
  • packages/visual-editor/locales/components/nb/visual-editor.json
  • packages/visual-editor/locales/components/sk/visual-editor.json
  • packages/visual-editor/locales/components/tr/visual-editor.json
  • packages/visual-editor/locales/platform/da/visual-editor.json
  • packages/visual-editor/locales/platform/ja/visual-editor.json
  • packages/visual-editor/locales/platform/nb/visual-editor.json
  • packages/visual-editor/locales/platform/nl/visual-editor.json
  • packages/visual-editor/locales/platform/sk/visual-editor.json
  • packages/visual-editor/locales/platform/tr/visual-editor.json
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/visual-editor/locales/components/sk/visual-editor.json
  • packages/visual-editor/locales/platform/nb/visual-editor.json
  • packages/visual-editor/locales/components/tr/visual-editor.json
  • packages/visual-editor/locales/platform/da/visual-editor.json
  • packages/visual-editor/locales/components/da/visual-editor.json
  • packages/visual-editor/locales/components/ja/visual-editor.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: call_unit_test / unit_tests (22.x)
  • GitHub Check: call_unit_test / unit_tests (24.x)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: create-dev-release
  • GitHub Check: semgrep/ci
🔇 Additional comments (17)
packages/visual-editor/locales/components/nb/visual-editor.json (1)

1-113: LGTM! Norwegian locale file structure is correct.

The JSON structure is valid, pluralization suffixes follow i18next conventions (_one, _other), and interpolation placeholders are consistent across plural forms.

Minor observation: There's some terminology variation for "location" across different keys (plassering, lokasjoner, sted, steder). This may be intentional for natural Norwegian phrasing in different contexts, but consider verifying with a native speaker for consistency if desired.

packages/visual-editor/locales/platform/sk/visual-editor.json (5)

554-557: Slovak pluralization for "mile" looks correct.

The four plural forms follow Slovak grammar rules:

  • mile_one: "míľa" (1)
  • mile_few: "míle" (2-4)
  • mile_many: "míľ" (5+, decimals)
  • mile_other: "míľ" (fallback)

This pattern correctly handles Slovak noun declension for distance units.


546-549: Good use of interpolated {{mile}} token for noun-number agreement.

Using a separate pluralized token for "mile" that gets interpolated into the distance strings is the correct approach for Slavic languages where nouns must agree with numbers in grammatical form. This allows:

  • locationsWithinDistanceOf_* to focus on location count pluralization
  • mile_* to handle the distance unit pluralization independently

This is cleaner than trying to handle compound pluralization in a single string.

Also applies to: 561-561


763-766: Review pluralization for "totalReviews".

The Slovak pluralization appears correct:

  • totalReviews_one: "{{count}} recenzia"
  • totalReviews_few: "{{count}} recenzie"
  • totalReviews_many: "{{count}} recenzií"
  • totalReviews_other: "{{count}} recenzií"

The feminine noun "recenzia" correctly declines through its plural forms.


666-674: New socialLinks object with accessible labels.

The social media follow prompts are well-structured and provide meaningful accessible labels for screen readers (e.g., "Sledujte nás na Facebooku" = "Follow us on Facebook").


1-777: LGTM - Slovak localization file is well-structured.

The translation file correctly implements:

  • Valid JSON structure
  • Proper Slovak plural forms (one/few/many/other) for i18next
  • Consistent {{placeholder}} interpolation syntax
  • New keys for social links, distance units, and UI elements

No issues identified with the localization changes.

packages/visual-editor/locales/platform/nl/visual-editor.json (5)

66-66: LGTM!

The translation updates are consistent:

  • getDirections is uniformly translated to "Routebeschrijving" across all three occurrences (lines 106, 150, 508)
  • Hero-related keys are now properly localized to Dutch ("Heldenbanner", "Held koptekst", "Heldenbeeld") instead of the previous English-based translations
  • closesAt update to "Sluit om" is more natural Dutch phrasing

Also applies to: 106-106, 150-150, 508-511


541-542: LGTM!

The mile pluralization implementation is well-structured:

  • mile_one ("mijl") and mile_other ("mijlen") provide proper Dutch singular/plural forms
  • Using the {{mile}} token in locationsWithinDistanceOf and nearbyLocationsEmptyState allows i18next to select the correct plural form based on the distance value
  • This pattern correctly decouples the noun pluralization from the containing sentence

Also applies to: 547-548, 552-552


578-612: LGTM!

Brand names are appropriately preserved ("App Store", "Apple", "Dribbble", "Galaxy Store", "Uber Eats") while generic UI terms are correctly translated to Dutch ("Vinkje", "Sluiten", "Afspelen"). The "Uber Eats" correction from the previous "Uber eet" is appropriate as it's a brand name.


657-665: LGTM!

The socialLinks block is well-structured with consistent Dutch phrasing. Good attention to platform-specific conventions: YouTube uses "Abonneer je" (Subscribe) rather than "Volg" (Follow), which is the appropriate action for that platform. The xLink entry helpfully includes both "X" and "Twitter" for user clarity.


754-755: LGTM!

The totalReviews pluralization correctly uses Dutch singular ("recensie") and plural ("recensies") forms with the {{count}} placeholder.

packages/visual-editor/locales/platform/tr/visual-editor.json (5)

541-542: LGTM! Mile-based distance keys properly added.

The Turkish translations correctly use different grammatical structures for the singular and plural forms:

  • _one: Uses 'a (dative suffix) with "mesafede"
  • _other: Uses 'den (ablative suffix) with "uzaklıktaki"

This follows proper Turkish grammar conventions for attaching suffixes to quoted text.


547-548: LGTM! Turkish pluralization is correct.

Both mile_one and mile_other translate to "mil" — this is correct because Turkish nouns typically don't change form when preceded by a numeral.


552-552: LGTM! Empty state message updated with mile placeholder.

The template now uses {{radius}} {{mile}} for dynamic distance display, aligning with the mile-based terminology introduced across other locales.


657-665: LGTM! Social links block added with proper Turkish translations.

The new socialLinks object provides accessible aria labels for social media links. The translations appropriately localize "Follow us on..." and "Subscribe to our YouTube channel" to Turkish.


754-755: LGTM! Turkish plural forms are consistent.

Both totalReviews_one and totalReviews_other using "inceleme" is correct for Turkish, as the noun form doesn't change based on count when preceded by a numeral.

packages/visual-editor/locales/platform/ja/visual-editor.json (1)

62-66: LGTM!

The remaining translation updates look good:

  • Hour-related strings (close, closed, closesAt, openNow, temporarilyClosed) are contextually appropriate.
  • The mile_other and locationsWithinDistanceOf_other keys properly support the new mile-based placeholders.
  • Brand names (Apple, Galaxy Store, Uber Eats) are correctly kept in English.
  • The socialLinks object provides proper Japanese translations for social media follow prompts.
  • Pluralization keys (totalReviews_other) follow i18next conventions.

Also applies to: 539-544, 560-560, 576-576, 589-590, 594-594, 601-601, 608-608, 653-661, 668-668, 750-750

Copy link
Contributor

@benlife5 benlife5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙌

@mkilpatrick mkilpatrick merged commit e11cb11 into main Jan 12, 2026
18 checks passed
@mkilpatrick mkilpatrick deleted the separate-translations branch January 12, 2026 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

create-dev-release Triggers dev release workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants