Skip to content

chore: swap to i18next-cli#1033

Open
mkilpatrick wants to merge 33 commits intomainfrom
i18n
Open

chore: swap to i18next-cli#1033
mkilpatrick wants to merge 33 commits intomainfrom
i18n

Conversation

@mkilpatrick
Copy link
Collaborator

@mkilpatrick mkilpatrick commented Feb 5, 2026

This swaps from i18n-scanner to i18next-cli because:

  • i18n-scanner hasn't been updated in two years and i18next-cli seems to be the preferred library
  • i18next-cli has better syncing functionality and options, which let's us simplify/remove some of the custom scripts that were previously necessary

This also removes the insertI18n script because it was clunky and not always accurate. We should add the correct translation functions manually when necessary.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 5, 2026

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.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 5, 2026

⚠️ Deleted Translation Keys Detected

🔤 Deleted Translation Keys

CTA

Key Languages Removed
CTA cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

Category

Key Languages Removed
Category cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

Heading

Key Languages Removed
Heading cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

Image

Key Languages Removed
Image cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

Link

Key Languages Removed
Link cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

addressInput

Key Languages Removed
addressInput.city cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
addressInput.countryCode cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
addressInput.line1 cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
addressInput.line2 cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
addressInput.line3 cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
addressInput.postalCode cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
addressInput.region cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
addressInput.sublocality cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

arrayField

Key Languages Removed
arrayField cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

borderRadiusDefaultLabel

Key Languages Removed
borderRadiusDefaultLabel cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

categories

Key Languages Removed
categories.directory cs,da de
categories.locator cs,da de

category

Key Languages Removed
category cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

components

Key Languages Removed
components.DirectoryGrid cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
components.Video cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
components.enhancedCallToAction cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
components.faqs cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
components.imageButton cs,da et,fi hr,hu it,ja lt,lv nb,nl pl,pt ro,sk sv,tr zh,zh-TW
components.servicesList cs,da de

copyrightMessage

Key Languages Removed
copyrightMessage cs,da de

ctaTypes

Key Languages Removed
ctaTypes.link cs,da de

currentLocation

Key Languages Removed
currentLocation da,pl sk,sv

emailList

Key Languages Removed
emailList cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

enterSchemaMarkup

Key Languages Removed
enterSchemaMarkup cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

expandedHeaderDesktop

Key Languages Removed
expandedHeaderDesktop cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

expandedHeaderMobile

Key Languages Removed
expandedHeaderMobile cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

fields

Key Languages Removed
fields.CTAVariant cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.ariaLabel cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.aspectRatioForLogo cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.breadcrumbsBackgroundColor cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.businessName cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.businessNameHeadingLevel cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.cards cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.category cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.contributorName cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.coordinate cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.description cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.destinationURL cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.displayType cs,da de
fields.emailsListLength cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.eventName cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.events cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.format cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.hasLocalizedValue cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.heading cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.headingText cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.headshot cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.height cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.hero cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.hoursText cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.imageUrl cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.includeHyperlink cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.infoColumn cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.insightSection cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.level cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.localGeomodifier cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.localGeomodifierHeadingLevel cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.logo cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.logoLink cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.logoStyles cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.logoUrl cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.mainPhone cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.name cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.options.HoursStatus cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.options.HoursTable cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.options.base cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.options.bottom cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.options.large cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.options.right cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.options.showFullText cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.options.small cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.options.sticky zh-TW
fields.options.top cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.options.truncate cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.presetImageType cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.primaryCTAVariant cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.primaryCta cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.primaryHeader cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.primaryHeaderLinks cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.products cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.secondaryCTAVariant cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.secondaryCta cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.secondaryFooter cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.secondaryFooterLinks cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.secondaryHeader cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.secondaryHeaderLinks cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.secondaryLinks cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.servicesColumn cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.servicesList cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.siteName cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.socialLinks cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.socialMedia cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.target cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.team cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.teamSection cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.testimonialSection cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.testimonials cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.textList cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.title cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.url cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.utilityImagesStyles cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.utilityImagesWidth cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.width cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

filters

Key Languages Removed
filters cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

label

Key Languages Removed
label cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

linkType

Key Languages Removed
linkType cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

loadingReviews

Key Languages Removed
loadingReviews cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

localGeomodifier

Key Languages Removed
localGeomodifier cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

logoLink

Key Languages Removed
logoLink fr

mediaType

Key Languages Removed
mediaType cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

name

Key Languages Removed
name cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

noHeroFieldsMsg

Key Languages Removed
noHeroFieldsMsg cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

noPromoFieldsMsg

Key Languages Removed
noPromoFieldsMsg cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

pageSettings

Key Languages Removed
pageSettings cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

presetImages

Key Languages Removed
presetImages.appGallery cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.appGalleryOutline cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.appStore cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.appStoreOutline cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.apple cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.arrowDown cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.arrowLeft cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.arrowRight cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.arrowUp cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.calendar cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.check cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.close cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.download cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.dribbble cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.email cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.facebook cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.figma cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.galaxyStore cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.galaxyStoreOutline cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.google cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.googlePlay cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.googlePlayOutline cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.heart cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.location cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.menu cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.minus cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.next cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.pause cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.phone cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.play cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.plus cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.previous cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.search cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.share cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.star cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.twitter cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
presetImages.uberEats cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

primaryHeader

Key Languages Removed
primaryHeader cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

professionalHero

Key Languages Removed
professionalHero cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

recentReviews

Key Languages Removed
recentReviews cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

search

Key Languages Removed
search cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

secondaryHeader

Key Languages Removed
secondaryHeader cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

theme

Key Languages Removed
theme.fontWeight.black cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
theme.fontWeight.bold cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
theme.fontWeight.extrabold cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
theme.fontWeight.extralight cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
theme.fontWeight.light cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
theme.fontWeight.medium cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
theme.fontWeight.normal cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
theme.fontWeight.semibold cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
theme.fontWeight.thin cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
theme.radius.pill cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
theme.radius.pill_shape cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

title

Key Languages Removed
title cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

video

Key Languages Removed
video cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 5, 2026

Walkthrough

This PR modernizes the i18n infrastructure by replacing i18next-scanner with i18next-cli and restructuring the translation pipeline. It updates 23 locale files across platform and components directories, adds new i18n configuration files for extracting and linting translations, introduces new npm scripts for extract/translate/propagate/lint workflows, and removes legacy translation management scripts. Additionally, multiple component files are updated to align with new translation key naming conventions (e.g., lowercase keys) and i18n patterns. The PR also adds utility functions for JSON/locale handling and introduces new validation tooling for interpolation variables in translations.

Sequence Diagram(s)

sequenceDiagram
    actor Dev as Developer
    participant CLI as i18next-cli
    participant Extract as Extract Step
    participant Translate as Translate Step
    participant Propagate as Propagate Step
    participant Lint as Lint Step
    participant Verify as Verify Interpolation
    participant Locale as Locale Files

    Dev->>CLI: Run i18n:update
    
    CLI->>Extract: i18n:extract:platform
    Extract->>Locale: Scan TS/TSX for keys
    Locale-->>Extract: Return found keys
    Extract->>Locale: Write extracted.json
    
    CLI->>Translate: i18n:translate:platform
    Translate->>Locale: Load platform en & target locales
    Locale-->>Translate: Return locale data
    Translate->>Translate: Mask interpolation vars
    Translate->>Translate: Translate missing/empty keys
    Translate->>Translate: Unmask interpolation vars
    Translate->>Locale: Update platform locales
    
    CLI->>Propagate: i18n:propagate:components
    Propagate->>Locale: Load platform & components locales
    Locale-->>Propagate: Return locale data
    Propagate->>Propagate: Build allowed key set
    Propagate->>Propagate: Sync platform→components keys
    Propagate->>Locale: Update components locales
    
    CLI->>Lint: i18n:lint
    Lint->>Locale: Check for empty translations
    Locale-->>Lint: Return validation results
    Lint-->>CLI: Report issues
    
    CLI->>Verify: i18n:check:interpolation
    Verify->>Locale: Validate {{var}} consistency
    Locale-->>Verify: Return validation results
    Verify-->>CLI: Report mismatches
    
    CLI-->>Dev: Workflow complete
Loading

Possibly Related PRs

Suggested Labels

i18n, refactor, dependencies

Suggested Reviewers

  • mkilpatrick
  • asanehisa
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'chore: swap to i18next-cli' clearly and concisely summarizes the main change: replacing i18n-scanner with i18next-cli.
Description check ✅ Passed The description explains why the swap is being made (i18n-scanner outdated, i18next-cli superior), mentions removal of custom scripts, and notes removal of insertI18n script with justification.

✏️ 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 i18n

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

Note

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

Caution

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

⚠️ Outside diff range comments (4)
packages/visual-editor/locales/components/sv/visual-editor.json (1)

25-37: ⚠️ Potential issue | 🟡 Minor

Polish Swedish translations for new section labels.

Lines 25–37 and 62–75 include a few literal/awkward strings (or English) that read unnaturally in Swedish UI. Consider updating for consistency with common terminology.

✏️ Example adjustments (confirm with localization)
-  "coreInfoSection": "Kärninfo -sektionen",
+  "coreInfoSection": "Kärninfo-sektionen",
-  "hours": "Timme",
-  "hoursSection": "Timmarssektion",
+  "hours": "Öppettider",
+  "hoursSection": "Öppettidsavsnitt",
-  "promoMedia": "Promo media",
+  "promoMedia": "Kampanjmedia",
-  "servicesSection": "Serviceavdelning",
+  "servicesSection": "Tjänsteavsnitt",

Also applies to: 62-75

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

28-28: ⚠️ Potential issue | 🟡 Minor

“パン塊” is an incorrect breadcrumb term.

For breadcrumb navigation, “パンくず” or “パンくずリスト” is the standard UI term.

✍️ Suggested wording
-  "breadcrumb": "パン塊",
+  "breadcrumb": "パンくず",
packages/visual-editor/locales/platform/nb/visual-editor.json (1)

141-141: ⚠️ Potential issue | 🟡 Minor

Incorrect Norwegian translation for "Default".

The translation "Misligholde" is used for "Default" in multiple places, but this Norwegian word means "to default on a payment" or "to neglect/fail an obligation" — not a default/fallback option in a UI context. The correct translation for a default setting should be "Standard".

Affected locations:

  • Line 141: "default": "Misligholde"
  • Line 275: "default": "Misligholde"
  • Line 448: "fontSizeDefaultLabel": "Misligholde"
  • Line 566: "spacingDefaultLabel": "Misligholde"
🌍 Proposed fix for translation accuracy
-  "default": "Misligholde",
+  "default": "Standard",
-        "default": "Misligholde",
+        "default": "Standard",
-  "fontSizeDefaultLabel": "Misligholde",
+  "fontSizeDefaultLabel": "Standard",
-  "spacingDefaultLabel": "Misligholde",
+  "spacingDefaultLabel": "Standard",

Also applies to: 275-275, 448-448, 566-566

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

83-85: ⚠️ Potential issue | 🟡 Minor

Same interpolation issue: {{numéro}} should likely be {{number}}.

This is the same issue as in the components French locale. The interpolation variable {{numéro}} should match the code's parameter name, which is likely number.

Proposed fix
     "footerUtilityImagesSlot": {
-      "defaultAlt": "Image utilitaire {{numéro}}"
+      "defaultAlt": "Image utilitaire {{number}}"
     },
🤖 Fix all issues with AI agents
In `@packages/visual-editor/locales/components/ja/visual-editor.json`:
- Line 46: The translation key locationsWithinDistanceOf_one uses non-matching
placeholders {{距離}} and {{ユニット}} which i18next won't interpolate and differ from
the _other variant; update the string for locationsWithinDistanceOf_one to use
the same interpolation tokens as the plural form (e.g., {{distance}} and
{{unit}}) and mirror the structure of locationsWithinDistanceOf_other so both
singular and plural keys interpolate consistently.

In `@packages/visual-editor/locales/components/pt/visual-editor.json`:
- Around line 20-24: The Portuguese and French translations use locale-specific
placeholder names ({{número}} / {{numéro}}) but the app passes the interpolation
key "number"; update the translation key
components.footerUtilityImagesSlot.defaultAlt (and the equivalent French key) to
use the placeholder {{number}} so the runtime interpolation receives the correct
variable name.

In `@packages/visual-editor/locales/components/ro/visual-editor.json`:
- Line 9: The "breadcrumb" translation value in the locales JSON is incorrect
("Pescăruș" = seagull); update the value for the "breadcrumb" key in
packages/visual-editor/locales/components/ro/visual-editor.json to a correct
Romanian term such as "Traseu de navigare", "Navigare" or leave as "Breadcrumb"
(choose the preferred term used elsewhere for consistency) so the UI label
correctly reflects the navigation breadcrumb element.

In `@packages/visual-editor/locales/platform/ja/visual-editor.json`:
- Line 481: The translation key locationsWithinDistanceOf_one uses non-matching
placeholder names {{距離}} and {{ユニット}} which i18next won't replace; update the
value for locationsWithinDistanceOf_one to use the same interpolation
placeholders as locationsWithinDistanceOf_other (e.g., {{distance}} and
{{unit}}) and preserve spacing/order and plural form so interpolation works
consistently across both keys.

In `@packages/visual-editor/package.json`:
- Line 85: The package.json dependency for i18next-cli is invalid (currently
"^1.42.0"); confirm whether version 1.42.0 exists in the npm registry and if not
update the i18next-cli entry to a valid published version (e.g., "^1.36.1") or
pin to the exact published release; edit the "i18next-cli" dependency in
package.json (alongside the existing "i18next" entry) to a registry-valid semver
and run npm/yarn install to verify resolution.

In `@packages/visual-editor/scripts/propagatePlatformToComponents.mjs`:
- Around line 10-16: The loadJsonSafe function currently swallows all errors and
returns {} for any failure; change loadJsonSafe to only treat missing files as
empty by catching the fs.readFile/JSON.parse error, checking the error code
(e.g., err.code === 'ENOENT') and returning {} only for that case, and
rethrowing (or propagate) any other errors so malformed JSON or read-permission
issues surface; update references to loadJsonSafe accordingly so callers still
get the parsed object when successful.
🟡 Minor comments (35)
packages/visual-editor/locales/platform/hu/visual-editor.json-244-244 (1)

244-244: ⚠️ Potential issue | 🟡 Minor

Localize the new linkTarget label.

This value is still English in the Hungarian file.

📝 Suggested translation
-    "linkTarget": "Link Target",
+    "linkTarget": "Hivatkozás célja",
packages/visual-editor/locales/components/pl/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Typo in translation value: "Breatcrumb" should be a proper Polish translation.

The value "Breatcrumb" appears to be a typo of the English word "Breadcrumb" rather than an actual Polish translation. This should be translated to Polish, e.g., "Ścieżka nawigacji" (navigation path) or "Nawigacja okruszkowa" (breadcrumb navigation).

🐛 Proposed fix
-  "breadcrumb": "Breatcrumb",
+  "breadcrumb": "Ścieżka nawigacji",
packages/visual-editor/locales/components/et/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Possible mistranslation: "Riivsaia" is the culinary breadcrumb.

"Riivsaia" in Estonian refers to breadcrumbs used in cooking (food item), not the navigation UI concept. For navigation breadcrumbs, consider using a term that conveys the path/trail concept, such as "Teekond" (path), "Jäljerida" (trail), or simply retaining "Breadcrumb" as a recognized UI term.

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

9-9: ⚠️ Potential issue | 🟡 Minor

Fix Swedish typo for breadcrumb label.

Line 9 looks misspelled (“Brödsmum”); likely intended “Brödsmulor”.

✏️ Suggested fix
-  "breadcrumb": "Brödsmum",
+  "breadcrumb": "Brödsmulor",
packages/visual-editor/locales/components/lv/visual-editor.json-35-37 (1)

35-37: ⚠️ Potential issue | 🟡 Minor

Translation inconsistency: "hours" translated as "Laiks" (time) instead of "Stundas" (hours).

Line 35 translates "hours" as "Laiks", which means "time" in Latvian. However, line 36 correctly uses "Stundu" (genitive of "Stundas") in "hoursSection": "Stundu sadaļa". This inconsistency suggests "hours" should be "Stundas" to match the section key and accurately represent business hours.

Proposed fix
-  "hours": "Laiks",
+  "hours": "Stundas",
packages/visual-editor/locales/components/hr/visual-editor.json-35-37 (1)

35-37: ⚠️ Potential issue | 🟡 Minor

Ambiguous and potentially incorrect translations for section labels.

A few translation concerns:

  • Line 35: "Sate" uses the accusative case, which is unusual for a standalone label. Consider "Sati" (nominative) or "Radno vrijeme" (working hours).
  • Lines 36-37: Both "hoursSection" and "informationSection" translate to the identical "Odjeljak" (Section), losing semantic distinction. Consider "Odjeljak s radnim vremenom" and "Informacijski odjeljak" to preserve context.
packages/visual-editor/locales/components/hr/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Potentially incorrect translation for "breadcrumb".

"Krušnica" means "breadbox" in Croatian, not the UI navigation concept of breadcrumbs. For navigation breadcrumbs, consider using "Navigacijska staza" (navigation path) or "Breadcrumb" (borrowed term), which are more commonly understood in Croatian UI contexts.

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

9-9: ⚠️ Potential issue | 🟡 Minor

Likely mistranslation: "Strúhanka" is the culinary term for breadcrumbs.

"Strúhanka" refers to bread crumbs used in cooking (for breading food). For UI navigation breadcrumbs, consider using "Navigačná cesta" (navigation path) or "Drobečková navigácia", which are more appropriate for the navigation context.

