Skip to content

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

pages-visual-editor-starter

npm i https://pkg.pr.new/yext/visual-editor/@yext/visual-editor@802

commit: 35c4e8d

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 4, 2025

Walkthrough

This PR reorganizes localization into a two-tier layout: components and platform. The i18next scanner config now reads/writes under locales/platform/{{lng}}/{{ns}}.json. Many new component locale files were added under locales/components/.... Runtime i18n changed from eager filesystem preloading to on-demand dynamic loading and injection via new APIs: loadComponentTranslations(...), loadPlatformTranslations(...), and injectTranslations(...). Scripts (checkEmptyTranslations, generateTranslations, sortTranslations) were updated to traverse nested locale directories. Templates and starter files export transformProps that call injectTranslations. The Vite plugin configureServer hook was removed and build-phase generation/cleanup simplified.

Sequence Diagram(s)

sequenceDiagram
    participant Template as Template (main/locator/directory/dev)
    participant Transform as transformProps
    participant Inject as injectTranslations()
    participant GetComp as getTranslations (components)
    participant I18nComp as i18nComponentsInstance

    Template->>Transform: receives TemplateProps
    Transform->>Inject: call with templateProps
    Inject->>Inject: extract document.locale
    Inject->>GetComp: request component translations for locale
    GetComp->>GetComp: dynamic import locales/components/<lng>/visual-editor.json
    alt import succeeds
        GetComp-->>Inject: return translations
    else import fails
        GetComp->>GetComp: retry with "en"
        GetComp-->>Inject: return fallback translations
    end
    Inject->>I18nComp: loadComponentTranslations(locale, translations)
    I18nComp->>I18nComp: merge resources
    Inject-->>Transform: return augmented props
    Transform-->>Template: enriched TemplateProps
Loading
sequenceDiagram
    participant Editor as Editor.tsx
    participant Meta as templateMetadata (platformLocale)
    participant LoadPlat as loadPlatformTranslations()
    participant I18nPlat as i18nPlatformInstance

    Editor->>Meta: check platformLocale
    alt platformLocale exists
        Editor->>LoadPlat: call with platformLocale
        LoadPlat->>LoadPlat: dynamic import locales/platform/<lng>/<ns>.json
        LoadPlat->>I18nPlat: inject translations
        LoadPlat-->>Editor: done
        Editor->>I18nPlat: changeLanguage(platformLocale)
    else platformLocale absent
        Editor->>I18nPlat: changeLanguage(locale)
    end
Loading
sequenceDiagram
    participant Scanner as i18next-scanner
    participant FS as Filesystem
    note right of Scanner: scanner config updated to platform path
    Scanner->>FS: loadPath/savePath -> locales/platform/{{lng}}/{{ns}}.json
    FS-->>Scanner: read/write platform-scoped JSONs
Loading

Possibly related PRs

Suggested reviewers

  • asanehisa
  • benlife5
  • colton-demetriou

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main objective of the PR: optimizing translation loading to only include necessary translations instead of bundling all of them.
Description check ✅ Passed The description clearly explains the problem being solved: translations were previously bundled and loaded entirely on the frontend, but now only necessary translations are loaded.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch separate-translations

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 60a18fe and 5232d4e.

📒 Files selected for processing (1)
  • packages/visual-editor/src/utils/i18n/fallbacks.ts (2 hunks)
🔇 Additional comments (2)
packages/visual-editor/src/utils/i18n/fallbacks.ts (2)

7-10: LGTM! Standard locale fallback pattern.

The en-US → en fallback aligns well with the PR's optimization goals, ensuring that if region-specific resources are unavailable, the system can defer to general English resources before hitting the global fallback.


35-35: LGTM! Cleaner idiomatic check.

Removing the double negation is a minor style improvement with no functional impact—both versions correctly check for the existence of resources.


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.

