feat(website): Emit per-page TechArticle JSON-LD and hreflang alternates#1589
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR adds locale-aware meta tag and structured data generation to a Vitepress website configuration file. It introduces locale configuration constants, builds helper functions to resolve page locale from request paths and construct localized URLs, and rewrites the head tag generator to emit canonical, hreflang, social metadata, and JSON-LD with locale-specific values. ChangesLocale-aware page head generation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request enhances the site's SEO and metadata by implementing locale-aware head tags and structured data. It introduces a comprehensive list of supported locales with mappings to BCP-47 and Open Graph codes, and updates the head generation logic to include canonical URLs, locale-specific meta tags, and hreflang alternates. Additionally, it adds TechArticle JSON-LD structured data for documentation pages. The review feedback suggests exporting the supported locales list to ensure synchronization with the main VitePress configuration and extracting the author information into a shared constant to avoid duplication.
Deploying repomix with
|
| Latest commit: |
b363c23
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://c5103381.repomix.pages.dev |
| Branch Preview URL: | https://seo-per-page-jsonld-hreflang.repomix.pages.dev |
There was a problem hiding this comment.
🧹 Nitpick comments (4)
website/client/.vitepress/config/configShard.ts (4)
41-97: 💤 Low valueConsider consolidating the three parallel locale tables.
supportedLocales,localeToBcp47, andlocaleToOgLocalemust be kept in sync by hand whenever a locale is added or removed. A single source-of-truth map would prevent drift and let TypeScript guarantee completeness without the per-tableRecord<Locale, string>reminder.♻️ Suggested consolidation
-const supportedLocales = [ - 'en', - 'zh-cn', - ... -] as const; - -type Locale = (typeof supportedLocales)[number]; - -const localeToBcp47: Record<Locale, string> = { en: 'en', 'zh-cn': 'zh-CN', ... }; -const localeToOgLocale: Record<Locale, string> = { en: 'en_US', 'zh-cn': 'zh_CN', ... }; +const localeConfig = { + en: { bcp47: 'en', og: 'en_US' }, + 'zh-cn': { bcp47: 'zh-CN', og: 'zh_CN' }, + 'zh-tw': { bcp47: 'zh-TW', og: 'zh_TW' }, + ja: { bcp47: 'ja', og: 'ja_JP' }, + // ... +} as const; + +type Locale = keyof typeof localeConfig; +const supportedLocales = Object.keys(localeConfig) as Locale[];🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/client/.vitepress/config/configShard.ts` around lines 41 - 97, Replace the three parallel structures (supportedLocales, localeToBcp47, localeToOgLocale) with a single as-const map object (e.g., localeDefinitions) that maps each locale key to its bcp47 and ogLocale strings; derive supportedLocales from Object.keys or from the map's keys and derive the Locale type from the map's keys so TypeScript enforces completeness, then remove localeToBcp47 and localeToOgLocale and replace their usages to read from localeDefinitions[locale].bcp47 and .ogLocale (references: supportedLocales, localeToBcp47, localeToOgLocale).
168-185: ⚡ Quick winStrengthen graph linking between
TechArticle.isPartOfand the globalWebSite.The global
WebSitenode (lines 194-199) has no@id, and the per-pageisPartOfhere re-declares an inlineWebSiterather than referencing it. Search engines treat these as separate nodes. Giving the globalWebSitea stable@id(e.g.${siteUrl}#website``) and usingisPartOf: { '@id`': '${siteUrl}`#website`' }` produces a single linked entity across pages — which is the PR's stated rationale for connecting article content to the product entity.♻️ Suggested change
// line 194 (global jsonLd) { + '`@id`': `${siteUrl}`#website``, '`@type`': 'WebSite', name: siteName, url: siteUrl, description: siteDescription, },- isPartOf: { '`@type`': 'WebSite', name: siteName, url: siteUrl }, + isPartOf: { '`@id`': `${siteUrl}`#website`` }, mainEntityOfPage: { '`@type`': 'WebPage', '`@id`': url },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/client/.vitepress/config/configShard.ts` around lines 168 - 185, The article JSON-LD currently embeds an inline WebSite in articleJsonLd.isPartOf, which prevents linking to the global WebSite node; update the global WebSite JSON-LD to include a stable `@id` (e.g., `${siteUrl}`#website``) and change articleJsonLd.isPartOf to reference that id via isPartOf: { '`@id`': `${siteUrl}`#website`` } so all pages point to the same WebSite entity (refer to articleJsonLd, isPartOf, siteUrl and the global WebSite JSON-LD block).
150-163: 💤 Low valueOptional: also emit
og:locale:alternatefor non-canonical locales.You're emitting
hreflangalternates for crawlers but not the OpenGraph equivalent. Adding oneog:locale:alternateper non-current locale is a small win for social previews that honor it.♻️ Suggested addition
for (const alt of supportedLocales) { tags.push([ 'link', { rel: 'alternate', hreflang: localeToBcp47[alt], href: buildLocaleUrl(alt, rest), }, ]); + if (alt !== locale) { + tags.push(['meta', { property: 'og:locale:alternate', content: localeToOgLocale[alt] }]); + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/client/.vitepress/config/configShard.ts` around lines 150 - 163, Add an OpenGraph locale-alternate meta for each non-canonical locale: inside the loop over supportedLocales (the for (const alt of supportedLocales) block) push an additional tag like ['meta', { property: 'og:locale:alternate', content: localeToBcp47[alt] }] for every alt that is not the current locale (compare alt !== currentLocale or your equivalent variable), or if you prefer, after the loop iterate supportedLocales and push that meta for each non-current alt; ensure you use localeToBcp47[alt] for the content and add the tags to the existing tags array the same way buildLocaleUrl and hreflang are added.
137-146: ⚡ Quick winSet
og:typetoarticleon non-home documentation pages to match the TechArticle JSON-LD
createPageHeademitsTechArticleJSON-LD for!isHome, but the globalheadhardcodesog:typetowebsite; add['meta', { property: 'og:type', content: isHome ? 'website' : 'article' }]to the per-pagetagsarray.VitePress merges
headentries and doesn’t dedupe byproperty/name, so remove the globalog:typetag when moving it tocreatePageHeadto avoid duplicate OG tags.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/client/.vitepress/config/configShard.ts` around lines 137 - 146, Update the per-page head tags in createPageHead to set og:type based on isHome by adding ['meta', { property: 'og:type', content: isHome ? 'website' : 'article' }] to the tags array (the function createPageHead and its local tags variable are the targets), and remove the global hardcoded og:type meta from the top-level head so VitePress doesn't emit duplicate Open Graph tags; ensure the new tag is only added in createPageHead for non-home pages to match the TechArticle JSON-LD.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@website/client/.vitepress/config/configShard.ts`:
- Around line 41-97: Replace the three parallel structures (supportedLocales,
localeToBcp47, localeToOgLocale) with a single as-const map object (e.g.,
localeDefinitions) that maps each locale key to its bcp47 and ogLocale strings;
derive supportedLocales from Object.keys or from the map's keys and derive the
Locale type from the map's keys so TypeScript enforces completeness, then remove
localeToBcp47 and localeToOgLocale and replace their usages to read from
localeDefinitions[locale].bcp47 and .ogLocale (references: supportedLocales,
localeToBcp47, localeToOgLocale).
- Around line 168-185: The article JSON-LD currently embeds an inline WebSite in
articleJsonLd.isPartOf, which prevents linking to the global WebSite node;
update the global WebSite JSON-LD to include a stable `@id` (e.g.,
`${siteUrl}`#website``) and change articleJsonLd.isPartOf to reference that id via
isPartOf: { '`@id`': `${siteUrl}`#website`` } so all pages point to the same WebSite
entity (refer to articleJsonLd, isPartOf, siteUrl and the global WebSite JSON-LD
block).
- Around line 150-163: Add an OpenGraph locale-alternate meta for each
non-canonical locale: inside the loop over supportedLocales (the for (const alt
of supportedLocales) block) push an additional tag like ['meta', { property:
'og:locale:alternate', content: localeToBcp47[alt] }] for every alt that is not
the current locale (compare alt !== currentLocale or your equivalent variable),
or if you prefer, after the loop iterate supportedLocales and push that meta for
each non-current alt; ensure you use localeToBcp47[alt] for the content and add
the tags to the existing tags array the same way buildLocaleUrl and hreflang are
added.
- Around line 137-146: Update the per-page head tags in createPageHead to set
og:type based on isHome by adding ['meta', { property: 'og:type', content:
isHome ? 'website' : 'article' }] to the tags array (the function createPageHead
and its local tags variable are the targets), and remove the global hardcoded
og:type meta from the top-level head so VitePress doesn't emit duplicate Open
Graph tags; ensure the new tag is only added in createPageHead for non-home
pages to match the TechArticle JSON-LD.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a6d69936-cbca-436c-888a-10833bb2a90b
📒 Files selected for processing (1)
website/client/.vitepress/config/configShard.ts
⚡ Performance Benchmark
Details
Historyde72078 [autofix.ci] apply automated fixes
|
Documentation pages now publish a per-page `TechArticle` JSON-LD with `inLanguage`, `mainEntityOfPage`, and an `isPartOf` link to the existing global `WebSite` graph. The home page intentionally skips the article schema since it is already represented by the `SoftwareApplication` entity. Every page also emits `hreflang` alternate links (BCP-47) for all supported locales plus `x-default` pointing at English, and an `og:locale` meta. This gives Google and AI Overviews enough language/locale signal to route users to the matching translation instead of falling back to English. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Consolidate `supportedLocales`, `localeToBcp47`, and `localeToOgLocale` into a single `localeConfig` map and export it along with the `Locale` type so the locale list lives in one place and can be reused by the main VitePress config later. - Extract the duplicated author block into a shared `siteAuthor` constant referenced by both the global SoftwareApplication graph and the per-page TechArticle. - Give the global `WebSite` node a stable `@id` and reference it from `TechArticle.isPartOf` so search engines see a single linked entity across pages instead of inlined duplicates. - Emit per-page `og:type` (`article` for docs, `website` for the home page) and drop the global `og:type` so the OpenGraph type matches the TechArticle schema. - Add `og:locale:alternate` for every non-current locale alongside the existing `hreflang` alternates so social previews can also route to the matching localized page. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
de72078 to
b363c23
Compare
Summary
ja/guide/installation.md→ localeja, restguide/installation).hreflangalternate links for all 15 supported locales plusx-defaultpointing at English, and anog:localemeta tag.TechArticleJSON-LD on documentation pages withinLanguage,mainEntityOfPage, and anisPartOflink to the existing globalWebSitegraph. The home page is intentionally excluded because the globalSoftwareApplicationentity already covers it.Why
The previous setup carried a single global
WebSite+SoftwareApplicationJSON-LD graph and nohreflangannotation. Google, AI Overviews, and other LLM-backed search surfaces lacked the signal to route users to the matching localized page or to identify documentation articles as such.Verification
node --run docs:buildsucceeds and the emitted HTML fordist/ja/guide/installation.html,dist/guide/installation.html, anddist/index.htmlcontains the expectedhreflangset,og:locale, and (for non-home pages) theTechArticleJSON-LD with the correctinLanguage.Checklist
node --run lint(website/client)node --run docs:build(website/client)