packages/visual-editor/locales/components/sk/visual-editor.json-35-37 (1)

35-37: ⚠️ Potential issue | 🟡 Minor

Potential translation inconsistencies.

A few translations may need review:

  1. Line 36: "hoursSection": "Čas" — "Čas" means "Time", but the key suggests "Hours Section". Consider "Otváracie hodiny" (Opening hours) for consistency with "hours": "Hodiny".

  2. Line 60: "phone": "Telefonovať" — This is the verb infinitive "To phone/call". If used as a noun label, "Telefón" would be more appropriate. If it's a call-to-action, "Zavolať" (Call) might work better.

  3. Lines 37, 75: Multiple keys (informationSection, servicesSection) translate to the same generic "Sekcia", which may cause ambiguity if displayed in different UI contexts.

Also applies to: 60-60

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

244-244: ⚠️ Potential issue | 🟡 Minor

Untranslated text in Croatian locale.

The value "Link Target" is English and should be translated to Croatian for consistency. Consider using "Cilj veze" or "Odredište veze".

Suggested fix
-    "linkTarget": "Link Target",
+    "linkTarget": "Cilj veze",
packages/visual-editor/locales/platform/it/visual-editor.json-483-484 (1)

483-484: ⚠️ Potential issue | 🟡 Minor

Inconsistent capitalization between singular and plural forms.

The singular form uses lowercase posizione while the plural form uses uppercase Località. For consistency, both should use the same casing (typically lowercase in Italian for common nouns in mid-sentence context).

📝 Suggested fix
-  "locationWithCount_one": "{{count}} posizione",
-  "locationWithCount_other": "{{count}} Località",
+  "locationWithCount_one": "{{count}} posizione",
+  "locationWithCount_other": "{{count}} località",
packages/visual-editor/locales/components/nb/visual-editor.json-25-25 (1)

25-25: ⚠️ Potential issue | 🟡 Minor

Fix the typo and verify new section labels are fully localized.

I see a spacing typo in coreInfoSection, and promoMedia is still in English. While you’re here, please confirm the new section labels (hours, hoursSection, informationSection, services, servicesSection) align with your Norwegian terminology.

🔧 Proposed fix for the typo
-  "coreInfoSection": "Kjerneinfo -seksjon",
+  "coreInfoSection": "Kjerneinfo-seksjon",

Also applies to: 35-37, 60-63, 74-75

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

9-9: ⚠️ Potential issue | 🟡 Minor

“パン塊” is an incorrect breadcrumb term.

For breadcrumb navigation, “パンくず” or “パンくずリスト” is the common UI wording in Japanese. Consider aligning to that.

✍️ Suggested wording
-  "breadcrumb": "パン塊",
+  "breadcrumb": "パンくず",
packages/visual-editor/locales/platform/ro/visual-editor.json-451-451 (1)

451-451: ⚠️ Potential issue | 🟡 Minor

Fix hyphen spacing in “Heading”.

There’s an extra space around the hyphen that will render awkwardly.

📝 Proposed fix
-  "Heading": "Îndreptându -se",
+  "Heading": "Îndreptându-se",
packages/visual-editor/locales/platform/tr/visual-editor.json-323-326 (1)

323-326: ⚠️ Potential issue | 🟡 Minor

Align “HoursTable” translation with existing label.

components.hoursTable uses “Saatler Tablosu”, but the option value is “Saat Tablosu”. Consider matching to avoid inconsistent UI wording.

✏️ Suggested translation tweak
-      "HoursTable": "Saat Tablosu",
+      "HoursTable": "Saatler Tablosu",
packages/visual-editor/locales/platform/tr/visual-editor.json-206-208 (1)

206-208: ⚠️ Potential issue | 🟡 Minor

Use the correct accusative form for “expand footer.”

“Altbilgi genişlet” is ungrammatical; the natural UI verb is “Altbilgiyi genişlet”.

✏️ Suggested translation tweak
-    "expandFooter": "Altbilgi genişlet",
+    "expandFooter": "Altbilgiyi genişlet",
packages/visual-editor/locales/platform/tr/visual-editor.json-479-484 (1)

479-484: ⚠️ Potential issue | 🟡 Minor

Use singular noun after numbers in Turkish.

For counted quantities, Turkish keeps the noun singular (e.g., “5 konum”, not “5 konumlar”). Also consider consistent casing between _one and _other.

✏️ Suggested translation tweak
-  "locationWithCount_one": "{{count}} Konum",
-  "locationWithCount_other": "{{count}} konumlar",
+  "locationWithCount_one": "{{count}} konum",
+  "locationWithCount_other": "{{count}} konum",
packages/visual-editor/locales/platform/tr/visual-editor.json-44-45 (1)

44-45: ⚠️ Potential issue | 🟡 Minor

Differentiate verb “close” from state “closed.”

With the new closed key, both close and closed translate to “Kapalı”. If close is a button/action, the more natural verb is “Kapat”, while “Kapalı” fits the state. Please confirm intent.

✏️ Suggested translation tweak
-  "close": "Kapalı",
+  "close": "Kapat",
   "closed": "Kapalı",
packages/visual-editor/locales/platform/cs/visual-editor.json-403-403 (1)

403-403: ⚠️ Potential issue | 🟡 Minor

Prefer the imperative form for “showCTA”.
“Zobrazit …” is infinitive; the imperative “Zobrazte …” aligns better with the Czech style guideline for action labels. Based on learnings: In Czech translations for the visual editor, prefer imperative verb forms that align with the English phrasing (e.g., use 'Naplňte nádobu' for 'Fill Container') rather than adjective+noun constructions.

✏️ Suggested update
-    "showCTA": "Zobrazit výzvu k akci",
+    "showCTA": "Zobrazte výzvu k akci",
packages/visual-editor/locales/platform/cs/visual-editor.json-451-451 (1)

451-451: ⚠️ Potential issue | 🟡 Minor

“Heading” is better translated as “Nadpis”.
“Záhlaví” maps to “Header” and can confuse with existing header labels.

✏️ Suggested update
-  "Heading": "Záhlaví",
+  "Heading": "Nadpis",
packages/visual-editor/locales/platform/cs/visual-editor.json-45-45 (1)

45-45: ⚠️ Potential issue | 🟡 Minor

Use sentence case for the “closed” status label.
Uppercase “ZAVŘENO” is inconsistent with other status strings and can read as shouting. Consider “Zavřeno” unless UI styling uppercases it.

✏️ Suggested update
-  "closed": "ZAVŘENO",
+  "closed": "Zavřeno",
packages/visual-editor/locales/components/nl/visual-editor.json-35-37 (1)

35-37: ⚠️ Potential issue | 🟡 Minor

Use compound form to match Dutch conventions and align with similar translations in this file.
"Uren sectie" (two words) is not standard Dutch spelling. The compound form "urensectie" follows Dutch rules for compound nouns and aligns with "Dienstensectie" (servicesSection) already in the file.

✏️ Proposed change
-  "hoursSection": "Uren sectie",
+  "hoursSection": "Urensectie",
packages/visual-editor/locales/components/nl/visual-editor.json-60-63 (1)

60-63: ⚠️ Potential issue | 🟡 Minor

Use idiomatic Dutch compound forms for consistency.
"Sectie Fotogalerij" should be "Fotogalerijsectie" and "Promo media" should be "Promomedia" per Dutch compound noun conventions (samenstellingen schrijf je aaneen). Note that "Promobanner" on line 62 already uses the correct compound form, establishing the pattern.

✏️ Proposed changes
-  "photoGallerySection": "Sectie Fotogalerij",
+  "photoGallerySection": "Fotogalerijsectie",
...
-  "promoMedia": "Promo media",
+  "promoMedia": "Promomedia",
packages/visual-editor/locales/platform/et/visual-editor.json-483-484 (1)

483-484: ⚠️ Potential issue | 🟡 Minor

Change locationWithCount_other from nominative plural to partitive singular.

Estonian grammar requires partitive singular after numerals greater than 1 (e.g., "kaks asukohta", "kolm asukohta"). The current nominative plural form "asukohad" is grammatically incorrect; it should be the partitive singular "asukohta".

✏️ Suggested fix
-  "locationWithCount_other": "{{count}} asukohad",
+  "locationWithCount_other": "{{count}} asukohta",
packages/visual-editor/locales/platform/sk/visual-editor.json-483-484 (1)

483-484: ⚠️ Potential issue | 🟡 Minor

Align singular/plural noun for location counts.
The singular “umiestnenie” doesn’t match the plural “lokality.” Consider “lokalita/lokality.”

Suggested wording
-  "locationWithCount_one": "{{count}} umiestnenie",
-  "locationWithCount_other": "{{count}} lokality",
+  "locationWithCount_one": "{{count}} lokalita",
+  "locationWithCount_other": "{{count}} lokality",
packages/visual-editor/locales/platform/sk/visual-editor.json-207-207 (1)

207-207: ⚠️ Potential issue | 🟡 Minor

Prefer an imperative form for the expand action.
“Rozširovať” reads like an ongoing action; UI commands elsewhere use an imperative (“Zobraziť”, “Skryť”). Consider “Rozšíriť pätu” to match “Rozšírená päta.”

Suggested wording
-    "expandFooter": "Rozširovať pätu",
+    "expandFooter": "Rozšíriť pätu",
packages/visual-editor/locales/platform/sk/visual-editor.json-451-451 (1)

451-451: ⚠️ Potential issue | 🟡 Minor

Translate “Heading” as a noun.
“Hlavný” is an adjective; for a component label, “Nadpis” is clearer.

Suggested wording
-  "Heading": "Hlavný",
+  "Heading": "Nadpis",
packages/visual-editor/locales/platform/sk/visual-editor.json-467-467 (1)

467-467: ⚠️ Potential issue | 🟡 Minor

Use a noun for “Link.”
“Prepojiť” is a verb, while nearby labels use the noun “odkaz” (e.g., “linkLabel”).

Suggested wording
-  "Link": "Prepojiť",
+  "Link": "Odkaz",
packages/visual-editor/scripts/generateTranslations.mjs-74-76 (1)

74-76: ⚠️ Potential issue | 🟡 Minor

Tighten context-marker stripping to avoid deleting legitimate bracketed text.

The current regex removes any bracketed content, which can truncate translations that legitimately include brackets. Target the specific [[context: ...]] marker instead.

🐛 Suggested fix
 function removeEmbeddedContext(text) {
-  return text.replace(/\[+.*?\]+/g, "").trim();
+  return text
+    .replace(/\s*\[\[context:.*?]]\s*/g, " ")
+    .replace(/\s{2,}/g, " ")
+    .trim();
 }
packages/visual-editor/locales/components/it/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Incorrect translation: "Pane" means "Bread", not "Breadcrumb".

The Italian translation for breadcrumb is incorrect. "Pane" literally means "Bread" in Italian. For a navigation breadcrumb, the correct translation would be "Percorso di navigazione" (navigation path) or "Briciole di pane" (literal breadcrumbs, commonly used in UI contexts).

Suggested fix
-  "breadcrumb": "Pane",
+  "breadcrumb": "Percorso di navigazione",
packages/visual-editor/locales/components/fi/visual-editor.json-37-37 (1)

37-37: ⚠️ Potential issue | 🟡 Minor

Incorrect translation: "Tiedonsiirto" means "Data transfer", not "Information Section".

The Finnish translation for informationSection is incorrect. "Tiedonsiirto" means "Data transfer" in Finnish. The correct translation for "Information Section" would be "Tiedot-osio" or "Tietoja-osio".

Suggested fix
-  "informationSection": "Tiedonsiirto",
+  "informationSection": "Tiedot-osio",
packages/visual-editor/locales/components/cs/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Incorrect translation: "Strouha" means "ditch/gutter", not "Breadcrumb".

The Czech translation for breadcrumb is incorrect. "Strouha" means "ditch" or "gutter" in Czech. For navigation breadcrumbs, the correct translation would be "Drobečková navigace" (breadcrumb navigation) or simply retain "Breadcrumb" as many Czech applications do.

Suggested fix
-  "breadcrumb": "Strouha",
+  "breadcrumb": "Drobečková navigace",
packages/visual-editor/locales/components/fr/visual-editor.json-21-23 (1)

21-23: ⚠️ Potential issue | 🟡 Minor

Interpolation variable {{numéro}} is incorrect — must be {{number}}.

The code passes number as the interpolation parameter (line 85 in FooterUtilityImagesSlot.tsx), but the French locale uses {{numéro}}. This mismatch will cause the French translation to display literally as "Image utilitaire {{numéro}}" instead of interpolating the image number. The English locale correctly uses {{number}}.

packages/visual-editor/locales/platform/zh-TW/visual-editor.json-479-484 (1)

479-484: ⚠️ Potential issue | 🟡 Minor

Align count phrasing between _one and _other.
_other currently drops the classifier, yielding “2位置”. Match the _one phrasing for consistency.

🛠️ Suggested fix
-  "locationsNear_other": "“{{name}}”附近有{{count}}位置",
+  "locationsNear_other": "“{{name}}”附近有 {{count}} 個位置",
...
-  "locationWithCount_other": "{{count}}位置",
+  "locationWithCount_other": "{{count}} 個位置",
packages/visual-editor/locales/components/zh-TW/visual-editor.json-42-47 (1)

42-47: ⚠️ Potential issue | 🟡 Minor

Fix inconsistent count wording between _one and _other.
_one uses 「個位置」 while _other drops the classifier, producing outputs like “2位置”. Align the _other strings with the _one format.

🛠️ Suggested fix
-  "locationWithCount_other": "{{count}}位置",
+  "locationWithCount_other": "{{count}} 個位置",
...
-  "locationsNear_other": "“{{name}}”附近有{{count}}位置",
+  "locationsNear_other": "“{{name}}”附近有 {{count}} 個位置",
🧹 Nitpick comments (16)
packages/visual-editor/locales/components/tr/visual-editor.json (2)

25-37: Minor capitalization inconsistency in section labels.

There's inconsistent capitalization between section translations:

  • Title case: "coreInfoSection": "Çekirdek Bilgi Bölümü", "hoursSection": "Saatler Bölümü"
  • Sentence case: "informationSection": "Bilgi bölümü"

Consider aligning to a consistent style (likely title case to match the majority).

💅 Suggested fix for consistency
-  "informationSection": "Bilgi bölümü",
+  "informationSection": "Bilgi Bölümü",

62-63: Minor capitalization inconsistency in promo labels.

Similar inconsistency here:

  • "promoBanner": "Promosyon Banner'ı" (title case)
  • "promoMedia": "Promosyon medyası" (sentence case)
💅 Suggested fix for consistency
-  "promoMedia": "Promosyon medyası",
+  "promoMedia": "Promosyon Medyası",
packages/visual-editor/src/components/Locator.tsx (1)

83-93: Pluralization fallback may produce grammatically incorrect text.

The defaultValue uses the singular form ("mile", "kilometer") regardless of count. If translation keys are missing, i18next will return the singular default even when count > 1, producing strings like "5 mile" instead of "5 miles".

Consider using a pluralized fallback or ensuring the translation keys always exist:

🔧 Proposed fix with pluralized fallback
 const translateDistanceUnit = (
   t: (key: string, options?: Record<string, unknown>) => string,
   unit: "mile" | "kilometer",
   count: number
 ) => {
   if (unit === "mile") {
-    return t("mile", { count, defaultValue: "mile" });
+    return t("mile", { count, defaultValue: count === 1 ? "mile" : "miles" });
   }
 
-  return t("kilometer", { count, defaultValue: "kilometer" });
+  return t("kilometer", { count, defaultValue: count === 1 ? "kilometer" : "kilometers" });
 };
packages/visual-editor/locales/components/lv/visual-editor.json (2)

9-9: Consider using a UI-appropriate term for "breadcrumb".

"Rīvmaize" is the literal Latvian translation for breadcrumbs (the food item). For navigation breadcrumbs in UI contexts, some locales use an adapted term or keep the English "breadcrumb" to preserve the metaphor. This might confuse users expecting a navigation-related term.

Please verify with a native Latvian speaker or the localization team whether "Rīvmaize" is the preferred UI term for navigation breadcrumbs, or if an alternative like "Navigācijas ceļš" (navigation path) would be clearer.


60-63: "promoMedia" left untranslated.

"promoMedia": "Promo Media" remains in English. If this is intentional (e.g., brand term), this is fine. Otherwise, consider translating to Latvian such as "Reklāmas mediji" for consistency with other translated keys like "promoBanner": "Reklāmas reklāmkarogs".

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

131-132: Duplicate keys with different casing at root level.

Both cta and CTA exist at the same nesting level with identical values. This creates ambiguity for developers choosing which key to use, and one may become orphaned over time. If these serve distinct purposes (e.g., component name vs. field label), consider documenting the distinction or using different key paths.

The same pattern appears with link/Link at lines 466-467.


190-191: Duplicate field keys: ctaVariant vs CTAVariant.

Similar to root-level duplicates, fields.ctaVariant (line 190) and fields.CTAVariant (line 191) both exist with nearly identical translations (only whitespace differs: "CTA -variantti" vs "CTA-variantti"). Consider consolidating to a single key.


324-325: PascalCase keys in fields.options break convention.

HoursStatus and HoursTable use PascalCase while surrounding options like hour12, hour24, and image use camelCase. This suggests these may be component type identifiers extracted differently by the new tooling.

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

483-484: Consider adding _few plural form for complete Croatian pluralization.