@@ -0,0 +1,14 @@
{
"closed": "Geschlossen",
"closesAt": "Schließt bei",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incorrect preposition for time expression.

"Schließt bei" uses "bei" (at a place), but for closing times in German, "um" (at a time) is the correct preposition.

Apply this diff:

-  "closesAt": "Schließt bei",
+  "closesAt": "Schließt um",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"closesAt": "Schließt bei",
"closesAt": "Schließt um",
🤖 Prompt for AI Agents
In packages/visual-editor/locales/components/de/visual-editor.json around line
3, the German translation uses the incorrect preposition "Schließt bei" for a
time expression; replace the string value with "Schließt um" so the entry
becomes the correct time-based phrasing.

@@ -0,0 +1,14 @@
{
"closed": "Suljettu",
"closesAt": "Sulkeutuu jhk",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Incomplete or incorrect translation.

"Sulkeutuu jhk" contains "jhk" which appears to be an abbreviation, placeholder, or typo rather than proper Finnish text. This needs to be verified and corrected with a complete translation.

Please verify this translation with a Finnish speaker or translation service.


I'll verify this translation issue by examining the file and researching the Finnish abbreviation.


🏁 Script executed:

#!/bin/bash
# Check if the file exists and examine its content
if [ -f "packages/visual-editor/locales/components/fi/visual-editor.json" ]; then
  echo "=== File content (first 20 lines) ==="
  head -20 "packages/visual-editor/locales/components/fi/visual-editor.json"
  echo ""
  echo "=== Full file content ==="
  cat "packages/visual-editor/locales/components/fi/visual-editor.json"
else
  echo "File not found"
fi

Length of output: 772


🌐 Web query:

What does "jhk" mean in Finnish? Finnish abbreviations

💡 Result:

"jhk" is an informal/lexicographic abbreviation of johonkin ("to/to some place, to somewhere"). [1][2][3]


Fix incomplete Finnish translation for "closesAt".

The translation "Sulkeutuu jhk" uses the abbreviation "jhk" (short for "johonkin" meaning "to somewhere"), which is inappropriate in user-facing UI text and fails to convey the temporal meaning expected from a "closesAt" field. Replace with a complete, proper Finnish phrase that indicates closing time (e.g., "Sulkeutuu klo" or similar).

🤖 Prompt for AI Agents
In packages/visual-editor/locales/components/fi/visual-editor.json around line
3, the value for "closesAt" ("Sulkeutuu jhk") is an inappropriate abbreviation
and doesn't convey closing time; replace it with a proper Finnish phrase such as
"Sulkeutuu klo" (or "Päättyy klo") to clearly indicate a time, and ensure
spacing/punctuation matches the project's locale string style.

Comment on lines +4 to +13
"friday": "perjantai",
"monday": "maanantai",
"open24Hours": "Avoinna 24 tuntia",
"openNow": "Avoinna nyt",
"saturday": "Lauantai",
"sunday": "sunnuntai",
"temporarilyClosed": "Väliaikaisesti suljettu",
"thursday": "torstai",
"tuesday": "tiistai",
"wednesday": "keskiviikko"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent capitalization in weekday names.

"Lauantai" (Saturday, line 8) is capitalized while all other weekday names are lowercase. Ensure consistent capitalization across all weekdays—either all lowercase or all capitalized, depending on Finnish localization conventions.

🤖 Prompt for AI Agents
In packages/visual-editor/locales/components/fi/visual-editor.json around lines
4 to 13, the weekday translations are inconsistent: "Lauantai" (line 8) is
capitalized while other weekdays are lowercase; update "Lauantai" to "lauantai"
(or normalize all weekday values to the chosen capitalization) so all weekday
strings follow the same lowercase convention.

Comment on lines +6 to +7
"open24Hours": "Ouvrir 24 heures",
"openNow": "Ouvrir maintenant",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use adjective form for status indicators, not infinitive verbs.

Lines 6-7 use infinitive verb forms ("Ouvrir") which read as commands ("Open 24 hours", "Open now") rather than status descriptions. For UI status indicators, the adjective form "Ouvert" should be used instead.

Apply this diff:

-  "open24Hours": "Ouvrir 24 heures",
-  "openNow": "Ouvrir maintenant",
+  "open24Hours": "Ouvert 24h/24",
+  "openNow": "Ouvert actuellement",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"open24Hours": "Ouvrir 24 heures",
"openNow": "Ouvrir maintenant",
"open24Hours": "Ouvert 24h/24",
"openNow": "Ouvert actuellement",
🤖 Prompt for AI Agents
In packages/visual-editor/locales/components/fr/visual-editor.json around lines
6-7, the values use infinitive verbs ("Ouvrir 24 heures", "Ouvrir maintenant")
but should be adjective status labels; update those string values to "Ouvert 24
heures" and "Ouvert maintenant" respectively so the UI shows status descriptions
rather than commands.

"closed": "Chiuso",
"closesAt": "Chiude a",
"friday": "Venerdì",
"monday": "Lunedi",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing accent in Italian weekday.

"Lunedi" should be "Lunedì" (with accent grave on the i).

Apply this diff:

-  "monday": "Lunedi",
+  "monday": "Lunedì",
🤖 Prompt for AI Agents
In packages/visual-editor/locales/components/it/visual-editor.json around line
5, the Italian weekday value is misspelled as "Lunedi"; replace it with the
correct accented form "Lunedì" (use grave accent on the i), ensure the JSON
string is properly quoted and the file saved with UTF-8 encoding so the accent
is preserved and the JSON remains valid.

Comment on lines +4 to +5
"friday": "Friday",
"monday": "Monday",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Complete the weekday translations.

The weekday names ("Friday", "Monday", "Saturday", "Sunday", "Thursday", "Tuesday", "Wednesday") are in English instead of Chinese. This creates an inconsistent localization experience compared to the properly translated status terms and other complete locale files (Estonian, Spanish, Portuguese, Latvian).

Consider translating the weekdays to Chinese:

  • friday → "星期五"
  • monday → "星期一"
  • saturday → "星期六"
  • sunday → "星期日"
  • thursday → "星期四"
  • tuesday → "星期二"
  • wednesday → "星期三"

Also applies to: 8-9, 11-13

🤖 Prompt for AI Agents
In packages/visual-editor/locales/components/zh/visual-editor.json around lines
4–5 (and similarly lines 8–9 and 11–13), the weekday values are still in
English; replace them with the Chinese translations provided (friday → "星期五",
monday → "星期一", saturday → "星期六", sunday → "星期日", thursday → "星期四", tuesday →
"星期二", wednesday → "星期三") so the locale file is consistent; update each
corresponding key's value to the matching Chinese string and save the JSON
ensuring valid syntax.

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

Choose a reason for hiding this comment

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

Might want to update the .gitattributes so this isn't auto-collapsed in PRs

"thursday": "Thursday",
"tuesday": "Tuesday",
"wednesday": "Wednesday"
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is missing some of component translations: searchThisArea for example from locator, openMenu from ExpandedHeader, etc.

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