Skip to content

feat: add default data for all locales (draft)#1044

Open
asanehisa wants to merge 28 commits intomainfrom
default-locales
Open

feat: add default data for all locales (draft)#1044
asanehisa wants to merge 28 commits intomainfrom
default-locales

Conversation

@asanehisa
Copy link
Contributor

@asanehisa asanehisa commented Feb 12, 2026

Intent:

  1. Add utilities for localized default content
  • defaultText and defaultRichText now build translatable defaults for all supported locales.
  • defaultRichText now generates consistent html + json per locale using getDefaultRTF.
  • packages/visual-editor/src/utils/defaultContent.ts
  1. Replace hardcoded { en, hasLocalizedValue } defaults across components
  • Many components now call defaultText(...) / defaultRichText(...) instead of embedding en only defaults.
  • This makes new components/sections start with locale-populated values (when you drag them into Editor).
  1. Inject translated defaults only for untouched template layouts (during Pages Generation step in transformProps)
  • Before migration/resolve, check if layout still matches canonical template defaults.
  • If untouched, inject missing locale values from default registries.
  • If edited, do nothing (preserve user customizations).
  • Core logic: packages/visual-editor/src/utils/defaultLayoutTranslations.ts
  1. Add component-default translation generation workflow
  • New script + package scripts to diff/fill missing component-defaults locale keys.
  • packages/visual-editor/scripts/generateComponentDefaultTranslations.ts
  • packages/visual-editor/package.json

TODO:

  1. Ensure default data in defaultLayoutData matches default data in components or else it can't update the text for different locales. Adjust category in Product Section after Text Atom PR.
  2. Ensure github workflow fails i18n test if someone forgot to run i18n:translate:component-defaults
  3. Test with Locator (locator rn has meta title error when i try to make one in a non-en locale)
  4. Test with locales other than es and fr
  5. Confirm it looks correct in ThemeEditor too (just never got around to)
  6. Refactor / merge conflicts on duvals i18n PR

@asanehisa asanehisa added the create-dev-release Triggers dev release workflow label Feb 12, 2026
@github-actions
Copy link
Contributor

Warning: Component files have been updated but no migrations have been added. See https://github.com/yext/visual-editor/blob/main/packages/visual-editor/src/components/migrations/README.md for more information.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 12, 2026

commit: 7de33b2

@asanehisa asanehisa force-pushed the default-locales branch 8 times, most recently from 9a93e16 to 9738401 Compare February 12, 2026 20:07
@asanehisa asanehisa changed the title feat: test (draft) feat: add default data for all locales Feb 13, 2026
@asanehisa asanehisa changed the title feat: add default data for all locales feat: add default data for all locales (draft) Feb 13, 2026
@asanehisa asanehisa marked this pull request as ready for review February 13, 2026 21:03
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

Walkthrough

This PR introduces a comprehensive system for managing localized default text across the visual editor's components. It adds 25 new locale translation files (covering locales from Czech to Chinese), establishes a centralized component default registry indexed by locale, creates utilities (defaultText and defaultRichText) to resolve locale-aware defaults, and implements template layout injection logic to enrich untouched default layouts with locale-specific translations. Components throughout the codebase are updated to use these new utilities instead of hard-coded localized objects. The system includes a script to auto-generate missing translations, test infrastructure to validate localization coverage, and integration into template processing pipelines (main, directory, locator) via a new processTemplateLayoutData wrapper.

Sequence Diagram