Croatian uses different plural forms: "lokacija" (1), "lokacije" (2-4), "lokacija" (5+). The current setup handles 1 and 5+ correctly, but displaying "2 lokacija" instead of "2 lokacije" would be grammatically incorrect.

If i18next is configured for Croatian plural rules, consider adding:

"locationWithCount_few": "{{count}} lokacije"
packages/visual-editor/locales/platform/lt/visual-editor.json (1)

324-325: Duplicate keys under options: HoursStatus and HoursTable.

These PascalCase keys duplicate existing keys at components.hoursStatus (line 95) and components.hoursTable (line 96) with identical translations. If intentional for different namespaces, this is acceptable; otherwise, consider referencing the existing keys.

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

9-9: Confirm breadcrumb term matches your HU glossary.

“Zsemlemorzsa” is very literal; if your UI standard uses “morzsamenü” (or similar), consider aligning for consistency.

🔤 Example adjustment (if this matches your glossary)
-  "breadcrumb": "Zsemlemorzsa",
+  "breadcrumb": "Morzsamenü",

35-37: Consider aligning “hours” terminology with standard business-hours wording.

If your product glossary prefers “Nyitvatartás”, these two keys might read more naturally.

🔤 Example adjustment (if this matches your glossary)
-  "hours": "Órák",
-  "hoursSection": "Órás szakasz",
+  "hours": "Nyitvatartás",
+  "hoursSection": "Nyitvatartás szakasz",

60-63: Align promo banner wording with existing “szalaghirdetés” usage.

heroBanner uses “szalaghirdetés”, so keeping promoBanner consistent may improve UX consistency.

🔤 Example adjustment
-  "promoBanner": "Promó banner",
+  "promoBanner": "Promóciós szalaghirdetés",
packages/visual-editor/scripts/generateTranslations.mjs (2)

93-115: Add request timeout to prevent hanging on stalled API calls.

The fetch call lacks a timeout and can hang indefinitely if the Google Translate API becomes unresponsive, freezing the CLI. Node 18+ (which this package supports) has global fetch available, so no polyfill is needed—only a timeout.

⏱️ Add a request timeout
 async function translateText(text, targetLang) {
   const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${defaultLng}&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`;
-  const res = await fetch(url);
+  const controller = new AbortController();
+  const timeout = setTimeout(() => controller.abort(), 15000);
+  let res;
+  try {
+    res = await fetch(url, { signal: controller.signal });
+  } finally {
+    clearTimeout(timeout);
+  }

198-221: Batch requests to avoid triggering the unofficial endpoint's rate-limiting.

The translate.googleapis.com/translate_a/single endpoint (gtx client) is unofficial and uses dynamic rate limiting—it can return 429 errors or temporarily block your IP if too many requests arrive concurrently. Batching helps prevent hitting these thresholds.

🔧 Batch requests with a small concurrency window
-    await Promise.allSettled(
-      keysToTranslate.map(async (key) => {
+    const BATCH_SIZE = 5;
+    for (let i = 0; i < keysToTranslate.length; i += BATCH_SIZE) {
+      const batch = keysToTranslate.slice(i, i + BATCH_SIZE);
+      await Promise.allSettled(
+        batch.map(async (key) => {
           let english = defaultJson[key];
           const context = extractContextFromKey(key, defaultKeySet);
@@
-      })
-    );
+        })
+      );
+    }
packages/visual-editor/i18next-cli.platform.config.ts (1)

47-47: Platform config includes additional translation functions pt and msg.

The platform config extracts from functions ["t", "*.t", "i18next.t", "pt", "msg"] while the components config only uses ["t", "*.t", "i18next.t"]. Ensure this difference is intentional and that pt and msg are platform-specific translation helpers.

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

🤖 Fix all issues with AI agents
In `@packages/visual-editor/scripts/generateTranslations.mjs`:
- Around line 154-160: The loadJsonSafe function currently swallows all
read/parse errors by returning {}, which hides corrupted JSON; update
loadJsonSafe to catch the error into a variable, treat only ENOENT as a benign
case (return {}), but rethrow any other errors (including JSON.parse failures)
so callers can handle or fail fast; locate the async function loadJsonSafe and
change its try/catch to inspect err.code === 'ENOENT' before returning {} and
otherwise throw err.
- Around line 101-103: The removeEmbeddedContext function is too aggressive:
replace the broad regex /\[+.*?\]+/g with a marker-specific pattern so only the
translation context marker is stripped; update removeEmbeddedContext to use a
dedicated regex constant (e.g. match only "[[context...]]" or "[context:...]"
depending on project convention) such as /\[\[context:.*?\]\]/g or
/\[context:.*?\]/g and trim the result, and ensure the function references that
regex (removeEmbeddedContext) so legitimate bracketed text like "[beta]" is
preserved.
- Around line 212-280: In translateFile, currently failures only increment
failCount and still call saveJson causing partial writes; after the
Promise.allSettled completes check failCount and if > 0 prevent writing (or
abort) by skipping saveJson and logging/throwing an error so no partial
translations are persisted. Concretely: after the await Promise.allSettled(...)
block inspect failCount and if it's non‑zero and not isDryRun, log a clear error
including type/lng and either throw or continue to the next language instead of
calling saveJson; ensure the finalConsole log reflects that the file was not
saved when failures occurred.

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

🤖 Fix all issues with AI agents
In `@packages/visual-editor/locales/components/fr/visual-editor.json`:
- Line 9: Replace the incorrect French translation for the "breadcrumb" key in
the locales JSON: update the value of the "breadcrumb" property (in
packages/visual-editor/locales/components/fr/visual-editor.json) from
"Chapelure" to the standard UI term "Fil d'Ariane" so the breadcrumb navigation
label uses the correct French UX phrasing.

In `@packages/visual-editor/locales/components/ja/visual-editor.json`:
- Line 9: The "breadcrumb" translation value currently reads "パン塊" (wrong
meaning); update the JSON entry for the "breadcrumb" key in
packages/visual-editor/locales/components/ja/visual-editor.json to use the
correct Japanese UI term "パンくずリスト" (or alternatively "Breadcrumb" if you prefer
to match the Romanian style) so the label accurately represents breadcrumb
navigation.

In `@packages/visual-editor/locales/platform/ja/visual-editor.json`:
- Around line 479-484: Unify the Japanese plural forms so _one and _other are
identical: update "locationWithCount_other" to match "locationWithCount_one" by
using the more natural counter phrase "{{count}} 件の場所"; locate the two keys
"locationWithCount_one" and "locationWithCount_other" in the visual-editor.json
and replace the _other value accordingly so both read "{{count}} 件の場所".

In `@packages/visual-editor/locales/platform/ro/visual-editor.json`:
- Line 451: Replace the incorrect Romanian value for the "Heading" key
(currently "Îndreptându -se") with the correct UI term "Titlu" (or "Rubrică" to
match existing "headingLevel": "Nivel de rubrică"); also remove the spurious
space before any hyphen if present (fix the hyphenation artifact). Update the
"Heading" entry so it reads simply "Titlu" to align with "headingLevel" and
other UI label translations.
- Around line 131-132: The cta/CTA and link/Link keys are ambiguous in Romanian
and mirror each other; either give them semantically distinct translations to
match how they’re used (e.g., distinguish the UI label used by CtaWrapper.tsx's
pt("cta","CTA") from the group label used by CTAGroup.tsx's pt("CTA","CTA")), or
consolidate usage by updating the source to a single key and removing the
duplicate in the locale. Locate the keys "cta"/"CTA" and "link"/"Link" in
packages/visual-editor/locales/platform/ro/visual-editor.json and: 1) if keeping
both keys, replace the duplicate values with different, context-appropriate
Romanian strings that reflect the different UI contexts referenced by
CtaWrapper.tsx and CTAGroup.tsx; or 2) if consolidating, change the source
references (pt calls in CtaWrapper.tsx and CTAGroup.tsx) to use one canonical
key and remove the redundant locale entry.

In `@packages/visual-editor/scripts/checkInterpolationVariables.mjs`:
- Around line 306-335: The code currently calls autoFixSingleVariableMismatch
and, if it returns null for a single-variable mismatch, falls through without
reporting; update the logic around the autoFixSingleVariableMismatch call (the
block using CHECK_ONLY, autoFixSingleVariableMismatch, localeFlat, fixedEntries,
and issues) so that when details.mismatchCount > 0 and either CHECK_ONLY is true
or autoFixSingleVariableMismatch returned null (i.e., no safe fix), you push an
issue entry into the issues array with the same fields currently used (instance,
locale, file, line via findLineNumberForKey, key, expected, actual,
mismatchCount); alternatively change the later condition to check
details.mismatchCount > 0 instead of > 1 to ensure single-variable mismatches
that couldn't be fixed are reported.
🧹 Nitpick comments (8)
packages/visual-editor/locales/platform/fr/visual-editor.json (1)

68-68: Inconsistent key casing: PascalCase keys mixed with camelCase siblings.

DirectoryGrid (line 68), Video (line 124), HoursStatus (line 324), and HoursTable (line 325) use PascalCase while all surrounding keys in the same objects use camelCase (e.g., directory, videoSection, hoursStatus). If these are extracted directly from React component names, consider normalizing them to camelCase in the i18next-cli config (e.g., via a key transformation) to keep the locale files consistent.

Example normalization
-    "DirectoryGrid": "Grille de répertoire",
+    "directoryGrid": "Grille de répertoire",
...
-    "Video": "Vidéo",
+    "video": "Vidéo",
...
-      "HoursStatus": "Statut des heures",
-      "HoursTable": "Tableau des heures",
+      "hoursStatus": "Statut des heures",
+      "hoursTable": "Tableau des heures",

(Corresponding source code t() calls would need to be updated to match.)

Also applies to: 124-124, 324-325

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

68-68: PascalCase keys break the prevailing camelCase convention.

DirectoryGrid (line 68), Video (line 124), HoursStatus (line 324), HoursTable (line 325), and Heading (line 451) all use PascalCase while the rest of the file is consistently camelCase. This is likely auto-extracted from React component names by i18next-cli. If so, this is expected, but worth confirming the extraction config is intentional and consistent across all locales.

Also applies to: 124-124, 324-325, 451-451

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

68-68: PascalCase keys mixed with camelCase siblings.

DirectoryGrid (Line 68) and Video (Line 124) within components, and HoursStatus/HoursTable (Lines 324–325) within options, break the camelCase convention used by all their sibling keys. This is likely an artifact of how the new i18next-cli extraction maps source-code identifiers to translation keys. Not a functional issue, but worth noting for consistency.

Also applies to: 124-124, 324-325

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

68-68: PascalCase keys (DirectoryGrid, Video, CTAVariant, HoursStatus, HoursTable) break the camelCase convention used by the rest of this file.

The vast majority of keys in this file follow camelCase (e.g., directoryRootLinkLabel, hoursStatus, hoursTable, ctaVariant). These PascalCase additions appear to be auto-extracted component/type names. Notably, some of these duplicate existing camelCase keys in other sections (e.g., components.hoursStatus at line 95 vs options.HoursStatus at line 324).

If these keys are generated by the new i18next-cli extraction, it may be worth configuring the key naming to enforce consistent casing.

Also applies to: 124-124, 191-191, 324-325

packages/visual-editor/scripts/propagatePlatformToComponents.mjs (1)

37-49: Consider extracting shared utilities (flatten, unflatten, sortObject, loadJsonSafe) into a common module.

These functions are duplicated nearly verbatim across propagatePlatformToComponents.mjs, checkInterpolationVariables.mjs, and generateTranslations.mjs. A shared scripts/utils.mjs would reduce maintenance surface and ensure bug fixes propagate consistently (e.g., the loadJsonSafe error-handling fix in this file isn't applied in the other two scripts).

Also applies to: 54-70, 75-86

packages/visual-editor/scripts/checkInterpolationVariables.mjs (1)

289-292: Double file read: localePath is read twice — once raw and once parsed.

Line 291 reads the raw file content for line-number lookup, then line 292 reads and parses the same file again via loadJsonSafe. Consider reading once and reusing.

♻️ Proposed optimization
-    const localeRaw = await fs.readFile(localePath, "utf-8").catch(() => "");
-    const localeFlat = flatten(await loadJsonSafe(localePath));
+    let localeRaw = "";
+    let localeFlat = {};
+    try {
+      localeRaw = await fs.readFile(localePath, "utf-8");
+      localeFlat = flatten(JSON.parse(localeRaw));
+    } catch (error) {
+      if (error?.code !== "ENOENT") throw error;
+    }
packages/visual-editor/scripts/generateTranslations.mjs (2)

155-178: Relies on an unofficial, undocumented Google Translate endpoint.

translate.googleapis.com/translate_a/single?client=gtx is not a public API — it's subject to rate limiting, CAPTCHAs, and can break without notice. For a CI/development tool this may be acceptable, but be aware of the fragility.

Additionally, Promise.allSettled on line 275 fires all key translations concurrently with no concurrency limit or rate throttling, which increases the chance of hitting rate limits for locales with many missing keys.

♻️ Add basic concurrency limiting

Consider batching translations (e.g., 5 at a time) to avoid hammering the endpoint:

// Simple serial or batched approach
for (const key of keysToTranslate) {
  // translate one at a time, or use a small batch with Promise.all
}

203-215: flatten uses for...in instead of Object.keys(), inconsistent with other scripts.

for (const key in obj) iterates inherited enumerable properties, unlike Object.keys(obj) used in the other two scripts' flatten implementations. For JSON.parse output this is benign, but it's an inconsistency that could surprise if the input shape ever changes.

♻️ Align with other scripts
-  for (const key in obj) {
+  for (const key of Object.keys(obj)) {

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

Caution

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

⚠️ Outside diff range comments (2)
packages/visual-editor/locales/platform/sv/visual-editor.json (1)

28-28: ⚠️ Potential issue | 🟡 Minor

Fix invalid Swedish translation for breadcrumb navigation key.

"Brödsmum" is not valid Swedish. The correct translation is "Brödsmula" (singular form, matching the key name). This affects user-facing breadcrumb navigation text.

Proposed fix
-  "breadcrumb": "Brödsmula",
+  "breadcrumb": "Brödsmula",
packages/visual-editor/scripts/generateTranslations.ts (1)

180-205: ⚠️ Potential issue | 🟡 Minor

Unofficial Google Translate endpoint with no timeout or rate limiting.

  1. The client=gtx endpoint is an undocumented, unofficial Google Translate API. It can be rate-limited or blocked without notice.
  2. fetch has no AbortSignal timeout — a hung request will stall the pipeline indefinitely.

Consider adding a timeout:

Proposed timeout addition
 const translateText = async (
   text: string,
   targetLang: string
 ): Promise<string> => {
   const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${DEFAULT_LANGUAGE}&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`;
-  const response = await fetch(url);
+  const response = await fetch(url, {
+    signal: AbortSignal.timeout(10_000),
+  });
🤖 Fix all issues with AI agents
In `@packages/visual-editor/locales/components/ja/visual-editor.json`:
- Around line 44-47: Unify the Japanese singular/plural variants so they are
identical for each key: make locationsNear_one and locationsNear_other the same
string, and make locationsWithinDistanceOf_one and
locationsWithinDistanceOf_other the same string; choose a single
particle/phrase, consistent spacing around interpolation tokens (e.g.,
"「{{name}}」付近の {{count}} か所の場所" or preferred equivalent), and use the same
counter word (件 or か所) in both forms so translators and runtime output do not
diverge when count changes.

In `@packages/visual-editor/locales/platform/cs/visual-editor.json`:
- Around line 323-324: The JSON contains duplicated keys with inconsistent
casing: "hoursTable" (components) vs "HoursTable" (fields.options) and
"hoursStatus" vs "HoursStatus"; decide whether both variants are required or
consolidate them—either make the two "hoursTable"/"HoursTable" values identical
(e.g., both "TABULKA HODINY" or both "Tabulka hodin") and do the same for
"hoursStatus"/"HoursStatus", or remove the redundant keys so only the canonical
key (prefer the existing "hoursTable" and "hoursStatus" in components) remains;
update the translations accordingly in visual-editor.json to ensure consistent
casing/values for the keys HoursTable/hoursTable and HoursStatus/hoursStatus.
- Around line 189-190: The JSON contains duplicate translation keys "ctaVariant"
and "CTAVariant" with identical values; remove the redundant key or consolidate
them depending on runtime usage: search the codebase for references to
CTAVariant and ctaVariant (case-sensitive) and if only one form is used keep
that single key (prefer the existing "ctaVariant"), otherwise keep both but
ensure translations stay synchronized and add a comment explaining why both
forms are required; update the file to remove the duplicate entry (or add the
explanatory comment) and run the provided ripgrep checks to verify no callers
break.

In `@packages/visual-editor/locales/platform/da/visual-editor.json`:
- Line 239: The Danish locale entry for the key "linkLabel" currently contains
English text ("Link Label"); update the value for "linkLabel" in the
visual-editor.json Danish file to a proper Danish translation (e.g.,
"Link-etiket" or another agreed translation) or explicitly mark it as
untranslated/TO_TRANSLATE if you want to flag it for later review; reference the
nearby key "linkTarget" to match localization style and ensure the JSON string
remains valid.

In `@packages/visual-editor/locales/platform/fi/visual-editor.json`:
- Around line 481-482: Update the Finnish pluralization strings for the location
count: change the translation value for "locationWithCount_other" from the
nominative plural to the partitive singular used after numerals, i.e., replace
the current "{{count}} sijainnit" with "{{count}} sijaintia"; ensure the keys
"locationWithCount_one" and "locationWithCount_other" remain unchanged and
consistent with nearby entries like "{{count}} sijaintia lähellä kohdetta
{{name}}".

In `@packages/visual-editor/locales/platform/fr/visual-editor.json`:
- Around line 239-244: The translation for the key "linkType" is inconsistent
("Type de liaison") with adjacent keys like "linkLabel" and "linkTarget" which
use "lien"; update the value for linkType to "Type de lien" so all three keys
(linkType, linkLabel, linkTarget) use the same terminology and ensure
consistency across the locale file.
- Line 402: The translation for the key "showCTA" is inconsistent — replace its
value "Afficher l'incitation à l'action" with the acronym form used elsewhere;
update the "showCTA" entry to use "Afficher le CTA" so it matches keys like
"cta" and "showPrimaryCTA".

In `@packages/visual-editor/locales/platform/hr/visual-editor.json`:
- Line 243: The entry for the key "linkTarget" is still in English; replace its
value with the Croatian translation to match the surrounding keys (e.g., change
"Link Target" to "Odredište veze" for the "linkTarget" key) so the locale file
remains fully translated and consistent with "linkLabel" and "linkType".

In `@packages/visual-editor/locales/platform/hu/visual-editor.json`:
- Around line 189-190: The locale file contains near-duplicate keys ctaVariant
and CTAVariant with inconsistent values (one has an extra space before the
hyphen); update them so they use the same normalized translation string (e.g.,
"CTA-változat") and consistent spacing, and if both keys are required by the
code ensure both entries have identical values, otherwise remove or consolidate
the duplicate key to the canonical identifier (ctaVariant or CTAVariant) used by
the source code.
- Line 243: Replace the English value for the JSON key "linkTarget" with the
correct Hungarian translation; locate the "linkTarget" entry in
packages/visual-editor/locales/platform/hu/visual-editor.json and change "Link
Target" to a Hungarian string such as "Hivatkozás célja" (or another approved
Hungarian translation) ensuring the file remains valid JSON and preserves
surrounding commas/formatting.

In `@packages/visual-editor/locales/platform/it/visual-editor.json`:
- Around line 481-482: The translation keys locationWithCount_one and
locationWithCount_other have inconsistent capitalization ("{{count}} posizione"
vs "{{count}} Località"); update locationWithCount_other to match the singular
style by changing "{{count}} Località" to "{{count}} località" so both forms use
the same lowercase capitalization.

In `@packages/visual-editor/locales/platform/nb/visual-editor.json`:
- Line 244: The JSON key "linkType" currently has an English value ("Link
Type"); replace that value with the correct Norwegian translation (e.g.,
"Koblingstype" or "Linktype") so the locale entry for "linkType" is in
Norwegian; update the value for the "linkType" key in the visual-editor.nb
locale JSON to the chosen Norwegian string.

In `@packages/visual-editor/locales/platform/nl/visual-editor.json`:
- Line 611: The Dutch translation for the key theme.fontWeight.fontWeight is
incorrect—"Lettertype" means "Font" not "Font Weight"; update the value for the
"fontWeight" key (the one nested under theme.fontWeight / the JSON entry
currently "Lettertype") to a correct Dutch term such as "Lettergewicht" or
"Tekstdikte" so it correctly reflects "Font Weight".

In `@packages/visual-editor/locales/platform/pl/visual-editor.json`:
- Around line 481-482: The pluralization for the key "locationWithCount" only
defines `_one` and `_other`, which is incorrect for Polish; add `_few` and
`_many` variants so i18next/CLDR plural rules can select correct forms (e.g.,
`_few` => "{{count}} lokalizacje", `_many` => "{{count}} lokalizacji") alongside
the existing `_one` and `_other`; apply the same pattern to any other Polish
plural keys (e.g., "kilometer", "mile", "totalReviews", "locationsNear") to
ensure counts map to one/few/many/other correctly.

In `@packages/visual-editor/locales/platform/sk/visual-editor.json`:
- Line 450: The JSON value for the "heading" key is mistranslated as
"Smerovanie" (routing); update the value to the correct Slovak term "Nadpis" for
the "heading" key in this file and also search for the "sectionHeading" key
(mentioned at line 391) and replace its value "Smerovanie sekcie" with "Nadpis
sekcie" if appropriate to keep consistency; ensure you only change the string
values for the keys "heading" and "sectionHeading" (no other keys or
formatting).
- Around line 481-482: Change the inconsistent Slovak wording so both keys use
the same root "lokácia"/"lokality": update locationWithCount_one to use
"{{count}} lokácia" (singular) and keep/update locationWithCount_other to
"{{count}} lokality" (plural) so the singular and plural forms are consistent
and use the same concept.

In `@packages/visual-editor/locales/platform/sv/visual-editor.json`:
- Line 68: Remove the unused PascalCase locale keys HoursStatus and HoursTable
from the visual-editor JSON to avoid duplicate entries—keep the camelCase keys
used by the code (components.hoursStatus and components.hoursTable) and retain
intentional PascalCase keys like DirectoryGrid and Video; locate the two
PascalCase entries named "HoursStatus" and "HoursTable" in the file and delete
those lines so only the camelCase variants remain.

In `@packages/visual-editor/locales/platform/zh-TW/visual-editor.json`:
- Around line 481-482: The two plural keys locationWithCount_one and
locationWithCount_other are inconsistent (one uses " {{count}} 個位置" with a space
and classifier, the other uses "{{count}}位置" without them); update the
translations so both plural forms match (e.g., set locationWithCount_other to
the same string as locationWithCount_one or vice versa) to ensure consistent
pluralization and display across zh-TW; edit the entries for
locationWithCount_one and locationWithCount_other in the visual-editor.json
accordingly.

In `@packages/visual-editor/locales/platform/zh/visual-editor.json`:
- Around line 481-482: The two translation keys locationWithCount_one and
locationWithCount_other are inconsistent; make both identical by using the same
Chinese phrase with the classifier — update locationWithCount_other to match
locationWithCount_one (i.e., both should be "{{count}} 个位置") so both plural
forms are the same.
- Around line 189-190: Remove the duplicate top-level locale keys "ctaVariant"
and "CTAVariant": either delete both if unused, or consolidate by keeping a
single canonical key (preferably "fields.ctaVariant") and move/rename the
translated value there; then run a quick search for usages of "ctaVariant" or
"CTAVariant" to update any callers to the canonical key and ensure msg()/i18n
lookups still resolve correctly.
🧹 Nitpick comments (15)
packages/visual-editor/locales/platform/fi/visual-editor.json (1)

68-68: Inconsistent key casing: PascalCase keys among camelCase siblings.

"DirectoryGrid" and "Video" use PascalCase while all surrounding keys in components use camelCase (e.g., directoryCard, videoSection). The same pattern appears for CTAVariant (Line 190), HoursStatus (Line 323), and HoursTable (Line 324). If these map directly to component/option names in code, this may be intentional — but it's worth confirming the convention so lookups don't silently miss due to casing mismatches.

,

#!/bin/bash
# Check if PascalCase keys like DirectoryGrid, Video, HoursStatus, HoursTable, CTAVariant
# are used consistently across all locale files in this directory tree
echo "=== PascalCase keys in platform locale files ==="
rg -n '"(DirectoryGrid|Video|CTAVariant|HoursStatus|HoursTable)"' --type json -g '*/locales/platform/*' | head -40

echo ""
echo "=== Check component code referencing these keys ==="
rg -n 'DirectoryGrid|HoursStatus|HoursTable' --type ts --type tsx -g '!**/locales/**' -g '!node_modules/**' | head -30

Also applies to: 124-124

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

68-68: PascalCase keys mixed with camelCase siblings.

DirectoryGrid (Line 68), Video (Line 124), HoursStatus (Line 323), and HoursTable (Line 324) use PascalCase, while surrounding keys are camelCase (directory, videoSection, hour12, hour24). This is likely driven by the new i18next-cli extracting component/enum names verbatim, but it creates an inconsistent convention within the same JSON object.

If PascalCase is the intended convention going forward for component-derived keys, consider documenting it. Otherwise, normalizing to camelCase would keep the file consistent.

Also applies to: 124-124, 323-324

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

68-68: Inconsistent key casing: PascalCase keys among camelCase siblings.

"DirectoryGrid" (Line 68) and "Video" (Line 124) use PascalCase, while all surrounding sibling keys in components use camelCase (e.g., directory, videoSection, aboutSection). The same pattern appears in fields.options with "HoursStatus" / "HoursTable" (Lines 323–324). This likely reflects the i18next-cli extracting raw component/type names. Consider normalizing to camelCase for consistency, or documenting the convention if PascalCase is intentional for component-name keys.

Also applies to: 124-124

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

68-68: Normalize PascalCase keys to camelCase for consistency.

Keys DirectoryGrid (line 68), Video (line 124), HoursStatus (line 323), and HoursTable (line 324) use PascalCase while the file overwhelmingly uses camelCase (e.g., directory, videoSection, hoursStatus, hoursTable). Note that camelCase equivalents already exist: hoursStatus (line 95) and hoursTable (line 96), creating redundancy. Either standardize all component/field names to camelCase to match the file convention, or document the rationale for the mixed convention.

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

28-28: Pre-existing typo: "Breatcrumb" → "Breadcrumb".

"Breatcrumb" (Line 28) and "Breatcrumbs" (Line 60) appear to be typos — the English loanword should be "Breadcrumb"/"Breadcrumbs". Not introduced in this PR, but worth fixing while the locale files are being reworked.

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

68-68: PascalCase keys in locale file correspond to source code identifiers and appear intentional, but confirm this pattern is expected for future consistency.

DirectoryGrid (line 68), Video (line 124), HoursStatus and HoursTable (lines 323-324), and CTAVariant (line 190) use PascalCase while all sibling keys in their respective objects use camelCase. These keys correspond to source code component/type names (DirectoryGrid type in defaultLayoutData.ts, CTAVariant type in atoms/cta.tsx, HoursStatusAtom/HoursTableAtom in LocatorResultCard.tsx), suggesting the naming is intentional to match source identifiers. Verify that this convention is documented or enforced for future locale additions to maintain consistency in the file.

Also, ctaVariant (line 189) and CTAVariant (line 190) coexist with slightly different translation values ("CTA -Variante" vs "CTA-Variante") — they appear to serve different purposes (field label vs type name), which is fine, but be aware both exist in the file.

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

68-68: PascalCase keys introduced within otherwise camelCase objects.

New keys like DirectoryGrid, Video, HoursStatus, and HoursTable use PascalCase while siblings use camelCase (e.g., directory, videoSection, hoursTable). This is likely intentional (matching component/enum names in code), but the mix could cause confusion—especially "hoursTable" (line 96) vs "HoursTable" (line 324) which live in different nested objects but look like near-duplicates.

Also applies to: 124-124, 323-324

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

68-68: Inconsistent key casing: PascalCase keys (DirectoryGrid, Video) among camelCase siblings.

These likely mirror React component names, but they break the naming convention used by every other key in the components object. The same pattern appears in fields.options with HoursStatus/HoursTable (Lines 323–324). If this is intentional for the new i18next-cli extraction, no action needed—just flagging for awareness.

Also applies to: 124-124

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

68-68: PascalCase keys DirectoryGrid and Video break the camelCase convention used by all surrounding keys.

Every other key in the components object uses camelCase (e.g., aboutSection, bannerSection, hoursStatus). These two PascalCase keys stand out and may confuse future contributors or cause lookup misses if code elsewhere expects camelCase.

If i18next-cli is extracting these from PascalCase component names, consider adding a key transformation in the extraction config to normalize to camelCase.

Also applies to: 124-124

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

68-68: PascalCase keys DirectoryGrid and Video break the components naming convention.

Every other key in the components object uses camelCase (e.g., directory, videoSection, aboutSection). These two PascalCase keys appear to come from the i18next-cli extracting component names verbatim. If this is intentional, consider documenting the convention; otherwise, align them with the existing camelCase pattern.

Also applies to: 124-124

packages/visual-editor/scripts/checkInterpolationVariables.mjs (1)

22-101: Utility functions duplicated from jsonUtils.ts.

loadJsonSafe, saveJson, flatten, unflatten, sortObject, and getLocales mirror their TypeScript counterparts in src/utils/i18n/jsonUtils.ts. This is understandable since the .mjs script runs with node directly and can't import .ts files without a build step, but it creates a maintenance surface — changes to one copy must be mirrored in the other.

Consider running this script via tsx (like propagatePlatformToComponents.ts) to share the implementations, or add a comment noting the dependency.

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

254-282: Unbounded parallelism — all keys translated concurrently per locale.

Promise.allSettled fires every translateText call in parallel with no concurrency cap. For locales with many missing keys, this can trigger Google's rate limiter and cause most requests to fail.

Consider batching with a concurrency limit (e.g., via a simple semaphore or p-limit).

Example with p-limit
+import pLimit from "p-limit";
+
+const limit = pLimit(5);
+
     await Promise.allSettled(
       keysToTranslate.map(async (key) => {
+        return limit(async () => {
           const english = defaultJson[key];
           // ... translation logic ...
+        });
       })
     );

45-56: Misleading error when --type flag is present but value is missing.

If --type is the last argv element, process.argv[index + 1] is undefined, producing the error Unsupported --type "undefined". A clearer check would distinguish "missing value" from "wrong value."