sequenceDiagram
    participant Build as Build Process
    participant Registry as Component Default Registry
    participant Scanner as i18next Scanner
    participant Component as Visual Editor Component
    participant Utils as defaultContent Utils
    participant Resolver as resolveComponentData
    participant RenderEngine as React Renderer

    Build->>Registry: Load all locale JSON files from locales/component-defaults/*
    Registry-->>Build: componentDefaultRegistry (locale → key → string)
    
    Build->>Scanner: Load supported locales from supportedLocales.json
    Scanner->>Scanner: Update lngs config dynamically
    
    Component->>Utils: defaultText("buttonText.label", "Button")
    Utils->>Registry: Lookup key for current locale
    alt Locale exists in registry
        Registry-->>Utils: Return localized string
    else Fallback to English
        Registry-->>Utils: Return en default
    end
    Utils-->>Component: Return TranslatableString {en, fr, de, ...}
    
    Component->>Resolver: resolveTranslatableType(value, locale)
    Resolver->>Utils: Get locale-specific value
    Utils->>Registry: Lookup locale in componentDefaultRegistry
    Registry-->>Utils: String value
    Utils-->>Resolver: Resolved value
    Resolver-->>Component: Resolved text/element
    
    Component->>RenderEngine: Render with resolved default value
    RenderEngine-->>RenderEngine: Display localized text
Loading

Possibly related PRs

Suggested reviewers

  • asanehisa
  • mkilpatrick
  • benlife5
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add default data for all locales (draft)' clearly describes the primary change: adding default data for all supported locales across the visual editor.
Description check ✅ Passed The description comprehensively explains the PR intent through four main objectives: adding localization utilities, replacing hardcoded defaults, injecting translated defaults, and adding a translation generation workflow. It directly relates to the changeset with clear explanations of the rationale and implementation details.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

✨ 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 default-locales

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: 10

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🤖 Fix all issues with AI agents
In `@packages/visual-editor/locales/component-defaults/fr/visual-editor.json`:
- Around line 1-7: The localized heading in aboutSection.heading uses the
translated placeholder "[[nom]]" which likely won't resolve because the code
expects the language-independent entity field placeholder "[[name]]"; update the
FR locale string to use the same placeholder token the component expects
(replace "[[nom]]" with "[[name]]") and verify AboutSection.tsx's interpolation
logic still finds and replaces [[name]] (or adjust the component if it expects a
different token), ensuring placeholder tokens remain consistent across locales
and the AboutSection.tsx rendering path.

In `@packages/visual-editor/locales/component-defaults/zh-TW/visual-editor.json`:
- Line 3: The locale entry under the "heading" key uses a translated placeholder
`[[姓名]]` which breaks the embedded-field resolver that expects ASCII field keys
via the regex /\[\[([a-zA-Z0-9._]+)\]\]/g; change the placeholder to the
canonical field key `[[name]]` (i.e., "heading": "關於[[name]]") and audit other
locale files for translated placeholders like `[[nome]]`, `[[namn]]`,
`[[naam]]`, `[[isim]]`, replacing them with the canonical `[[name]]` so the
resolver (which matches ASCII field identifiers) can correctly substitute
values.

In `@packages/visual-editor/scripts/generateComponentDefaultTranslations.ts`:
- Around line 168-194: The placeholder tokens like [[name]] must be protected
before calling translateText and restored afterwards; in the missingKeys async
handler (inside the Promise.allSettled loop) wrap any [[...]] placeholders in
the source string into safe temporary tokens, call translateText on the
tokenized text, then replace tokens back into the translated result (and also
apply removeEmbeddedContext if a context was embedded) before saving to
defaultsCache; ensure this tokenization/restoration runs regardless of whether
extractContextFromKey returned a context so placeholders are never sent raw to
the translation API (refer to functions/vars: extractContextFromKey,
embedContextInText, removeEmbeddedContext, translateText, defaultsCache,
missingKeys).

In
`@packages/visual-editor/src/components/pageSections/AboutSection/AboutSection.tsx`:
- Around line 206-209: Locale placeholder identifiers were translated (e.g., fr:
[[nom]]) which breaks findField() lookup because entity properties are
language-agnostic and expect the English property name; update all locale files
so aboutSection.heading uses the placeholder [[name]] (the constantValue passed
to defaultText in AboutSection.tsx) and scan other sections for similarly
translated placeholders (e.g., [[name]] vs [[nombre]]/[[Name]]) and normalize
them to the English property identifiers so findField() can resolve
document.name correctly.

In
`@packages/visual-editor/src/components/pageSections/EventSection/EventCard.tsx`:
- Around line 42-45: The missing localization key eventCard.description
referenced by defaultRichText("eventCard.description", ...) must be added to
every locale; update each locale's translation object (e.g., the en, zh, zh-TW,
es, tr, sk locale files) to include an eventCard.description entry with the
proper translated string (or confirm intentionally left in English), so that the
EventCard component's defaultRichText lookup finds a locale value instead of
always falling back to the English default.

In `@packages/visual-editor/src/components/pageSections/FAQsSection/FAQCard.tsx`:
- Around line 25-32: The locale files are missing the faqCard.question and
faqCard.answer keys used by defaultRichText, so add these two keys to every
locale object under the faqCard namespace (faqCard.question and faqCard.answer)
with appropriate localized rich-text strings (or copy the English fallback
content) so defaultRichText("faqCard.question", ...) and
defaultRichText("faqCard.answer", ...) can resolve for all locales; ensure the
keys match the same structure/format used by other rich-text translations so the
FAQCard component reads them correctly.

In
`@packages/visual-editor/src/components/pageSections/ProfessionalHeroSection.tsx`:
- Around line 727-730: The secondary CTA `link` is a URL but is wrapped with the
translation helper defaultText("professionalHeroSection.secondaryCtaLink", "#"),
which will send the placeholder "#" into the i18n pipeline; change the
assignment for `link` in ProfessionalHeroSection (the secondary CTA object where
`link: defaultText("professionalHeroSection.secondaryCtaLink", "#")`) to a plain
string (`link: "#"`) like the primary CTA so the URL is not translated.

In `@packages/visual-editor/src/utils/resolveYextEntityField.ts`:
- Around line 113-166: The fallback branch inside resolveYextEntityField
currently returns raw strings or inner objects when locale is missing; change
those to return the full translatable object shape like the locale-present
branch: for TranslatableString use return { ...data, en:
resolveEmbeddedFieldsInString(data.en, streamDocument, locale) }, for
TranslatableRichText use return { ...data, en: { ...data.en, html:
resolveEmbeddedFieldsInString(data.en.html, streamDocument, locale) } }, and for
the final missing case return the original data object (not an empty string);
keep use of resolveEmbeddedFieldsInString and preserve hasLocalizedValue and
other existing fields on data.

In `@packages/visual-editor/src/vite-plugin/templates/locator.tsx`:
- Around line 107-123: The call to processTemplateLayoutData passes layoutData
as a positional property name but the function expects rawLayoutData; update the
options object in the locator template where processTemplateLayoutData is
invoked (the block that currently sets layoutData and resolvedPuckData) to pass
rawLayoutData: layoutData so rawLayoutData is populated correctly; ensure the
rest of the call (streamDocument: document, templateId: "locator",
buildProcessedLayout using migrate and resolveAllData with locatorConfig)
remains unchanged.

In `@packages/visual-editor/src/vite-plugin/templates/main.tsx`:
- Around line 109-125: The call to processTemplateLayoutData is passing
layoutData instead of the required rawLayoutData, causing rawLayoutData to be
undefined; update the object passed to processTemplateLayoutData in main.tsx
(and replicate the same change in locator.tsx and directory.tsx) to use
rawLayoutData: layoutData while keeping the other properties (streamDocument:
document, templateId: "main", buildProcessedLayout: async () => { const
migratedData = migrate(layoutData, migrationRegistry, mainConfig, document);
return await resolveAllData(migratedData, mainConfig, { streamDocument: document
}); }). Ensure the symbol names layoutData, rawLayoutData,
processTemplateLayoutData, migrate, resolveAllData, migrationRegistry,
mainConfig, and document are used exactly as shown.
🟡 Minor comments (17)
packages/visual-editor/locales/component-defaults/pl/visual-editor.json-8-11 (1)

8-11: ⚠️ Potential issue | 🟡 Minor

Fix incorrect Polish translation: "header": "Chodnikowiec" should be "Nagłówek"

The English source for this field is "header": "Header". The Polish translation "Chodnikowiec" (sidewalk/pavement) is semantically incorrect. The proper Polish equivalent for "Header" in this context is "Nagłówek" (header/heading).

packages/visual-editor/src/utils/resolveComponentData.tsx-144-150 (1)

144-150: ⚠️ Potential issue | 🟡 Minor

Avoid treating intentional empty translations as missing.
!value[locale] falls back to English even if the locale key exists but is intentionally empty. Consider falling back only when the locale value is null/undefined.

Suggested fix
-  if (value.hasLocalizedValue === "true" && !value[locale]) {
-    const fallbackValue = value.en ?? "";
+  const localizedValue = value[locale];
+  if (value.hasLocalizedValue === "true" && localizedValue == null) {
+    const fallbackValue = value.en ?? "";
     if (isRichText(fallbackValue)) {
       return toStringOrElement(fallbackValue);
     }
     return fallbackValue;
   }
packages/visual-editor/locales/component-defaults/sk/visual-editor.json-74-76 (1)

74-76: ⚠️ Potential issue | 🟡 Minor

Untranslated "Insights" string in Slovak locale.

insightSection.sectionHeading is left in English while other sectionHeading values in the same file are properly translated (e.g., nearbyLocations.sectionHeading → "Polohy v okolí", photoGallery.sectionHeading → "Galéria"). Provide Slovak translation to match the pattern.

packages/visual-editor/locales/component-defaults/hu/visual-editor.json-74-76 (1)

74-76: ⚠️ Potential issue | 🟡 Minor

"Insights" left untranslated in Hungarian.

Line 75 has the English word "Insights" instead of a Hungarian translation (e.g., "Betekintések" or "Elemzések"). This was likely missed during automated translation.

packages/visual-editor/locales/component-defaults/hu/visual-editor.json-3-3 (1)

3-3: ⚠️ Potential issue | 🟡 Minor

Likely mistranslation: "Körülbelül" means "approximately", not "about (regarding)".

In Hungarian, "Körülbelül" conveys numerical approximation ("approximately"), not "about" in the sense of "regarding" or "information about." A more appropriate translation for "About [[name]]" would be something like "A(z) [[név]]-ról/ről" or "Rólunk" depending on context. This appears to be a machine-translation artifact.

packages/visual-editor/locales/component-defaults/nb/visual-editor.json-54-55 (1)

54-55: ⚠️ Potential issue | 🟡 Minor

Several Norwegian strings left untranslated in English.

Lines 55, 91, and 93 contain English text ("Header Link", "Call to Action") while the rest of the file is translated to Norwegian. These appear to be missed by the translation script.

Also applies to: 89-94

packages/visual-editor/locales/component-defaults/fi/visual-editor.json-134-136 (1)

134-136: ⚠️ Potential issue | 🟡 Minor

Potentially awkward Finnish translation for "Featured Testimonials".

"Suositellut suosittelut" appears redundant — it roughly reads as "Recommended recommendations." Consider having a native Finnish speaker review this translation.

packages/visual-editor/locales/component-defaults/da/visual-editor.json-18-20 (1)

18-20: ⚠️ Potential issue | 🟡 Minor

Untranslated strings: directoryRoot values remain in English.

Both breadcrumbs.directoryRoot (Line 19) and directory.directoryRoot (Line 32) are still "Directory Root" in English instead of Danish. Similarly, footer.sectionLabel on Line 52 is "Footer label" rather than a Danish translation. These appear to be machine translation gaps.

Also applies to: 31-33

packages/visual-editor/locales/component-defaults/et/visual-editor.json-74-76 (1)

74-76: ⚠️ Potential issue | 🟡 Minor

Untranslated string: insightSection.sectionHeading is still in English.

This value remains "Insights" while all other strings in this file are translated to Estonian. Other locale files have this translated (e.g., German: "Einblicke", Czech: "Postřehy", Danish: "Indsigt"). The Estonian equivalent would be something like "Ülevaated".

packages/visual-editor/locales/component-defaults/de/visual-editor.json-27-30 (1)

27-30: ⚠️ Potential issue | 🟡 Minor

Mistranslation: "Taste" means keyboard key, not UI button.

"Taste" is used throughout this file for button labels (lines 29, 39, 63, 65, 69, 90, 92, 97, 109, 113, 118, 123), but in German it refers to a physical keyboard key. The standard German UI term is "Button" (widely adopted loanword) or "Schaltfläche". This affects all buttonText, primaryButtonText, and secondaryButtonText entries.

packages/visual-editor/locales/component-defaults/ja/visual-editor.json-1-137 (1)

1-137: ⚠️ Potential issue | 🟡 Minor

Machine-translated strings contain notable mistranslations.

A few entries stand out as incorrect or awkward:

  1. Line 6"socialHeader": "私たちに従ってください": "従う" means "obey/comply", not "follow" in the social-media sense. Consider "フォローしてください" or "ソーシャルメディア".
  2. Line 125"name": "最初から最後まで": The English source "First Last" is a placeholder name (like "John Doe"), but this was translated as "From beginning to end". It should be a Japanese placeholder name, e.g. "姓 名" or "山田 太郎".
  3. Line 115"subtitle": "字幕": "字幕" means movie/video subtitles. For a secondary heading, "サブタイトル" is more appropriate.

Since these defaults are user-facing in the editor, consider having a native speaker review the machine-translated files — especially for languages where placeholder names and UI idioms don't translate literally.

packages/visual-editor/locales/component-defaults/es/visual-editor.json-122-127 (1)

122-127: ⚠️ Potential issue | 🟡 Minor

Same placeholder-name mistranslation as the Japanese file.

Line 125 — "name": "Primero último" literally translates "First Last" word-by-word, but the intent is a placeholder person name (like "John Doe"). A more natural Spanish equivalent would be "Nombre Apellido" or a culturally common placeholder like "Juan Pérez".

packages/visual-editor/locales/component-defaults/es/visual-editor.json-77-79 (1)

77-79: ⚠️ Potential issue | 🟡 Minor

Capitalization inconsistency: "encontrar una ubicación" should be title-cased.

Line 78 — Other headings in this file use sentence case with an initial capital (e.g., "Próximos eventos", "Preguntas frecuentes"), but this one starts lowercase. Should be "Encontrar una ubicación".

Proposed fix
   "locator": {
-    "pageHeadingTitle": "encontrar una ubicación"
+    "pageHeadingTitle": "Encontrar una ubicación"
   },
packages/visual-editor/locales/component-defaults/zh/visual-editor.json-46-46 (1)

46-46: ⚠️ Potential issue | 🟡 Minor

Missing empty faqCard key present in the English baseline.

The en locale file has "faqCard": {} but it's absent here. Minor inconsistency — harmless since it's empty, but worth keeping in sync for the translation diff tooling.

packages/visual-editor/locales/component-defaults/en/visual-editor.json-65-65 (1)

65-65: ⚠️ Potential issue | 🟡 Minor

Inconsistent capitalization of "Call to Action" vs "Call To Action".

heroSection.primaryCtaLabel (line 65) uses "Call To Action" (Title Case) while primaryHeader.primaryCtaLabel (line 92) uses "Call to Action" (lowercase "to"). These serve the same purpose as default CTA labels — consider normalizing.

Also applies to: 92-92

packages/visual-editor/locales/component-defaults/zh/visual-editor.json-115-115 (1)

115-115: ⚠️ Potential issue | 🟡 Minor

Incorrect Chinese translation for "subtitle".

"字幕" means video captions/subtitles, not a heading subtitle. The correct term in a UI/heading context is "副标题".

Proposed fix
-    "subtitle": "字幕"
+    "subtitle": "副标题"
packages/visual-editor/locales/component-defaults/sv/visual-editor.json-115-115 (1)

115-115: ⚠️ Potential issue | 🟡 Minor

Questionable machine translation: "subtitle": "Texta".

"Texta" in Swedish means "to text (someone)" or "to subtitle (a film)". For a UI label representing a subtitle/subheading, "Underrubrik" or "Undertitel" would be more appropriate. This is likely a Google Translate artifact. Since the PR TODO mentions testing with more locales, this may get caught in a review pass of all translations.

🧹 Nitpick comments (7)
packages/visual-editor/locales/component-defaults/pl/visual-editor.json (1)

131-136: Minor translation quality: "referencje" used for "testimonials".

"Referencje" (Line 132, 135) more closely maps to "references" in Polish. A more natural translation for "testimonials" would be "opinie" or "rekomendacje". Similarly, on Line 107, "credentials" is rendered as "Referencje" which is a different meaning. Since these are auto-generated via Google Translate (per PR description), a quick human review pass on the Polish file would catch these semantic mismatches.

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

11-16: Import and reuse defaultLink from ExpandedFooter.tsx to avoid duplication.

This local defaultLink is identical to the export in ExpandedFooter.tsx (same key "footer.linkLabel", same shape). FooterExpandedLinkSectionSlot.tsx already imports it from there, confirming the import path works and the export is intended for reuse. Consolidating to the shared export prevents drift if the default link structure changes.

♻️ Suggested refactor
-import { defaultText } from "../../utils/defaultContent.ts";
-
-const defaultLink = {
-  linkType: "URL" as const,
-  label: defaultText("footer.linkLabel", "Footer Link"),
-  link: "#",
-  openInNewTab: false,
-};
+import { defaultLink } from "./ExpandedFooter.tsx";
packages/visual-editor/locales/component-defaults/zh/visual-editor.json (1)

125-125: Consider a more natural Chinese name placeholder.

"第一个 最后一个" is a literal translation of "First Last" but reads unnaturally in Chinese. A conventional placeholder like "张三" (the Chinese equivalent of "John Doe") would be more idiomatic.

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

98-125: Unofficial Google Translate API endpoint is unreliable and has no rate-limiting.

The client=gtx endpoint is an undocumented, free API that Google can rate-limit or block without notice. Combined with Promise.allSettled on Line 171 firing all missing keys concurrently, this can trigger rate limits or IP bans during bulk translation runs.

Consider:

  1. Using the official Cloud Translation API with an API key.
  2. Adding concurrency control (e.g., batching or a semaphore) to limit parallel requests.

18-39: extractContextFromKey uses _ separator but flattened keys use . — context feature is effectively dead code.

flatten produces keys like "aboutSection.heading". Since extractContextFromKey splits on "_" (CONTEXT_SEPARATOR), it will never find a multi-part key in the current schema. The embedContextInText / removeEmbeddedContext functions are therefore never invoked. If this is intentional scaffolding for future use, consider adding a comment. Otherwise, the separator should align with the actual key format.

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

189-226: Static analysis: forEach callback implicitly returns — use for...of to satisfy the linter.

Biome flags Line 191 because forEach callbacks shouldn't return values, even though injectLocalizedValues is void. Switching to for...of silences the lint and is more idiomatic for side-effect-only loops.

Suggested fix
 const injectLocalizedValues = (node: unknown, locales: string[]): void => {
   if (Array.isArray(node)) {
-    node.forEach((item) => injectLocalizedValues(item, locales));
+    for (const item of node) {
+      injectLocalizedValues(item, locales);
+    }
     return;
   }

125-152: Ambiguous English values (e.g., "Button") will silently skip injection.

The reverse-mapping approach via enValueToKeys means any English string used by multiple component keys (like "Button" for both ctaWrapper.buttonText and eventCard.buttonText) where those keys happen to have different locale translations will cause candidateValues.size !== 1, returning undefined and skipping injection for that value.

This is a safe design choice, but it could result in many common defaults (e.g., "Button", "Phone", "Text") not being injected. Consider logging a debug-level message when ambiguity is detected to help diagnose missing translations during development.

Comment on lines +206 to +209
constantValue: defaultText(
"aboutSection.heading",
"About [[name]]"
),
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how [[name]] placeholders are resolved — are they entity field references?
rg -n '\[\[name\]\]' --type=ts --type=tsx -C3
echo "---"
# Check all locale files for the aboutSection.heading key to see placeholder usage
fd 'visual-editor.json' packages/visual-editor/locales/component-defaults --exec grep -l 'aboutSection' {} \; | xargs -I{} sh -c 'echo "=== {} ===" && jq -r ".aboutSection.heading" {}'

Repository: yext/visual-editor

Length of output: 2484


🏁 Script executed:

#!/bin/bash
# Find where defaultText is defined and how it processes placeholders
rg -n 'export.*defaultText|function.*defaultText' packages/visual-editor/src --type=ts -A5
echo "---"
# Search for placeholder resolution/template rendering logic
rg -n 'replace.*\[\[|\[\[.*\]\]' packages/visual-editor/src --type=ts -C2 | head -50

Repository: yext/visual-editor

Length of output: 5299


🏁 Script executed:

cat packages/visual-editor/src/utils/resolveYextEntityField.ts

Repository: yext/visual-editor

Length of output: 5852


Fix translated placeholder identifiers in locale files.

The placeholder resolution uses findField() to look up properties directly on the entity document object (e.g., [[name]]document.name). Entity properties are language-agnostic and always use the English property names.

Translating the placeholder identifier breaks resolution in all non-English locales:

  • fr: [[nom]] → tries to find document.nom (doesn't exist; property is name)
  • es: [[nombre]] → tries to find document.nombre (doesn't exist)
  • de: [[Name]] → tries to find document.Name (case-sensitive; property is name)
  • And 15+ other affected locales

Only en, en-GB, ro, cs, and sk use the correct identifier [[name]].

Update all locale files to use [[name]] for aboutSection.heading (and audit other sections for the same issue).

🤖 Prompt for AI Agents
In
`@packages/visual-editor/src/components/pageSections/AboutSection/AboutSection.tsx`
around lines 206 - 209, Locale placeholder identifiers were translated (e.g.,
fr: [[nom]]) which breaks findField() lookup because entity properties are
language-agnostic and expect the English property name; update all locale files
so aboutSection.heading uses the placeholder [[name]] (the constantValue passed
to defaultText in AboutSection.tsx) and scan other sections for similarly
translated placeholders (e.g., [[name]] vs [[nombre]]/[[Name]]) and normalize
them to the English property identifiers so findField() can resolve
document.name correctly.

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/component-defaults/et/visual-editor.json`:
- Around line 74-76: The key insightSection.sectionHeading is still in English
("Insights"); update its value to the proper Estonian translation (for example
"Ülevaated" or "Teadmised") in the visual-editor.json so the locale file
contains the localized string for insightSection.sectionHeading.

In `@packages/visual-editor/locales/component-defaults/fi/visual-editor.json`:
- Around line 134-136: The Finnish translation for
testimonialSection.sectionHeading currently reads "Suositellut suosittelut",
which is awkwardly repetitive; update the value of
"testimonialSection"."sectionHeading" to a clearer phrase such as "Esitellyt
suosittelut" or "Suositellut suositukset" (pick the one that best fits the UI
context) to remove the near-duplicate wording and improve readability.
- Around line 105-116: In the professionalHeroSection locale object, fix two
Finnish translation errors: change the value of professionalName from
"Ammattilainen nimi" to the correct genitive form "Ammattilaisen nimi", and
change subtitle from "Tekstitys" to a more appropriate heading term like
"Alaotsikko"; update these keys (professionalHeroSection.professionalName and
professionalHeroSection.subtitle) in the JSON so the translations read
correctly.

In `@packages/visual-editor/locales/component-defaults/fr/visual-editor.json`:
- Line 36: Multiple keys use an inconsistent French translation for "Learn more"
— replace the non-idiomatic "Apprendre encore plus" with the idiomatic "En
savoir plus" for all relevant keys (e.g., textAndLinkLabel, insightCard.ctaLabel
and the other occurrences noted) so the translations are consistent across the
JSON; update each string value to "En savoir plus" while preserving the
surrounding JSON structure and keys.

In `@packages/visual-editor/locales/component-defaults/hu/visual-editor.json`:
- Around line 74-76: The JSON value for insightSection.sectionHeading is left in
English ("Insights"); update that value to a Hungarian translation (for example
"Betekintések" or "Cikkek") so it matches the rest of the locale file, ensuring
the key "insightSection" and its nested "sectionHeading" are preserved and only
the string value is changed.

In `@packages/visual-editor/locales/component-defaults/ja/visual-editor.json`:
- Around line 122-127: The translation for the placeholder name is incorrect:
replace the literal translation "最初から最後まで" under the teamCard.name key with a
proper Japanese placeholder for a person’s name (e.g., "姓 名" or "山田 太郎") so it
reads as a name placeholder rather than "from start to finish"; update the
teamCard.name value accordingly and scan other locale files generated by the
translation script for similar literal translations of "First Last" (e.g.,
Lithuanian/Estonian) to apply the same fix.
- Around line 3-6: The translation for the "socialHeader" key is too literal and
directive; update the value for "socialHeader" in the JSON (key: "socialHeader")
to a more natural social-media phrasing such as "フォローしてください" or "SNSでフォロー" so it
reads appropriately as "Follow Us" in Japanese.

In `@packages/visual-editor/src/utils/defaultLayoutTranslations.ts`:
- Around line 19-23: defaultLayoutsByTemplate currently parses JSON at module
load using JSON.parse on defaultLayoutData.main/directory/locator which will
throw and crash imports if any blob is malformed; change this to either wrap
each JSON.parse call in a try/catch that falls back to a safe default and logs
the error, or lazy-initialize parsing (e.g., replace the module-level object
with a getter/factory that parses on first access and handles parse failures).
Update references to defaultLayoutsByTemplate so consumers still get the same
shape (use VisualEditorTemplateId keys) while ensuring malformed JSON does not
throw during import.
🧹 Nitpick comments (4)
packages/visual-editor/src/utils/defaultLayoutTranslations.ts (2)

189-193: forEach callback implicitly returns a value — use for...of instead.

Biome flags this because the arrow in .forEach((item) => injectLocalizedValues(item, locales)) implicitly returns the (void) result. A for...of loop is cleaner and avoids the lint warning.

Suggested fix
 const injectLocalizedValues = (node: unknown, locales: string[]): void => {
   if (Array.isArray(node)) {
-    node.forEach((item) => injectLocalizedValues(item, locales));
+    for (const item of node) {
+      injectLocalizedValues(item, locales);
+    }
     return;
   }

154-170: extractDefaultRichTextInfo only handles a single top-level HTML tag.

The regex /<(strong|span)>(.*?)<\/\1>/i matches only the first <strong> or <span> element. If default rich text ever contains multiple elements or nested tags, this will silently extract only the first match. This is fine if defaults are always simple single-wrapped text, but worth noting as a constraint.

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

55-98: flatten, unflatten, loadJsonSafe, and saveJson are duplicated from generateTranslations.ts.

These four utilities have near-identical implementations in both scripts. Consider extracting them into a shared module (e.g., scripts/translationUtils.ts) to reduce maintenance burden.


156-184: No concurrency limit on translation API calls.

All missingKeys for a locale are fired concurrently via Promise.allSettled. For locales with many missing keys, this could trigger rate limiting from the Google Translate API. Consider batching or throttling (e.g., processing in chunks of 10–20).

Comment on lines +74 to +76
"insightSection": {
"sectionHeading": "Insights"
},
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

"Insights" left untranslated in Estonian.

insightSection.sectionHeading is still the English word "Insights" rather than an Estonian translation (e.g., "Ülevaated" or "Teadmised"). This was likely missed by the auto-translation step.

🤖 Prompt for AI Agents
In `@packages/visual-editor/locales/component-defaults/et/visual-editor.json`
around lines 74 - 76, The key insightSection.sectionHeading is still in English
("Insights"); update its value to the proper Estonian translation (for example
"Ülevaated" or "Teadmised") in the visual-editor.json so the locale file
contains the localized string for insightSection.sectionHeading.

Comment on lines +105 to +116
"professionalHeroSection": {
"businessName": "Yrityksen nimi",
"credentials": "Valtuustiedot",
"phoneLabel": "Puhelin",
"primaryButtonText": "Painike",
"primaryCtaLabel": "Ota minuun yhteyttä",
"professionalName": "Ammattilainen nimi",
"professionalTitle": "Ammattinimike",
"secondaryButtonText": "Painike",
"secondaryCtaLabel": "Lisätietoja",
"subtitle": "Tekstitys"
},
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

Minor Finnish translation quality issues in professionalHeroSection.

  • Line 111: "Ammattilainen nimi" is grammatically incorrect — it should be "Ammattilaisen nimi" (genitive form) to mean "Professional's name."
  • Line 115: "Tekstitys" means "subtitling" (as in film/TV captions), not a secondary heading. A more appropriate translation would be "Alaotsikko".

These may have been auto-generated; worth a quick review pass with a native speaker or improved prompt.

Suggested fix
-    "professionalName": "Ammattilainen nimi",
+    "professionalName": "Ammattilaisen nimi",
...
-    "subtitle": "Tekstitys"
+    "subtitle": "Alaotsikko"
📝 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
"professionalHeroSection": {
"businessName": "Yrityksen nimi",
"credentials": "Valtuustiedot",
"phoneLabel": "Puhelin",
"primaryButtonText": "Painike",
"primaryCtaLabel": "Ota minuun yhteyttä",
"professionalName": "Ammattilainen nimi",
"professionalTitle": "Ammattinimike",
"secondaryButtonText": "Painike",
"secondaryCtaLabel": "Lisätietoja",
"subtitle": "Tekstitys"
},
"professionalHeroSection": {
"businessName": "Yrityksen nimi",
"credentials": "Valtuustiedot",
"phoneLabel": "Puhelin",
"primaryButtonText": "Painike",
"primaryCtaLabel": "Ota minuun yhteyttä",
"professionalName": "Ammattilaisen nimi",
"professionalTitle": "Ammattinimike",
"secondaryButtonText": "Painike",
"secondaryCtaLabel": "Lisätietoja",
"subtitle": "Alaotsikko"
},
🤖 Prompt for AI Agents
In `@packages/visual-editor/locales/component-defaults/fi/visual-editor.json`
around lines 105 - 116, In the professionalHeroSection locale object, fix two
Finnish translation errors: change the value of professionalName from
"Ammattilainen nimi" to the correct genitive form "Ammattilaisen nimi", and
change subtitle from "Tekstitys" to a more appropriate heading term like
"Alaotsikko"; update these keys (professionalHeroSection.professionalName and
professionalHeroSection.subtitle) in the JSON so the translations read
correctly.

Comment on lines +134 to +136
"testimonialSection": {
"sectionHeading": "Suositellut suosittelut"
}
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

"Suositellut suosittelut" reads awkwardly — near-duplicate words.

"Suositellut" (featured) and "suosittelut" (testimonials) differ by one letter, making this look like a typo. Consider "Esitellyt suosittelut" or "Suositellut suositukset" for clarity.

🤖 Prompt for AI Agents
In `@packages/visual-editor/locales/component-defaults/fi/visual-editor.json`
around lines 134 - 136, The Finnish translation for
testimonialSection.sectionHeading currently reads "Suositellut suosittelut",
which is awkwardly repetitive; update the value of
"testimonialSection"."sectionHeading" to a clearer phrase such as "Esitellyt
suosittelut" or "Suositellut suositukset" (pick the one that best fits the UI
context) to remove the near-duplicate wording and improve readability.

},
"enhancedCta": {
"getDirectionsLabel": "Obtenir un itinéraire",
"textAndLinkLabel": "Apprendre encore plus"
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 translation for "Learn more" across keys.

Some keys use "Apprendre encore plus" (lines 36, 40, 66, 98, 114, 119) while insightCard.ctaLabel on line 71 uses "En savoir plus". The latter is the more idiomatic French for "Learn more"; "Apprendre encore plus" back-translates to "Learn even more." Consider aligning all instances to "En savoir plus" for consistency and accuracy.

Also applies to: 40-40, 66-66, 71-71, 98-98, 114-114, 119-119

🤖 Prompt for AI Agents
In `@packages/visual-editor/locales/component-defaults/fr/visual-editor.json` at
line 36, Multiple keys use an inconsistent French translation for "Learn more" —
replace the non-idiomatic "Apprendre encore plus" with the idiomatic "En savoir
plus" for all relevant keys (e.g., textAndLinkLabel, insightCard.ctaLabel and
the other occurrences noted) so the translations are consistent across the JSON;
update each string value to "En savoir plus" while preserving the surrounding
JSON structure and keys.

Comment on lines +74 to +76
"insightSection": {
"sectionHeading": "Insights"
},
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

"Insights" appears untranslated.

This string is left in English while every other value in the file is localized to Hungarian. Consider translating it (e.g., "Betekintések" or "Cikkek").

🤖 Prompt for AI Agents
In `@packages/visual-editor/locales/component-defaults/hu/visual-editor.json`
around lines 74 - 76, The JSON value for insightSection.sectionHeading is left
in English ("Insights"); update that value to a Hungarian translation (for
example "Betekintések" or "Cikkek") so it matches the rest of the locale file,
ensuring the key "insightSection" and its nested "sectionHeading" are preserved
and only the string value is changed.

Comment on lines +3 to +6
"heading": "[[name]]について",
"hoursHeader": "営業時間",
"servicesHeader": "提供されるサービス",
"socialHeader": "私たちに従ってください"
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

"Follow Us" social header translated too literally.

Line 6: "私たちに従ってください" means "Please obey/follow us" in a directive sense. For a social media "Follow Us" context, more natural Japanese would be "フォローしてください" or "SNSでフォロー".

🤖 Prompt for AI Agents
In `@packages/visual-editor/locales/component-defaults/ja/visual-editor.json`
around lines 3 - 6, The translation for the "socialHeader" key is too literal
and directive; update the value for "socialHeader" in the JSON (key:
"socialHeader") to a more natural social-media phrasing such as "フォローしてください" or
"SNSでフォロー" so it reads appropriately as "Follow Us" in Japanese.

Comment on lines +122 to +127
"teamCard": {
"buttonText": "ボタン",
"ctaLabel": "プロフィールにアクセス",
"name": "最初から最後まで",
"title": "アソシエイトエージェント"
},
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

Mistranslation: "First Last" placeholder name translated as "最初から最後まで" (From start to finish).

The teamCard.name value "First Last" is a placeholder representing a person's name (first name + last name). The Japanese translation "最初から最後まで" literally means "From start to finish," which is a mistranslation. Consider using a Japanese placeholder name like "姓 名" or "太郎 山田".

The same issue likely affects other locale files generated by the translation script (e.g., Lithuanian has "Pirmas Paskutinis" and Estonian has "Esimene Viimane" which are also literal translations of "First"/"Last").

🤖 Prompt for AI Agents
In `@packages/visual-editor/locales/component-defaults/ja/visual-editor.json`
around lines 122 - 127, The translation for the placeholder name is incorrect:
replace the literal translation "最初から最後まで" under the teamCard.name key with a
proper Japanese placeholder for a person’s name (e.g., "姓 名" or "山田 太郎") so it
reads as a name placeholder rather than "from start to finish"; update the
teamCard.name value accordingly and scan other locale files generated by the
translation script for similar literal translations of "First Last" (e.g.,
Lithuanian/Estonian) to apply the same fix.

Comment on lines +19 to +23
const defaultLayoutsByTemplate: Record<VisualEditorTemplateId, unknown> = {
main: JSON.parse(defaultLayoutData.main),
directory: JSON.parse(defaultLayoutData.directory),
locator: JSON.parse(defaultLayoutData.locator),
};
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

Module-level JSON.parse will crash the import if any default layout blob is malformed.

If defaultLayoutData.main, .directory, or .locator ever contains invalid JSON (e.g., during development or a build issue), this will throw at module load time with a cryptic parse error. Consider wrapping in a try/catch or lazy-initializing.

🤖 Prompt for AI Agents
In `@packages/visual-editor/src/utils/defaultLayoutTranslations.ts` around lines
19 - 23, defaultLayoutsByTemplate currently parses JSON at module load using
JSON.parse on defaultLayoutData.main/directory/locator which will throw and
crash imports if any blob is malformed; change this to either wrap each
JSON.parse call in a try/catch that falls back to a safe default and logs the
error, or lazy-initialize parsing (e.g., replace the module-level object with a
getter/factory that parses on first access and handles parse failures). Update
references to defaultLayoutsByTemplate so consumers still get the same shape
(use VisualEditorTemplateId keys) while ensuring malformed JSON does not throw
during import.

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.

1 participant