Suggested improvement
 const getTypeArg = (): TranslationType => {
   const index = process.argv.findIndex((arg) => arg === "--type");
-  const raw = index >= 0 ? process.argv[index + 1] : "platform";
+  if (index < 0) return "platform";
+
+  const raw = process.argv[index + 1];
+  if (!raw || raw.startsWith("-")) {
+    throw new Error(`--type requires a value. This script only supports "--type platform".`);
+  }

   if (raw !== "platform") {
packages/visual-editor/src/utils/i18n/jsonUtils.ts (1)

46-61: flatten coerces non-object leaf values to String — arrays become comma-joined strings.

String([1, 2]) produces "1,2", which is lossy. This is fine for i18n string values but worth noting if the JSON ever contains non-string leaves beyond null/undefined.

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

68-68: PascalCase keys (DirectoryGrid, Video) break the camelCase convention used by sibling entries.

Under components, all other keys use camelCase (e.g., aboutSection, videoSection). DirectoryGrid and Video use PascalCase, presumably matching React component names. This is fine if intentional but consider aligning the convention for consistency across the i18n surface.

Also applies to: 124-124

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

Note

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

Caution

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

⚠️ Outside diff range comments (3)
packages/visual-editor/locales/platform/cs/visual-editor.json (1)

28-28: ⚠️ Potential issue | 🟡 Minor

Same mistranslation: "breadcrumb": "Strouha" — see components file comment.

Same issue as in the components locale file. "Strouha" is not the correct Czech for navigational "breadcrumb." Line 60 (components.breadcrumbs) correctly uses "Strouhanka."

Proposed fix
-  "breadcrumb": "Strouha",
+  "breadcrumb": "Drobečková navigace",
packages/visual-editor/locales/platform/lv/visual-editor.json (1)

28-28: ⚠️ Potential issue | 🟡 Minor

"breadcrumb" translated as "Rīvmaize" (food breadcrumbs) — likely a machine-translation error.

"Rīvmaize" is the Latvian word for culinary breadcrumbs. For the UI navigation concept, the English loanword "Breadcrumb" (or a descriptive term like "Navigācijas ceļš") is more appropriate. The same issue appears at line 60 (components.breadcrumbs).

Proposed fix
-  "breadcrumb": "Rīvmaize",
+  "breadcrumb": "Navigācijas ceļš",

And at line 60:

-    "breadcrumbs": "Navigācijas ceļš",
+    "breadcrumbs": "Navigācijas ceļi",
packages/visual-editor/locales/platform/hr/visual-editor.json (1)

461-491: ⚠️ Potential issue | 🟡 Minor

Incorrect Croatian _few plural forms for unit nouns.

Croatian uses distinct forms for counts 2–4 (_few). Currently both _few and _other are identical, which produces incorrect grammar:

Key Current _few Correct _few
kilometer_few (Line 462) "kilometara" "kilometra"
mile_few (Line 490) "milja" "milje"

Croatian counting: 2 kilometra (gen. sg.), 5 kilometara (gen. pl.); 2 milje (nom. pl.), 5 milja (gen. pl.).

Proposed fix
   "kilometer_one": "kilometar",
-  "kilometer_few": "kilometara",
+  "kilometer_few": "kilometra",
   "kilometer_other": "kilometara",
   "mile_one": "milja",
-  "mile_few": "milja",
+  "mile_few": "milje",
   "mile_other": "milja",
🤖 Fix all issues with AI agents
In `@packages/visual-editor/locales/components/fi/visual-editor.json`:
- Line 37: Update the Finnish translation for the JSON key "informationSection"
in visual-editor.json: replace the incorrect value "Tiedonsiirto" with the
correct term "Tieto-osio" so the label reads appropriately for Finnish users;
locate the "informationSection" key in the file and change its string value
accordingly.

In `@packages/visual-editor/locales/components/fr/visual-editor.json`:
- Around line 54-56: The translation key "mile_many" in the locale file is
incorrect ("kilomètres"); update the value for "mile_many" to the correct French
word "miles" so it matches "mile_one" and "mile_other" and avoids showing
kilometers where miles are intended; locate the key "mile_many" in the
visual-editor.json and replace its string accordingly.

In `@packages/visual-editor/locales/components/hr/visual-editor.json`:
- Line 38: Update the Croatian `_few` plural variants to use the nominative
plural (counts 2–4) instead of the genitive plural; specifically change the keys
locationWithCount_few, locationsNear_few, locationsWithinDistanceOf_few,
mile_few, totalReviews_few and the kilometer_few entry to their correct
nominative-plural forms (e.g., "{{count}} lokacije" for locationWithCount_few,
"{{count}} lokacije u blizini…" for locationsNear_few, "{{count}} lokacije
unutar…" for locationsWithinDistanceOf_few, "milje" for mile_few, "{{count}}
recenzije" for totalReviews_few, and use "kilometra" for kilometer_few where
appropriate); verify other mentioned keys (lines 43,46,49,54,96) and replace
their `_few` values similarly with the nominative plural endings (-ije/-je for
feminine -ija/-ja nouns and -a for neuter/masculine as appropriate).

In `@packages/visual-editor/locales/components/it/visual-editor.json`:
- Line 9: The Italian translation for the key "breadcrumb" is incorrect ("Pane"
means bread); update the value for the "breadcrumb" key in the visual-editor
locale to a proper navigation term such as "Breadcrumb", "Percorso di
navigazione", or simply "Percorso" so it reflects the UI concept of navigation
breadcrumbs rather than the food item.

In `@packages/visual-editor/locales/platform/fr/visual-editor.json`:
- Around line 490-491: The "mile_many" translation is incorrect ("kilomètres");
update the JSON entry for the key "mile_many" to use the correct French word
"miles" to match the other plural forms (see "mile_one" and "mile_other"); edit
the value for "mile_many" in the visual-editor localization JSON so it equals
"miles" and run any i18n/locale validations or tests to ensure no formatting
errors.

In `@packages/visual-editor/scripts/generateTranslations.ts`:
- Around line 334-361: The current Promise.allSettled over translationTargets
fires all translateText calls in parallel causing rate-limits; change this to a
bounded-concurrency loop or use a limiter like p-limit to run at most N (e.g.,
5) translations concurrently. Keep the per-item logic
(maskInterpolationVariables, embedContextInText, removeEmbeddedContext,
unmaskInterpolationVariables), preserve updating cache.set(key.trim(),
translated), successCount/failCount increments, and the console logs/error
handling, but schedule translateText(...) through the limiter (or process
translationTargets in async batches) so translateText, cache updates and
counters occur inside the limited task function. Ensure Promise.allSettled
awaits the queued limited tasks so the script still waits for all translations
to finish.
🟡 Minor comments (47)
packages/visual-editor/locales/components/pl/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Typo: "Breatcrumb" → "Breadcrumb".

Missing the letter 'd'. This will be user-visible in the UI.

✏️ Proposed fix
-  "breadcrumb": "Breatcrumb",
+  "breadcrumb": "Breadcrumb",
packages/visual-editor/locales/components/es/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

"Migaja de pan" is an unusual choice for breadcrumb navigation.

The literal translation "Migaja de pan" (a single bread crumb/morsel) is not the standard UI term in Spanish. The common terms are "Migas de pan" (plural) or, more idiomatically for web navigation, "Ruta de navegación." Consider aligning with typical Spanish UI conventions.

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

46-48: ⚠️ Potential issue | 🟡 Minor

Inconsistent word order in locationsNear_many compared to sibling plural forms.

The _many variant places the count after the noun ("Ubicaciones {{count}} cerca de..."), while _one and _other place it before ("{{count}} ubicación/ubicaciones cerca de..."). This will produce visually jarring text if the _many form is ever resolved.

Proposed fix
-  "locationsNear_many": "Ubicaciones {{count}} cerca de \"{{name}}\"",
+  "locationsNear_many": "{{count}} ubicaciones cerca de \"{{name}}\"",
packages/visual-editor/locales/components/es/visual-editor.json-96-98 (1)

96-98: ⚠️ Potential issue | 🟡 Minor

Inconsistent term across totalReviews plural forms.

totalReviews_many uses "opiniones" while _one uses "reseña" and _other uses "reseñas". These are different words ("opinions" vs. "reviews"). Plural variants of the same key should use the same term to avoid a jarring switch in vocabulary based on count.

Proposed fix
-  "totalReviews_many": "{{count}} opiniones",
+  "totalReviews_many": "{{count}} reseñas",
packages/visual-editor/locales/components/ro/visual-editor.json-25-25 (1)

25-25: ⚠️ Potential issue | 🟡 Minor

Inconsistent use of definite vs. indefinite article in section labels.

The new section-related keys mix "Secțiune de …" (indefinite) and "Secțiunea …" (definite):

Key Value Article
hoursSection "Secțiune de ore" indefinite
informationSection "Secțiune de informații" indefinite
coreInfoSection "Secțiunea de informații de bază" definite
photoGallerySection "Secțiunea Galerie foto" definite
servicesSection "Secțiunea Servicii" definite

Pick one form consistently. Since these are UI section headings, the definite article ("Secțiunea …") is typically more natural in Romanian.

Proposed fix (align to definite article)
-  "hoursSection": "Secțiune de ore",
+  "hoursSection": "Secțiunea de ore",
-  "informationSection": "Secțiune de informații",
+  "informationSection": "Secțiunea de informații",

Also applies to: 35-37, 65-68, 79-80

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

49-49: ⚠️ Potential issue | 🟡 Minor

Wording in locationsWithinDistanceOf_few diverges from sibling plural forms.

The _few form uses "în {{distance}} {{unit}} din" while _one (line 50) uses "la {{distance}} {{unit}} de" and _other (line 51) uses "pe o rază de {{distance}} {{unit}} de". While some variation across plural forms can be natural, the preposition combination "în…din" vs "la…de" / "pe o rază de…de" feels inconsistent and could confuse users. Consider aligning the phrasing across plural variants.

packages/visual-editor/locales/components/ro/visual-editor.json-20-24 (1)

20-24: ⚠️ Potential issue | 🟡 Minor

Utility image alt text — translation is acceptable.

"Imagine utilitar {{number}}" conveys "Utility image {{number}}". Grammatically, "utilitară" (feminine adjective to agree with "imagine") would be more correct than "utilitar" (masculine/neuter), but this is a minor grammatical nit.

Optional grammar fix
-      "defaultAlt": "Imagine utilitar {{number}}"
+      "defaultAlt": "Imagine utilitară {{number}}"
packages/visual-editor/locales/components/lt/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Likely incorrect machine translation for "breadcrumb".

"Komprumbas" does not appear to be a valid Lithuanian word. The literal translation for "breadcrumb" would be "Duonos trupinys" (or "trupiniai" for plural). For a UI navigation context, it's common to use "Naršymo kelias" (navigation path) or simply leave it as the loanword "Breadcrumb." This looks like a Google Translate artifact from the new automated translation pipeline.

Suggested fix
-  "breadcrumb": "Duonos komprumbas",
+  "breadcrumb": "Naršymo kelias",
packages/visual-editor/locales/components/it/visual-editor.json-36-36 (1)

36-36: ⚠️ Potential issue | 🟡 Minor

Incorrect Italian word order for "hoursSection".

"Ore sezione" reverses the expected Italian noun–modifier order. Other section keys in this file follow the correct pattern — e.g., "servicesSection": "Sezione Servizi" (Line 80) and "coreInfoSection": "Sezione Informazioni fondamentali" (Line 25).

Proposed fix
-  "hoursSection": "Ore sezione",
+  "hoursSection": "Sezione orari",
packages/visual-editor/locales/components/it/visual-editor.json-43-51 (1)

43-51: ⚠️ Potential issue | 🟡 Minor

Inconsistent terminology across plural forms for location-related keys.

The _many and _other plural variants use different Italian words for the same concept:

Key _many _other
locationWithCount "posizioni" "località"
locationsNear "luoghi" "località"
locationsWithinDistanceOf "posizioni" "località"

In Italian, both _many and _other are plural forms (CLDR rules), so they should use consistent vocabulary. This looks like a machine translation artifact. Pick one term (e.g., "località" or "posizioni") and use it consistently across all plural variants.

packages/visual-editor/locales/platform/nb/visual-editor.json-445-446 (1)

445-446: ⚠️ Potential issue | 🟡 Minor

"Misligholde" is a mistranslation of "default" in a UI context.

"Misligholde" means "to default on a debt/obligation" (i.e., fail to pay). For a UI label meaning "the preset/standard option," the correct Norwegian term is "Standard". This same mistranslation appears elsewhere in the file (lines 140, 273, 563) but those are pre-existing; this changed line perpetuates the error.

Proposed fix
-  "fontSizeDefaultLabel": "Misligholde",
+  "fontSizeDefaultLabel": "Standard",
packages/visual-editor/locales/platform/nb/visual-editor.json-238-243 (1)

238-243: ⚠️ Potential issue | 🟡 Minor

Inconsistent translation of "link" prefix across sibling keys.

linkLabel → "Koblingsetikett" and linkType → "Koblingstype" both use the Norwegian root "Kobling", but linkTarget on Line 242 uses the English loanword "Link" → "Linkmål". For consistency, this should also use "Kobling".

Proposed fix
-    "linkTarget": "Linkmål",
+    "linkTarget": "Koblingsmål",
packages/visual-editor/locales/components/zh-TW/visual-editor.json-25-25 (1)

25-25: ⚠️ Potential issue | 🟡 Minor

New keys use Simplified Chinese–style "信息" instead of Traditional Chinese "資訊".

Lines 25 and 37 use "信息" (common in zh-CN) where zh-TW convention prefers "資訊." While the existing unchanged line 10 (businessInformation) already uses "信息," new keys added to this file are an opportunity to use the correct regional variant.

Proposed fix
-  "coreInfoSection": "核心信息部分",
+  "coreInfoSection": "核心資訊部分",
-  "informationSection": "信息部分",
+  "informationSection": "資訊部分",

Also applies to: 37-37

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

35-36: ⚠️ Potential issue | 🟡 Minor

"hours" is translated as the time unit ("小時") instead of business/operating hours ("營業時間").

In the context of a location page, "hours" refers to business hours (營業時間), not the time unit (小時). The same issue applies to "hoursSection" → should be "營業時間部分" rather than "小時部分". The AI summary mentions Google Translate integration for automated translation, which likely caused this mistranslation.

Proposed fix
-  "hours": "小時",
-  "hoursSection": "小時部分",
+  "hours": "營業時間",
+  "hoursSection": "營業時間部分",
packages/visual-editor/locales/components/sk/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Likely mistranslation: "Strúhanka" is the culinary term for breadcrumbs, not the UI navigation concept.

"Strúhanka" refers to food-coating breadcrumbs. For breadcrumb navigation in Slovak, a more appropriate translation would be something like "Navigačná cesta" or simply keeping it as "Breadcrumb," which is commonly used in Slovak tech/UI contexts. This looks like a machine-translation artifact.

packages/visual-editor/locales/components/fr/visual-editor.json-49-51 (1)

49-51: ⚠️ Potential issue | 🟡 Minor

Inconsistent quotation style and phrasing across plural variants.

locationsWithinDistanceOf_many uses French guillemets « » and the preposition "dans", while _one uses \"\" with "à" and _other uses \"\" with "dans un rayon de". Plural variants should only differ in grammatical number, not in punctuation style or phrasing — users will see different formatting depending on the count.

Consider aligning all three variants to use the same quote style (preferably « » for proper French typography) and consistent phrasing.

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

44-45: ⚠️ Potential issue | 🟡 Minor

Inconsistent wording between locationsNear_one and locationsNear_other.

_one uses "位置" while _other uses "地点" for "location(s)." These are synonyms, but the inconsistency suggests the translations weren't reviewed for uniformity. Since _one is never resolved for zh (per CLDR), only the _other form matters here—but consider aligning them if these files are maintained by hand or used as a reference.

packages/visual-editor/locales/platform/zh/visual-editor.json-476-477 (1)

476-477: ⚠️ Potential issue | 🟡 Minor

Inconsistent wording between locationsNear_one and locationsNear_other.

_one uses "位置" (location) while _other uses "地点" (place). Since Chinese has no grammatical plural distinction, these two forms will both be displayed and should use identical wording.

Proposed fix
  "locationsNear_one": ""{{name}}"附近有 {{count}} 个位置",
- "locationsNear_other": ""{{name}}"附近有 {{count}} 个地点",
+ "locationsNear_other": ""{{name}}"附近有 {{count}} 个位置",
packages/visual-editor/locales/components/lv/visual-editor.json-80-80 (1)

80-80: ⚠️ Potential issue | 🟡 Minor

Inconsistent "section" translation: nodaļa vs sadaļa.

servicesSection uses "nodaļa" (department), while all other *Section keys in this file use "sadaļa" (section): hoursSection"Stundu sadaļa", informationSection"Informācijas sadaļa", photoGallerySection"Foto galerijas sadaļa". Use "sadaļa" for consistency.

Suggested fix
-  "servicesSection": "Pakalpojumu nodaļa",
+  "servicesSection": "Pakalpojumu sadaļa",
packages/visual-editor/locales/components/lv/visual-editor.json-35-36 (1)

35-36: ⚠️ Potential issue | 🟡 Minor

hours translated as "Laiks" (time) — likely should be "Stundas" or "Darba laiks".

"Laiks" means "time" in general, not "hours." This is a business-hours context, and inconsistently, hoursSection on the next line uses "Stundu sadaļa" (hours section). Consider "Darba laiks" (business hours) or "Stundas" for consistency.

Suggested fix
-  "hours": "Laiks",
+  "hours": "Darba laiks",
packages/visual-editor/locales/components/lv/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

"breadcrumb" translated as food item rather than navigation concept.

"Rīvmaize" is the literal Latvian word for breadcrumbs (food). In a UI navigation context, this may confuse users. Latvian web conventions typically use "Navigācijas ceļš" (navigation path) or sometimes retain "Breadcrumb" as a loanword. Please verify with a native speaker or Latvian UI localization reference.

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

67-68: ⚠️ Potential issue | 🟡 Minor

Translation quality issues: redundancy and untranslated value.

Two problems in these adjacent keys:

  1. Line 67"Reklāmas reklāmkarogs" is redundant; "reklāmkarogs" already means "advertising banner." Consider just "Reklāmkarogs" or "Akciju reklāmkarogs" (promo banner).
  2. Line 68"Promo Media" is left entirely in English. All other keys in this file are translated to Latvian. This looks like a machine-translation miss.
Suggested fix
-  "promoBanner": "Reklāmas reklāmkarogs",
-  "promoMedia": "Promo Media",
+  "promoBanner": "Akciju reklāmkarogs",
+  "promoMedia": "Reklāmas mediji",
packages/visual-editor/locales/platform/ro/visual-editor.json-68-68 (1)

68-68: ⚠️ Potential issue | 🟡 Minor

Mixed-language translation: "Director Grid".

"Grid" is left in English while "Director" is Romanian. Elsewhere in this file, "grid" is translated — e.g., line 89: "gridSection": "Secțiune Grila". Consider using "Grilă Director" for consistency.

Proposed fix
-    "directoryGrid": "Director Grid",
+    "directoryGrid": "Grilă Director",
packages/visual-editor/locales/platform/fi/visual-editor.json-124-124 (1)

124-124: ⚠️ Potential issue | 🟡 Minor

Inconsistent key casing: "Video" should be "video".

All other keys in the components object use camelCase (e.g., videoSection, directoryGrid, testimonialCard). "Video" with an uppercase V breaks this convention and may cause lookup failures if consuming code expects "video".

Proposed fix
-    "Video": "Video",
+    "video": "Video",
packages/visual-editor/locales/platform/fi/visual-editor.json-445-446 (1)

445-446: ⚠️ Potential issue | 🟡 Minor

Mistranslation: "Laiminlyönti" means "neglect", not "default".

"Laiminlyönti" is the Finnish word for "neglect/default on an obligation" (legal/financial sense). The correct translation for a UI default value label is "Oletus" — which is already correctly used elsewhere in this file (e.g., Line 140: "default": "Oletus"). This looks like a Google Translate error confusing the two English meanings of "default".

The same mistranslation also exists in unchanged lines (273, 563) — worth fixing those too.

Proposed fix
- "fontSizeDefaultLabel": "Laiminlyönti",
+ "fontSizeDefaultLabel": "Oletus",
packages/visual-editor/locales/components/fi/visual-editor.json-75-75 (1)

75-75: ⚠️ Potential issue | 🟡 Minor

"Palvelusosasto" means "service department" (organizational), not a UI "services section".

In Finnish, "osasto" refers to a department (e.g., hospital ward, store department), while "osio" is the correct term for a section of a page or UI. Consider "Palvelut-osio".

🌐 Proposed fix
-  "servicesSection": "Palvelusosasto",
+  "servicesSection": "Palvelut-osio",
packages/visual-editor/locales/components/fi/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Incorrect Finnish UI term for "breadcrumb".

"Leivänmuru" is a literal translation meaning a physical bread crumb. The standard Finnish UI term for breadcrumb navigation is "murupolku" (crumb path).

🌐 Proposed fix
-  "breadcrumb": "Leivänmuru",
+  "breadcrumb": "Murupolku",
packages/visual-editor/locales/components/fi/visual-editor.json-63-63 (1)

63-63: ⚠️ Potential issue | 🟡 Minor

"Promootio" translates to "Promotion", missing the "media" aspect of promoMedia.

Consider "Promomedia" to retain both concepts.

🌐 Proposed fix
-  "promoMedia": "Promootio",
+  "promoMedia": "Promomedia",
packages/visual-editor/locales/components/fi/visual-editor.json-25-25 (1)

25-25: ⚠️ Potential issue | 🟡 Minor

Spurious space before hyphen in compound word.

Finnish compound words with a hyphen don't have a space before the hyphen. "Ydintiedot -osio" → "Ydintiedot-osio".

🌐 Proposed fix
-  "coreInfoSection": "Ydintiedot -osio",
+  "coreInfoSection": "Ydintiedot-osio",
packages/visual-editor/locales/components/cs/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Mistranslation: "breadcrumb": "Strouha" is incorrect Czech.

"Strouha" doesn't mean "breadcrumb" in the navigational sense — it appears to be a truncated/garbled machine translation. The platform file (Line 60 in components) correctly uses "Strouhanka" for "breadcrumbs". For the singular key, consider using "Drobečková navigace" (the standard Czech UI term) or at minimum "Strouhanka" to stay consistent with the plural form.

Proposed fix
-  "breadcrumb": "Strouha",
+  "breadcrumb": "Drobečková navigace",
packages/visual-editor/locales/components/hr/visual-editor.json-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Questionable Croatian translation for "breadcrumb".

"Krušnica" does not appear to be a standard Croatian term for breadcrumb navigation. In Croatian web UI, the breadcrumb trail is commonly referred to as "navigacijski put" (navigation path), "breadcrumb" (kept as-is), or "putanja" (path). This looks like it may be a machine-translation artifact. Please verify with a native Croatian speaker.

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

35-37: ⚠️ Potential issue | 🟡 Minor

Translation quality issues: incorrect case and incomplete translations.

Three concerns here:

  1. "hours": "Sate" — "Sate" is the accusative form. For a standalone UI label, the nominative "Sati" is expected.
  2. "hoursSection": "Odjeljak" and "informationSection": "Odjeljak" — These translate to just "Section" with no qualifier, unlike other similar keys in this file (e.g., "servicesSection": "Odjeljak za usluge", "photoGallerySection": "Odjeljak fotogalerije"). These should include context, e.g., "Odjeljak radnog vremena" and "Odjeljak informacija".

This is consistent with the PR description mentioning Google Translate integration in the new translate script — these appear to be machine-translated without post-editing.

Suggested fix
-  "hours": "Sate",
-  "hoursSection": "Odjeljak",
-  "informationSection": "Odjeljak",
+  "hours": "Sati",
+  "hoursSection": "Odjeljak radnog vremena",
+  "informationSection": "Odjeljak informacija",
packages/visual-editor/locales/platform/da/visual-editor.json-445-446 (1)

445-446: ⚠️ Potential issue | 🟡 Minor

"Misligholdelse" is a mistranslation of "default" in UI context.

"Misligholdelse" means "default" in the legal/financial sense (breach, non-compliance). The correct Danish UI term for a preset/standard value is "Standard". This same mistranslation also appears at lines 140, 273, and 563 — likely a systematic machine-translation error.

Proposed fix (this line + pre-existing occurrences)
-  "fontSizeDefaultLabel": "Misligholdelse",
+  "fontSizeDefaultLabel": "Standard",

Also fix pre-existing occurrences outside the changed lines:

Line 140: "default": "Standard"
Line 273: "default": "Standard"  
Line 563: "spacingDefaultLabel": "Standard"
packages/visual-editor/locales/platform/es/visual-editor.json-645-646 (1)

645-646: ⚠️ Potential issue | 🟡 Minor

Inconsistent translation: "opiniones" vs "reseñas" for reviews.

Line 645 (_many) uses "opiniones" (opinions) while line 646 (_other) uses "reseñas" (reviews). Although _many is never selected for Spanish, these should use the same term for consistency. "Reseñas" is the correct translation for "reviews" in this context.

Proposed fix
-  "totalReviews_many": "{{count}} opiniones",
+  "totalReviews_many": "{{count}} reseñas",
packages/visual-editor/locales/platform/es/visual-editor.json-124-124 (1)

124-124: ⚠️ Potential issue | 🟡 Minor

Inconsistent key casing: "Video" should be "video".

The key uses PascalCase while all sibling keys in the components object use camelCase (e.g., timestamp, videoSection, hoursStatus). Other "video" keys elsewhere in the file (lines 366, 431) also use lowercase. This inconsistency exists in the English source locale as well, so both locales should be corrected to use "video" for consistency.

packages/visual-editor/locales/platform/sk/visual-editor.json-478-481 (1)

478-481: ⚠️ Potential issue | 🟡 Minor

Inconsistent terminology in locationsNear plural forms.

The _one form uses "umiestnenie" (placement/positioning) while the _few/_many/_other forms use "miest" (from "miesto" = place/location). These should share the same root word, consistent with how locationWithCount was corrected.

Proposed fix
-  "locationsNear_one": "{{count}} umiestnenie blízko „{{name}}"",
+  "locationsNear_one": "{{count}} miesto blízko „{{name}}"",
packages/visual-editor/locales/platform/tr/visual-editor.json-480-481 (1)

480-481: ⚠️ Potential issue | 🟡 Minor

Turkish pluralization: nouns after numerals should not take the plural suffix.

In Turkish, when a noun is preceded by a number, it remains singular (e.g., "5 konum", not "5 konumlar"). The _other form should use the singular noun, same as _one. Additionally, _one capitalizes "Konum" while _other lowercases "konumlar" — these should be consistent.

Proposed fix
-  "locationWithCount_one": "{{count}} Konum",
-  "locationWithCount_other": "{{count}} konumlar",
+  "locationWithCount_one": "{{count}} konum",
+  "locationWithCount_other": "{{count}} konum",
packages/visual-editor/locales/platform/fr/visual-editor.json-480-482 (1)

480-482: ⚠️ Potential issue | 🟡 Minor

Inconsistent quoting style across locationsWithinDistanceOf plural variants.

Line 480 (_one) and line 482 (_other) wrap {{name}} in escaped double-quotes (\"{{name}}\"), but the new _many variant on line 481 uses French guillemets (« {{name}} »). This will produce visually different output depending on the count.

Proposed fix — align with sibling entries
-    "locationsWithinDistanceOf_many": "{{count}} emplacements dans {{distance}} {{unit}} de « {{name}} »",
+    "locationsWithinDistanceOf_many": "{{count}} emplacements dans {{distance}} {{unit}} de \"{{name}}\"",
packages/visual-editor/locales/platform/hu/visual-editor.json-445-446 (1)

445-446: ⚠️ Potential issue | 🟡 Minor

fontSize is mistranslated; fontSizeDefaultLabel uses a misleading word for "Default".

  • Line 445: "Betűkészlet" means "font family / font set" in Hungarian, not "font size." The correct translation is "Betűméret". The same mistranslation appears at Line 608 (theme.fontSize), which was not changed in this PR but carries the same error.
  • Line 446: "Mulasztás" literally means "negligence / omission." While it can loosely be used for "default" in legal contexts, the standard Hungarian UI term is "Alapértelmezett" (already used at Line 140). The same issue exists at Line 273 (options.default) and Line 563 (spacingDefaultLabel).
Proposed fix
-  "fontSize": "Betűkészlet",
-  "fontSizeDefaultLabel": "Mulasztás",
+  "fontSize": "Betűméret",
+  "fontSizeDefaultLabel": "Alapértelmezett",
packages/visual-editor/locales/platform/hu/visual-editor.json-124-124 (1)

124-124: ⚠️ Potential issue | 🟡 Minor

Change "Video" to "video" to match the camelCase/lowercase convention.

All other single-word component keys in this section use lowercase (address, phone, team, testimonial, timestamp), while compound keys use camelCase (aboutSection, videoSection). "Video" is the only single-word key using PascalCase and should be lowercase "video" for consistency.

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

205-205: ⚠️ Potential issue | 🟡 Minor

Use imperative verb form for "expandFooter".

Per the established Latvian translation convention, UI action labels should use the imperative form to match the English intent. "Paplašināt kājeni" is the infinitive; the imperative would be "Paplašiniet kājeni". Based on learnings, in Latvian translations for the visual editor, prefer imperative verb forms that align with the English UI (e.g., use 'Aizpildiet konteineru' for 'Fill Container').

Proposed fix
-    "expandFooter": "Paplašināt kājeni",
+    "expandFooter": "Paplašiniet kājeni",
packages/visual-editor/locales/platform/hr/visual-editor.json-124-125 (1)

124-125: ⚠️ Potential issue | 🟡 Minor

Inconsistent key casing: "Video" should be "video".

All sibling keys under components use camelCase (directoryGrid, bodyText, videoSection, etc.), but this key starts with an uppercase V. If the code references components.video (lowercase), the translation will silently miss.

Proposed fix
-    "Video": "Video",
+    "video": "Video",
packages/visual-editor/locales/platform/hr/visual-editor.json-644-646 (1)

644-646: ⚠️ Potential issue | 🟡 Minor

Inconsistent word and incorrect _few form for totalReviews.

Two issues:

  1. Word mismatch: _one uses "pregled" while _few/_other use "recenzija" — these should use the same word across all plural forms.
  2. _few grammar: if using "recenzija" (feminine), counts 2–4 require "recenzije", not "recenzija".
Proposed fix (using "recenzija" consistently)
-  "totalReviews_one": "{{count}} pregled",
-  "totalReviews_few": "{{count}} recenzija",
+  "totalReviews_one": "{{count}} recenzija",
+  "totalReviews_few": "{{count}} recenzije",
   "totalReviews_other": "{{count}} recenzija",
packages/visual-editor/locales/platform/hr/visual-editor.json-477-485 (1)

477-485: ⚠️ Potential issue | 🟡 Minor

Croatian _few plural for "lokacija" should be "lokacije".

For feminine noun "lokacija", counts 2–4 require the nominative plural "lokacije". All three _few entries currently duplicate the _other form ("lokacija"), producing incorrect grammar for counts like 2, 3, 4.

Affected keys: locationsNear_few (Line 478), locationsWithinDistanceOf_few (Line 481), locationWithCount_few (Line 484).

Proposed fix
-  "locationsNear_few": "{{count}} lokacija u blizini \"{{name}}\"",
+  "locationsNear_few": "{{count}} lokacije u blizini \"{{name}}\"",
-  "locationsWithinDistanceOf_few": "{{count}} lokacija unutar {{distance}} {{unit}} od \"{{name}}\"",
+  "locationsWithinDistanceOf_few": "{{count}} lokacije unutar {{distance}} {{unit}} od \"{{name}}\"",
-  "locationWithCount_few": "{{count}} lokacija",
+  "locationWithCount_few": "{{count}} lokacije",
packages/visual-editor/scripts/generateTranslations.ts-356-358 (1)

356-358: ⚠️ Potential issue | 🟡 Minor

Inconsistent log prefix — type is missing from the error log.

Success logs on Line 354 use [${type}/${locale}] but the error log on Line 358 uses [${locale}].

Suggested fix
-          console.error(`[${locale}] Failed to translate key "${key}":`, error);
+          console.error(`[${type}/${locale}] Failed to translate key "${key}":`, error);
packages/visual-editor/scripts/generateTranslations.ts-185-187 (1)

185-187: ⚠️ Potential issue | 🟡 Minor

Context markers may survive in translated output if the MT engine alters them.

Google Translate can transliterate or translate the [[context: …]] marker (e.g., [[contexto: dirección]] in Spanish, or bracket substitution in CJK languages). The current regex requires the literal word context and ASCII ]], so mangled markers will leak into the final translation string.

Consider using a more robust sentinel (e.g., an XML-like tag <x-ctx>…</x-ctx> which MT engines tend to pass through) or validate post-translation that no residual markers remain:

💡 Quick fallback: warn when removal is a no-op
 const removeEmbeddedContext = (text: string): string => {
-  return text.replace(/\s*\[\[\s*context:\s*[\s\S]*?\]\]/gi, "").trim();
+  const cleaned = text.replace(/\s*\[\[\s*context:\s*[\s\S]*?\]\]/gi, "").trim();
+  // Warn if markers may have been mangled by MT
+  if (cleaned.includes("[[") || cleaned.includes("]]")) {
+    console.warn(`Possible residual context marker in: "${cleaned}"`);
+  }
+  return cleaned;
 };
packages/visual-editor/locales/platform/it/visual-editor.json-462-463 (1)

462-463: ⚠️ Potential issue | 🟡 Minor

_many plural keys are unused for Italian cardinal plurals.

Italian CLDR rules define cardinal plural categories as one and other only. The _many category applies exclusively to ordinal plurals (for numbers 8, 11, 80, 800) and is not relevant in cardinal contexts. These keys—kilometer_many, locationsNear_many, locationsWithinDistanceOf_many, locationWithCount_many, mile_many, and totalReviews_many—will never be selected by i18next's plural resolver for the it locale and represent dead translations.

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

20-24: Minor katakana spacing inconsistency.

Line 22 uses a half-width space in "ユーティリティ イメージ", while other compound katakana terms elsewhere in this file are written without spaces (e.g., "ヒーローバナー", "ヒーローヘッダー", "フォトギャラリーセクション"). Consider removing the space for consistency.

🌐 Proposed fix
-      "defaultAlt": "ユーティリティ イメージ {{number}}"
+      "defaultAlt": "ユーティリティイメージ {{number}}"
packages/visual-editor/locales/components/pt/visual-editor.json (1)

38-38: _many plural forms are unused for Portuguese.

Portuguese CLDR plural rules only define one and other categories — the _many form is never selected at runtime by i18next. These keys are harmless but effectively dead code. If the new i18next-cli tooling is auto-generating them, it may be worth configuring it to skip _many for Romance languages to keep locale files lean.

Also applies to: 43-43, 46-46, 49-49, 54-54, 96-96

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

38-40: _many plural form is unused in Spanish.

Per CLDR plural rules, Spanish only uses one and other categories — there is no many category. Keys like kilometer_many, locationWithCount_many, mile_many, and totalReviews_many will never be selected by i18next for the es locale. They are harmless but add dead entries. If these were auto-generated by the new i18next-cli tooling across all locales, this is understandable, but worth being aware of.

Also applies to: 43-45, 54-56, 96-98

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

70-73: Minor translation inconsistencies in "promo" keys.

  • promoBanner: "Reklaminė reklamjuostė" is somewhat redundant ("promotional ad-banner").
  • promoMedia: "Promo žiniasklaida" mixes the untranslated English loanword "Promo" with Lithuanian, while promoBanner uses a fully Lithuanian translation.

Consider aligning both — e.g., "Reklaminė medžiaga" or "Reklaminė žiniasklaida" for promoMedia, and simplifying promoBanner to "Reklamjuostė" to avoid redundancy.

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

25-25: Verify coreInfoSection translation.

"Pamatinformācija" means "basic information" — which is a reasonable rendering of "core info." Just noting that other *Section keys include the word "sadaļa" (section) in their value (e.g., "Stundu sadaļa", "Informācijas sadaļa"). If this key is meant to label a section header consistently with those, consider "Pamatinformācijas sadaļa".

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

238-243: Finnish grammar: "Linkki kohde" should be a compound or genitive form.

Line 243 correctly uses a compound word ("Linkkityyppi"), and Line 238 correctly uses the genitive ("Linkin etiketti"). Line 242's "Linkki kohde" (two bare nouns) is not grammatical Finnish — it should be "Linkkikohde" (compound) or "Linkin kohde" (genitive) to match the pattern.

Proposed fix
- "linkTarget": "Linkki kohde",
+ "linkTarget": "Linkin kohde",
packages/visual-editor/locales/components/fi/visual-editor.json (2)

61-61: Inconsistent "section" suffix across keys.

photoGallerySection uses "osa" (part) — "Valokuvagalleriaosa" — while other section keys like hoursSection ("Tuntien osio") and the corrected servicesSection / coreInfoSection use "osio". Consider using "osio" consistently: "Valokuvagalleria-osio".


1-97: Consider a native-speaker review pass on machine-translated strings.

Multiple translations in this file exhibit patterns typical of automated translation (literal translations, incorrect compound words, semantic drift). The informationSection → "Tiedonsiirto" error is the most critical, but several others (breadcrumb, servicesSection, promoMedia, coreInfoSection spacing) also need correction. If these translations were generated via the new Google Translate integration, a native Finnish speaker review pass would catch these before they reach users.

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

322-323: Inconsistent capitalization: "Timer Status" vs "Timer tabel".

Danish doesn't use title case — only the first word is capitalized. Line 323 is correct ("Timer tabel"), but line 322 should follow the same convention.

Proposed fix
-      "hoursStatus": "Timer Status",
+      "hoursStatus": "Timer status",
packages/visual-editor/locales/platform/es/visual-editor.json (1)

462-462: _many plural forms are unused for Spanish.

Spanish (per CLDR/Unicode) only has two plural categories: one and other. i18next will never resolve the _many key for the es locale. These entries at lines 462, 478, 481, 484, 490, and 645 are dead weight. If the new i18next-cli is auto-generating them, consider configuring it to only emit plural forms relevant to the target language.

Also applies to: 478-478, 481-481, 483-485, 490-490, 645-645

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

238-243: Minor casing inconsistency across sibling field translations.

linkLabel → "Bağlantı Etiketi", linkTarget → "Bağlantı Hedefi" (both title-cased), but linkType → "Bağlantı türü" (lowercase). Consider aligning to the same convention.

Proposed fix (align to title case)
-    "linkType": "Bağlantı türü",
+    "linkType": "Bağlantı Türü",
packages/visual-editor/locales/platform/ja/visual-editor.json (1)

28-28: Breadcrumb translation corrected to proper UI term.

パンくずリスト is the correct Japanese term for breadcrumb navigation. However, note that on line 60 (unchanged), components.breadcrumbs is still "パン粉" — which literally means "panko" (food breadcrumbs), not a navigation breadcrumb. Consider updating that value in a follow-up to maintain consistency.

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

124-124: Same PascalCase "Video" inconsistency as in the sv locale.

This is the root source locale — the key here propagates to all other platform locale files. Renaming to "video" here will need a corresponding update to any code referencing components.Video and to the propagation/extraction config so this doesn't resurface.

Proposed fix
-    "Video": "Video",
+    "video": "Video",
packages/visual-editor/locales/platform/sv/visual-editor.json (1)

124-124: "Video" is PascalCase while all other keys in the components section use camelCase.

The inconsistency is confirmed: every sibling key uses camelCase (directoryGrid, hoursStatus, videoSection, etc.). For consistency with the rest of the components section, rename this to "video".

Note: A search of the codebase found no references to this key, so it may be unused or orphaned. Renaming will not break existing code, but consider whether it should be retained at all.

Proposed fix
-    "Video": "Video",
+    "video": "Video",
packages/visual-editor/locales/platform/lv/visual-editor.json (1)

1-656: Several other translations appear to be machine-generated with semantic errors.

A few examples beyond the changed lines (but worth a pass during this tooling migration):

  • Line 44: "close": "Tuvs" — "Tuvs" means "near" (proximity), not "close" (to shut). Should be "Aizvērt".
  • Line 47: "closeMenu": "Aizvērt ēdienkarti" — "ēdienkarte" means "food menu", not a navigation menu. Should be "Aizvērt izvēlni".
  • Line 503: "openMenu": "Atvērta ēdienkarte" — same "food menu" issue.
  • Line 635: "uppercase": "Lielie cilvēki" — "Big people" instead of "Lielie burti" (uppercase letters).

These aren't on changed lines so they may pre-date this PR, but since the PR is swapping i18n tooling and touching this file, it may be a good opportunity to run a quality check on the Latvian translations.

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

241-266: No timeout or retry logic on translation requests.

fetch has no timeout, so a hanging request will block the script indefinitely. Additionally, there's no retry for transient network/API failures — a single 429 or 5xx will permanently skip that key.

Consider adding an AbortSignal.timeout() and a simple retry with backoff:

💡 Suggested improvement
 const translateText = async (
   text: string,
-  targetLang: string
+  targetLang: string,
+  retries = 2
 ): Promise<string> => {
   const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${DEFAULT_LANGUAGE}&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`;
-  const response = await fetch(url);
+  const response = await fetch(url, { signal: AbortSignal.timeout(10_000) });
 
   if (!response.ok) {
+    if (retries > 0 && (response.status === 429 || response.status >= 500)) {
+      await new Promise((r) => setTimeout(r, 1000 * (3 - retries)));
+      return translateText(text, targetLang, retries - 1);
+    }
     throw new Error(
       `Google Translate API error: ${response.status} for text: "${text}"`
     );
   }

Also note that translate.googleapis.com with client=gtx is an unofficial, undocumented API that may be rate-limited or discontinued without notice. If this script is part of CI or runs frequently, consider switching to the official Cloud Translation API.

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

462-462: Remove unnecessary _many plural forms from Portuguese locale — they are never used by CLDR rules.

Portuguese only has two CLDR plural categories: one (for 0 and 1) and other (for all other values). The many category does not exist for Portuguese. These keys will never be resolved at runtime, even though i18next silently ignores them.

While not a functional issue, they add noise to the locale file and can confuse translators. Consider checking if the i18next-cli extraction tooling is unconditionally emitting _many forms for all languages regardless of CLDR support.

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

🤖 Fix all issues with AI agents
In `@packages/visual-editor/locales/components/fi/visual-editor.json`:
- Line 63: The translation for the locale key promoMedia currently reads
"Promootio" which omits the "media" meaning; update the value for promoMedia in
the visual-editor JSON to a Finnish string that includes media (e.g.,
"Promootiomedia" or "Markkinointimedia") so the key accurately reflects
promotional media content; ensure you only change the RHS string for the
promoMedia key and keep JSON formatting intact.
- Line 9: Replace the incorrect Finnish string value for the "breadcrumb" key in
the visual editor locale (packages/visual-editor locales file) from "Leivänmuru"
to the established UI term "Murupolku"; also update the same "breadcrumb" key in
the platform locale file where the same incorrect value appears so both locale
entries use "Murupolku" consistently.

In `@packages/visual-editor/locales/components/fr/visual-editor.json`:
- Around line 49-51: The three plural keys locationsWithinDistanceOf_many,
locationsWithinDistanceOf_one and locationsWithinDistanceOf_other use
inconsistent quoting and wording; update all three to the same French phrasing
and quote style (for example: use guillemets « {{name}} » consistently) and
standardize the preposition/phrase (e.g. "{{count}} emplacement(s) à
{{distance}} {{unit}} de « {{name}} »" or "{{count}} emplacements dans un rayon
de {{distance}} {{unit}} de « {{name}} »") so the displayed message is identical
across plural forms.

In `@packages/visual-editor/locales/components/hr/visual-editor.json`:
- Around line 35-37: Update the Croatian translations for the keys "hours",
"hoursSection", and "informationSection": replace the nominative form for
"hours" from "Sate" to "Sati", and make the two section labels unambiguous by
changing "hoursSection" to "Odjeljak radnog vremena" and "informationSection" to
"Odjeljak s informacijama" so each key clearly matches its English meaning.
- Line 9: Replace the incorrect Croatian translation value for the "breadcrumb"
key in visual-editor.json: change "Krušnica" to the correct standard term
"Krušne mrvice" (or the exact string used elsewhere in the codebase for the
component label) so the breadcrumb label is consistent and accurate across
locales.

In `@packages/visual-editor/locales/platform/fi/visual-editor.json`:
- Line 242: The Finnish translation for the JSON key "linkTarget" is
grammatically incorrect; replace the value "Linkki kohde" with a correct form
(use the compound "Linkkikohde" to match the style of "Linkkityyppi") so the
"linkTarget" entry reads the proper Finnish compound word.
- Line 446: The Finnish translation uses "Laiminlyönti" incorrectly for default
settings; update the keys fontSizeDefaultLabel and spacingDefaultLabel to use
"Oletus" instead of "Laiminlyönti", and fix the options.default entry (the JSON
key "default") to "Oletus" as well so all default-setting labels use the correct
Finnish word; locate the entries by their JSON keys (fontSizeDefaultLabel,
spacingDefaultLabel, options.default) and replace the value string with
"Oletus".
- Line 124: The "Video" JSON key uses PascalCase while sibling keys use
camelCase; rename the "Video" key to camelCase (e.g., "video") in this file and
the corresponding English locale so it matches siblings like "videoSection",
"aboutSection", and "address", and update any code references that read the old
"Video" key (search for "Video" in the codebase) to use the new "video" key.

In `@packages/visual-editor/locales/platform/fr/visual-editor.json`:
- Around line 478-482: The translations for the plural key
locationsWithinDistanceOf_many are inconsistent: replace the French guillemets
and the phrasing so it matches the other variants (locationsWithinDistanceOf_one
and locationsWithinDistanceOf_other); specifically, change
locationsWithinDistanceOf_many to use escaped double quotes around {{name}}
(\"{{name}}\") and include the phrase "dans un rayon de {{distance}} {{unit}}
de" so all plural forms use the same wording and quoting style.
- Line 124: The "Video" key in the components object is using PascalCase while
its siblings use camelCase; rename the key "Video" to "video" in the components
object (e.g., in packages/visual-editor/locales/platform/fr/visual-editor.json)
and mirror the same change across the other locale files where the PascalCase
copy exists so the localization keys are consistently camelCase; verify and
update any code or tests that reference the old "Video" key to use "video".

In `@packages/visual-editor/locales/platform/hr/visual-editor.json`:
- Line 462: The locale entry for the key "kilometer_few" is incorrect: replace
its value "kilometara" with the correct Croatian few-form "kilometra" (so
"kilometer_few" = "kilometra") and ensure the _few form now differs from the
genitive plural used in "kilometer_other"/"_other" to match the components file
fix.
- Around line 322-323: The translation for components.hoursTable is inconsistent
and grammatically incorrect ("Sate stol"); update the components.hoursTable
entry to match the correct and consistent translation used by options.hoursTable
("Tablica sati") so both keys (components.hoursTable and options.hoursTable) use
the same phrase and correct grammar.
🧹 Nitpick comments (3)
packages/visual-editor/locales/platform/it/visual-editor.json (1)

462-463: _many plural keys are unused for Italian locale.

Italian's CLDR plural rules only define one and other categories — there is no many category. Keys like kilometer_many, locationsNear_many, locationsWithinDistanceOf_many, locationWithCount_many, mile_many, and totalReviews_many will never be selected by i18next for the it locale. These are likely artifacts of the new i18next-cli extraction/propagation tooling.

Not a bug (they're silently ignored), but they add noise to the locale file. Consider whether the propagation script should be locale-aware for plural categories.

Also applies to: 478-479, 481-482, 483-485, 490-491, 645-646

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

242-242: linkTarget now translated — minor style note.

The past-review concern about this value being English is resolved. The neighboring keys follow a "<noun> veze" pattern ("linkLabel": "Oznaka veze", "linkType": "Vrsta veze"), while linkTarget is just "Odredište". For consistency, "Odredište veze" would align better, but this is a nitpick.


124-124: Update the translation key from "Video" to "video" to match camelCase convention.

The "Video" key is explicitly defined in the Video component (src/components/contentBlocks/Video.tsx, line 53) with uppercase casing, which breaks the camelCase naming convention used throughout the components section (e.g., videoSection, directoryGrid, heroImage, gallery, footer). Change "components.Video" to "components.video" in the component configuration and update all locale files accordingly.

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

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/components/de/visual-editor.json`:
- Around line 18-19: The German locale strings for visual-editor use a hardcoded
weekday "Donnerstag" in the keys closesAtTime and closesAtTimeWeek; update both
values to use a placeholder for the weekday (e.g., replace "Donnerstag" with a
variable like {{dayOfWeek}} or remove the hardcoded day) so the message is
dynamic and matches closesAtTimeWeek's intended interpolation, ensuring the JSON
keys closesAtTime and closesAtTimeWeek contain the correct placeholders.

In `@packages/visual-editor/locales/platform/de/visual-editor.json`:
- Around line 48-49: The German locale strings are hardcoding "Donnerstag";
update the JSON keys "closesAtTime" to remove the hardcoded day so it only
contains the time placeholder, and update "closesAtTimeWeek" to remove
"Donnerstag" and use the existing {{dayOfWeek}} variable instead; apply the same
change in both locales files where these keys appear so the output becomes e.g.
"Schließt um {{time}} Uhr" and "Schließt um {{time}} Uhr {{dayOfWeek}}".
🟡 Minor comments (25)
packages/visual-editor/locales/platform/es/visual-editor.json-644-646 (1)

644-646: ⚠️ Potential issue | 🟡 Minor

Inconsistent word in totalReviews_many vs the other plural forms.

_one uses "reseña" and _other uses "reseñas", but _many uses "opiniones" (opinions). All three should use the same root word for a consistent user experience.

Proposed fix
-  "totalReviews_many": "{{count}} opiniones",
+  "totalReviews_many": "{{count}} reseñas",
packages/visual-editor/locales/platform/es/visual-editor.json-477-479 (1)

477-479: ⚠️ Potential issue | 🟡 Minor

Inconsistent word order in locationsNear_many.

The _one form (line 477) uses "{{count}} ubicación cerca de ..." and the _other form (line 479) uses "{{count}} ubicaciones cerca de ..." — both place {{count}} first. The _many form on line 478 reverses this to "Ubicaciones {{count}} cerca de ...". This looks like a machine-translation artifact.

Proposed fix
-  "locationsNear_many": "Ubicaciones {{count}} cerca de \"{{name}}\"",
+  "locationsNear_many": "{{count}} ubicaciones cerca de \"{{name}}\"",
packages/visual-editor/locales/platform/da/visual-editor.json-446-446 (1)

446-446: ⚠️ Potential issue | 🟡 Minor

Mistranslation: "Misligholdelse" is the wrong sense of "default".

"Misligholdelse" translates to "default" in the legal/financial sense (breach of obligation, failure to pay), not "default" as in "the preset/standard option." The correct Danish term here would be "Standard".

This same mistranslation appears on other (unchanged) lines as well (e.g., "default" on line 140, options.default on line 273, spacingDefaultLabel on line 563), suggesting a systemic machine-translation error for the word "default."

Proposed fix for the changed line
-  "fontSizeDefaultLabel": "Misligholdelse",
+  "fontSizeDefaultLabel": "Standard",
packages/visual-editor/locales/platform/lt/visual-editor.json-238-243 (1)

238-243: ⚠️ Potential issue | 🟡 Minor

Inconsistent translation casing/grammar on linkTarget.

linkLabel and linkType both use the genitive form "Nuorodos" + lowercase noun (e.g., "Nuorodos etiketė", "Nuorodos tipas"), but linkTarget uses nominative "Nuoroda" + capitalized "Tikslas". This looks like a machine-translation artifact.

Suggested fix for consistency
-    "linkTarget": "Nuoroda Tikslas",
+    "linkTarget": "Nuorodos tikslas",
packages/visual-editor/locales/platform/zh/visual-editor.json-476-477 (1)

476-477: ⚠️ Potential issue | 🟡 Minor

Inconsistent wording between locationsNear_one and locationsNear_other.

_one uses "位置" while _other uses "地点". Since Chinese has no grammatical plural distinction, these two forms should be identical (similar to the fix applied to locationWithCount at lines 480–481).

Proposed fix
  "locationsNear_one": ""{{name}}"附近有 {{count}} 个位置",
- "locationsNear_other": ""{{name}}"附近有 {{count}} 个地点",
+ "locationsNear_other": ""{{name}}"附近有 {{count}} 个位置",
packages/visual-editor/locales/platform/fi/visual-editor.json-205-205 (1)

205-205: ⚠️ Potential issue | 🟡 Minor

Verb form: prefer imperative "Laajenna" over infinitive "Laajentaa" for a UI action.

Finnish UI labels for toggle/action fields conventionally use the imperative mood. "Laajentaa alatunnistetta" (infinitive: "to expand the footer") reads awkwardly; "Laajenna alatunniste" (imperative: "Expand footer") is more natural and consistent with standard Finnish UI conventions.

🌐 Proposed fix
-    "expandFooter": "Laajentaa alatunnistetta",
+    "expandFooter": "Laajenna alatunniste",
packages/visual-editor/locales/platform/nl/visual-editor.json-322-323 (1)

322-323: ⚠️ Potential issue | 🟡 Minor

Inconsistent translations with identical keys elsewhere in this file.

  • Line 322: "hoursStatus": "Uurstatus" uses singular "Uur", but components.hoursStatus at line 95 is "Urenstatus" (plural).
  • Line 323: "hoursTable": "Uren tabel" is two words, but components.hoursTable at line 96 is "Urentafel" (one word, different noun).

These refer to the same UI concepts and should use consistent translations. Dutch compound nouns are typically written as a single word.

Proposed fix to align with existing translations
-      "hoursStatus": "Uurstatus",
-      "hoursTable": "Uren tabel",
+      "hoursStatus": "Urenstatus",
+      "hoursTable": "Urentafel",
packages/visual-editor/locales/platform/nl/visual-editor.json-242-242 (1)

242-242: ⚠️ Potential issue | 🟡 Minor

Incorrect Dutch translation: "Doel koppelen" is a verb phrase ("to link a target"), not a noun.

The key linkTarget represents a UI label (noun), but "Doel koppelen" reads as an imperative/infinitive verb phrase. For consistency with "linkLabel": "Linklabel" and "linkType": "Linktype" on adjacent lines, this should be a compound noun.

Proposed fix
-      "linkTarget": "Doel koppelen",
+      "linkTarget": "Linkdoel",
packages/visual-editor/locales/platform/tr/visual-editor.json-480-481 (1)

480-481: ⚠️ Potential issue | 🟡 Minor

Turkish plural form konumlar is incorrect when preceded by a count.

In Turkish, nouns remain singular when preceded by a numeral (e.g., "5 konum", not "5 konumlar"). The _other form should use the same singular noun as _one. This is consistent with how other count-based keys in this file handle Turkish plurals:

  • kilometer_other: "kilometre" (not "kilometreler")
  • mile_other: "mil" (not "miller")
  • locationsNear_other: uses "konum" (not "konumlar")

Also, there's an inconsistent capitalization between "Konum" (line 480) and "konumlar" (line 481).

Proposed fix
-  "locationWithCount_one": "{{count}} Konum",
-  "locationWithCount_other": "{{count}} konumlar",
+  "locationWithCount_one": "{{count}} Konum",
+  "locationWithCount_other": "{{count}} Konum",
packages/visual-editor/locales/platform/hu/visual-editor.json-445-446 (1)

445-446: ⚠️ Potential issue | 🟡 Minor

fontSize mistranslated as "Betűkészlet" (Font family/set) instead of "Betűméret" (Font size).

"Betűkészlet" means "font set" or "font family" in Hungarian, not "font size." The correct translation is "Betűméret". The same error also exists on line 608 under theme.fontSize.

Proposed fix
-  "fontSize": "Betűkészlet",
+  "fontSize": "Betűméret",

And similarly for line 608:

-    "fontSize": "Betűkészlet",
+    "fontSize": "Betűméret",
packages/visual-editor/locales/platform/nb/visual-editor.json-322-323 (1)

322-323: ⚠️ Potential issue | 🟡 Minor

Minor inconsistency: "Timerbord" vs "Timetabell" for hoursTable.

The new entry at line 323 translates hoursTable as "Timetabell", but the existing components.hoursTable at line 96 uses "Timerbord". "Timetabell" is the better translation — consider updating line 96 to match.

Proposed fix

At line 96 (outside the changed range):

-    "hoursTable": "Timerbord",
+    "hoursTable": "Timetabell",
packages/visual-editor/locales/platform/fr/visual-editor.json-653-653 (1)

653-653: ⚠️ Potential issue | 🟡 Minor

videoThumbnail translation is garbled.

"Vide-gigantes" is not a valid French translation for "Video Thumbnail". The correct translation would be "Miniature vidéo" or "Vignette vidéo".

Proposed fix
-  "videoThumbnail": "Vide-gigantes",
+  "videoThumbnail": "Miniature vidéo",
packages/visual-editor/locales/platform/fr/visual-editor.json-28-28 (1)

28-28: ⚠️ Potential issue | 🟡 Minor

Inconsistency: breadcrumb vs breadcrumbs translations.

Line 28 correctly uses "Fil d'Ariane" (the standard French term for navigation breadcrumbs), but the existing components.breadcrumbs key at line 60 uses "Chapelure" — which means food breadcrumbs. These should be consistent; both navigation-related breadcrumb keys should use "Fil d'Ariane".

Proposed fix (line 60)
-    "breadcrumbs": "Chapelure",
+    "breadcrumbs": "Fil d'Ariane",
packages/visual-editor/locales/platform/zh-TW/visual-editor.json-610-610 (1)

610-610: ⚠️ Potential issue | 🟡 Minor

fontWeight translation is a literal calque.

"字體重量" literally means "font mass/weight (physical)". The conventional zh-TW translation for the CSS/typography concept of font weight is "字體粗細" (font thickness), which is widely used in UI/design contexts.

Proposed fix
-      "fontWeight": "字體重量"
+      "fontWeight": "字體粗細"
packages/visual-editor/locales/platform/zh-TW/visual-editor.json-322-323 (1)

322-323: ⚠️ Potential issue | 🟡 Minor

Minor translation inconsistency between hoursStatus and hoursTable.

hoursStatus translates "hours" as "小時" (literal time unit), yielding "小時狀態", while hoursTable uses the more natural "營業時間" (business hours), yielding "營業時間表". In the context of store operating hours, "營業狀態" would be more idiomatic for hoursStatus and consistent with hoursTable.

Proposed fix
-      "hoursStatus": "小時狀態",
+      "hoursStatus": "營業狀態",
       "hoursTable": "營業時間表",
packages/visual-editor/locales/platform/ro/visual-editor.json-68-68 (1)

68-68: ⚠️ Potential issue | 🟡 Minor

"Director Grid" mixes Romanian and English — "Grid" left untranslated.

Line 89 uses "Grila" (Romanian for "Grid") in "gridSection": "Secțiune Grila". For consistency, consider translating this to "Grilă Director" or similar.

Proposed fix
-    "directoryGrid": "Director Grid",
+    "directoryGrid": "Grilă Director",
packages/visual-editor/locales/platform/pl/visual-editor.json-461-464 (1)

461-464: ⚠️ Potential issue | 🟡 Minor

Incorrect Polish plural form for kilometer_many.

"kilometer_many": "kilometry" (line 463) is grammatically wrong. The _many category in Polish covers counts like 0, 5–21, 25–31 …, which require the genitive plural: "kilometrów", not the nominative plural "kilometry". The _other form (line 464, used for fractional counts) should also be "kilometrów" (or "kilometra" for genitive singular, depending on style).

Suffix Example counts Correct form
_one 1 kilometr
_few 2, 3, 4 kilometry
_many 0, 5–21 kilometrów ✗ (currently "kilometry")
_other fractional kilometrów ✗ (currently "kilometry")
Proposed fix
   "kilometer_one": "kilometr",
   "kilometer_few": "kilometry",
-  "kilometer_many": "kilometry",
-  "kilometer_other": "kilometry",
+  "kilometer_many": "kilometrów",
+  "kilometer_other": "kilometrów",
packages/visual-editor/locales/components/fi/visual-editor.json-75-75 (1)

75-75: ⚠️ Potential issue | 🟡 Minor

"Palvelusosasto" means "service department", not "services section".

Two issues: (1) "osasto" means department/ward (organizational unit), while "osio" is the Finnish term for a content section; (2) "palvelus-" has a military/obligatory connotation, whereas "palvelu-" is the standard modifier for services. The translation should be "Palveluosio".

🌐 Proposed fix
-  "servicesSection": "Palvelusosasto",
+  "servicesSection": "Palveluosio",
packages/visual-editor/locales/components/fi/visual-editor.json-25-25 (1)

25-25: ⚠️ Potential issue | 🟡 Minor

Inconsistent spacing in "Ydintiedot -osio".

The space before the hyphen (" -osio") doesn't follow Finnish compound word conventions and is inconsistent with other entries in this file (e.g., "Tieto-osio" on Line 37 uses no space). Should be "Ydintiedot-osio" or "Ydintietojen osio".

🌐 Proposed fix
-  "coreInfoSection": "Ydintiedot -osio",
+  "coreInfoSection": "Ydintiedot-osio",
packages/visual-editor/locales/components/fi/visual-editor.json-35-35 (1)

35-35: ⚠️ Potential issue | 🟡 Minor

"Tuntia" uses partitive case; standalone labels should use nominative "Tunnit".

"Tuntia" is the partitive form (used in expressions like "2 tuntia"). For a standalone UI label meaning "Hours", the nominative plural "Tunnit" is standard in Finnish. If this refers specifically to business/opening hours, "Aukioloajat" would be even more precise.

🌐 Proposed fix
-  "hours": "Tuntia",
+  "hours": "Tunnit",
packages/visual-editor/locales/platform/sk/visual-editor.json-486-489 (1)

486-489: ⚠️ Potential issue | 🟡 Minor

Inconsistent root word across plural forms of locationWithCount.

The _one form uses "lokácia" and _other uses "lokality" (both from the "lokácia" root — correct), but _few and _many use "miest" which derives from "miesto" (place). All forms should use the same root word.

The correct Slovak declensions of "lokácia" are: singular "lokácia", few "lokácie", genitive plural "lokácií".

Proposed fix
   "locationWithCount_one": "{{count}} lokácia",
-  "locationWithCount_few": "{{count}} miest",
-  "locationWithCount_many": "{{count}} miest",
+  "locationWithCount_few": "{{count}} lokácie",
+  "locationWithCount_many": "{{count}} lokácií",
   "locationWithCount_other": "{{count}} lokality",
packages/visual-editor/locales/platform/sk/visual-editor.json-478-478 (1)

478-478: ⚠️ Potential issue | 🟡 Minor

Inconsistent "location" terminology: "umiestnenie" vs "miesto" vs "lokácia".

Line 478 (locationsNear_one) uses "umiestnenie" (placement/positioning), line 482 (locationsWithinDistanceOf_one) uses "miesto" (place), and lines 486-489 (locationWithCount) use "lokácia/lokality." All three keys represent the concept of a physical location and should use a consistent term — ideally "miesto" or "lokácia" throughout.

Proposed fix (using "miesto" to match the _few/_many/_other forms on lines 479-481)
-  "locationsNear_one": "{{count}} umiestnenie blízko „{{name}}"",
+  "locationsNear_one": "{{count}} miesto blízko „{{name}}"",
packages/visual-editor/locales/platform/lv/visual-editor.json-483-485 (1)

483-485: ⚠️ Potential issue | 🟡 Minor

Inconsistent wording in locationWithCount plural group.

_zero and _one use "atrašanās vietas/vieta" (locations), but _other uses just "vietas" (places), dropping "atrašanās". This looks like a machine-translation artifact. For consistency, _other should match:

Proposed fix
   "locationWithCount_zero": "{{count}} atrašanās vietas",
   "locationWithCount_one": "{{count}} atrašanās vieta",
-  "locationWithCount_other": "{{count}} vietas",
+  "locationWithCount_other": "{{count}} atrašanās vietas",
packages/visual-editor/locales/platform/cs/visual-editor.json-486-489 (1)

486-489: ⚠️ Potential issue | 🟡 Minor

Inconsistent word choice across locationWithCount plural forms.

_one and _other use "umístění" (placement/location), while _few and _many use "míst" (places). Czech users will see the noun change depending on count, which reads oddly. Pick one term consistently — likely "míst" / "místo" / "místa" to match the _few/_many forms and align with locationsNear (Lines 479–481).

Proposed fix to unify the noun
-  "locationWithCount_one": "{{count}} umístění",
+  "locationWithCount_one": "{{count}} místo",
   "locationWithCount_few": "{{count}} míst",
   "locationWithCount_many": "{{count}} míst",
-  "locationWithCount_other": "{{count}} umístění",
+  "locationWithCount_other": "{{count}} míst",
packages/visual-editor/locales/platform/cs/visual-editor.json-478-478 (1)

478-478: ⚠️ Potential issue | 🟡 Minor

Same noun inconsistency in locationsNear.

_one uses "umístění" while the other plural forms (Lines 479–481) use "míst". For the same reason as locationWithCount, consider aligning:

Proposed fix
-  "locationsNear_one": "{{count}} umístění poblíž „{{name}}"",
+  "locationsNear_one": "{{count}} místo poblíž „{{name}}"",
🧹 Nitpick comments (6)
packages/visual-editor/locales/platform/ja/visual-editor.json (1)

609-611: Nit: "フォント重量" is a literal translation; consider "フォントウェイト".

In web/CSS contexts, the katakana transliteration "フォントウェイト" is more commonly used in Japanese UI than the literal "フォント重量" (which reads as physical weight). Other keys in this file already use katakana transliterations for CSS terms (e.g., "フォントサイズ" for font size on line 445).

Proposed fix
     "fontWeight": {
-      "fontWeight": "フォント重量"
+      "fontWeight": "フォントウェイト"
     },
packages/visual-editor/locales/platform/zh/visual-editor.json (2)

322-323: Inconsistent translation style for "hours" between hoursStatus and hoursTable.

hoursStatus is translated literally as "小时状态" (hour status) while hoursTable uses the more natural "营业时间表" (business hours table). For consistency and naturalness, consider using "营业时间状态" or at least "时间状态" for hoursStatus, matching the style of hoursTable.

Proposed fix
- "hoursStatus": "小时状态",
+ "hoursStatus": "营业时间状态",
  "hoursTable": "营业时间表",

610-610: Literal translation for fontWeight.

"字体重量" (font weight/mass) reads awkwardly in Chinese UI contexts. The standard term is "字体粗细" (font thickness), which is widely used in design tools and browser DevTools Chinese localizations.

Proposed fix
- "fontWeight": "字体重量"
+ "fontWeight": "字体粗细"
packages/visual-editor/locales/platform/nb/visual-editor.json (1)

446-446: "Misligholde" is a questionable translation for "Default".

"Misligholde" literally means "to neglect / default on an obligation," not "default" in the software-settings sense. "Standard" would be more natural. This is a pre-existing pattern (lines 140, 273, 563) so not blocking, but worth cleaning up across the file in a follow-up.

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

1-650: Several translation values left in English — verify if intentional.

Multiple keys have untranslated English values in this German locale file:

  • Line 25: "base": "Base"
  • Line 31: "callToAction": "Call to Action" (also lines 61, 170)
  • Line 94: "heroSection": "Hero Section"
  • Line 93: "heroImage": "Hero Image"
  • Line 90: "header": "Header" (and similar Header/Footer usages)

Some of these (e.g., "Call to Action", "Header", "Footer", "Hero") may be intentional loanwords commonly used in German web/marketing contexts. If so, no action needed. If the intent is full localization, these should be translated.

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

322-323: Minor inconsistency in "hours" terminology.

Line 322 translates "hours" as "orari" ("Stato degli orari") while Line 323 uses "ore" ("Tabella delle ore"). In Italian, "orari" (schedules/timetables) is more appropriate for business hours context, while "ore" (time units) is slightly different. Consider aligning both:

Proposed fix
  "hoursStatus": "Stato degli orari",
- "hoursTable": "Tabella delle ore",
+ "hoursTable": "Tabella degli orari",

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

🤖 Fix all issues with AI agents
In `@packages/visual-editor/locales/platform/de/visual-editor.json`:
- Line 534: The value for the JSON key "secondaryHeaderLinks" has incorrect
German adjective agreement ("Erweiterter Header-Links"); update the value to the
correct plural form "Erweiterte Header-Links" so the adjective agrees with the
plural noun.
- Line 379: The translation value for the key "primaryFooter" uses the feminine
form "Primäre Footer" which is inconsistent with other entries like "Erweiterter
Footer"; update the value of the "primaryFooter" JSON entry to the masculine
form "Primärer Footer" so it matches the other footer translations.
- Line 388: The German translation for the key "secondaryHeading" uses incorrect
adjective declension; change the value from "Erweiterter Überschrift" to the
grammatically correct feminine form "Erweiterte Überschrift" for the noun
"Überschrift" (key: secondaryHeading).

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

🤖 Fix all issues with AI agents
In `@packages/visual-editor/locales/platform/de/visual-editor.json`:
- Line 510: The translation for the key "primaryHeaderLinks" uses a singular
adjective ("Primärer") but must agree with the plural noun "Header-Links";
update the value for "primaryHeaderLinks" to use the plural adjective form
(e.g., "Primäre Header-Links") so it matches the plural noun, mirroring the fix
applied for "Erweiterte Header-Links".
- Line 380: The German translation for the key "primaryHeading" uses the wrong
adjective declension ("Primärer Überschrift"); change the string value for
"primaryHeading" to the feminine form "Primäre Überschrift" so it matches the
feminine noun "Überschrift" (consistent with the fix applied to "Erweiterte
Überschrift").

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

🤖 Fix all issues with AI agents
In `@packages/visual-editor/locales/platform/de/visual-editor.json`:
- Line 211: The translation for the key footerSocialIcons uses "Ikonen" which is
uncommon in German UI; update the value for footerSocialIcons to use the more
appropriate term "Symbole" (e.g., "Footer soziale Symbole") or "Icons" to match
UI terminology and be consistent with the other key named icons.
- Line 96: The translation for components.hoursTable ("Geschäftszeiten") is
inconsistent with fields.options.hoursTable ("Stundentabelle"); update
fields.options.hoursTable to match components.hoursTable by replacing
"Stundentabelle" with "Geschäftszeiten" (or intentionally choose a different
term if the UI context requires a distinction), ensuring both keys
(components.hoursTable and fields.options.hoursTable) use the same German string
for consistency.
🧹 Nitpick comments (3)
packages/visual-editor/scripts/checkInterpolationVariables.ts (3)

122-132: Static analysis ReDoS warning — low risk here but worth a minor hardening.

The leafKey is derived from translation JSON keys (not arbitrary user input), and escapeRegex neutralises special characters, so the practical ReDoS risk is negligible. Still, since findLineNumberForKey is only used for diagnostic line numbers, a simple indexOf-based approach would sidestep the concern entirely and be slightly simpler.

♻️ Suggested simplification
 const findLineNumberForKey = (fileContent: string, key: string): number => {
   const leafKey = key.split(".").pop() ?? key;
-  const keyPattern = new RegExp(`"${escapeRegex(leafKey)}"\\s*:`, "g");
-  const match = keyPattern.exec(fileContent);
-
-  if (!match) {
+  const needle = `"${leafKey}"`;
+  const idx = fileContent.indexOf(needle);
+  if (idx === -1) {
     return 1;
   }
-
-  return fileContent.slice(0, match.index).split("\n").length;
+  return fileContent.slice(0, idx).split("\n").length;
 };

This trades the strict \s*: suffix match for a simpler substring lookup, which is fine for a best-effort line hint. If you need the colon check, the current code is acceptable given the controlled input.


193-200: Potential undefined access after array guards — add non-null assertions or destructure.

unexpectedIndexes[0] and missing[0] are typed as potentially undefined in strict TypeScript, even though the length-1 guard at lines 172 and 189 makes them safe at runtime. Downstream usage on lines 199–200 (actualExpressions[targetIndex], replacementVariable) would propagate undefined silently if the invariant were ever broken.

♻️ Suggested hardening
-  const targetIndex = unexpectedIndexes[0];
-  const replacementVariable = missing[0];
+  const targetIndex = unexpectedIndexes[0]!;
+  const replacementVariable = missing[0]!;

Or use a runtime assertion:

const targetIndex = unexpectedIndexes[0];
const replacementVariable = missing[0];
if (targetIndex === undefined || replacementVariable === undefined) {
  return null; // defensive: shouldn't happen given prior guards
}

255-258: English locale is validated against itself — skip it to avoid wasted I/O.

getSubdirectoryNames(instanceDir) returns all locale subdirectories including "en". The comparison will always yield mismatchCount === 0 for English vs. English, so it's harmless, but it reads and parses the file unnecessarily and makes the intent less clear.

Also, the locale file is read twice: once as raw text (line 257) and once parsed via loadJsonSafe (line 258). You could avoid the duplicate I/O by parsing the raw content directly.

♻️ Suggested changes
+  const locales = (await getSubdirectoryNames(instanceDir)).filter(
+    (l) => l !== PRIMARY_LOCALE
+  );
-  const locales = await getSubdirectoryNames(instanceDir);

To deduplicate the file read:

     const localeRaw = await fs.readFile(localePath, "utf-8").catch(() => "");
-    const localeFlat = flatten(await loadJsonSafe(localePath));
+    const localeFlat = flatten(
+      localeRaw ? JSON.parse(localeRaw) : {}
+    );

(Adjust error handling to taste — loadJsonSafe may have additional safety you want to preserve.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants