diff --git a/packages/gitbook/src/components/Search/server-actions.tsx b/packages/gitbook/src/components/Search/server-actions.tsx index cabb2abca9..85989312f5 100644 --- a/packages/gitbook/src/components/Search/server-actions.tsx +++ b/packages/gitbook/src/components/Search/server-actions.tsx @@ -3,7 +3,7 @@ import type { GitBookBaseContext, GitBookSiteContext } from '@/lib/context'; import { resolvePageId } from '@/lib/pages'; import { fetchServerActionSiteContext, getServerActionBaseContext } from '@/lib/server-actions'; -import { findSiteSpaceBy } from '@/lib/sites'; +import { findSiteSpaceBy, getSiteSectionTitle } from '@/lib/sites'; import { filterOutNullable } from '@/lib/typescript'; import type { Revision, @@ -350,6 +350,7 @@ async function transformSitePageResult( ): Promise { const { pageItem, spaceItem, spaceURL, siteSection, siteSectionGroup, siteSpace } = args; const { linker } = context; + const currentLanguage = siteSpace?.space.language; const page: ComputedPageResult = { type: 'page', @@ -367,7 +368,7 @@ async function transformSitePageResult( }, siteSection && { icon: siteSection?.icon as IconName, - label: siteSection.title, + label: getSiteSectionTitle(siteSection, currentLanguage), }, (siteSection?.siteSpaces?.filter( // If a space is the only one in its langauge, it's a translation variant and we don't want to show it. diff --git a/packages/gitbook/src/components/SitePage/SitePage.tsx b/packages/gitbook/src/components/SitePage/SitePage.tsx index e75a89c346..349cce5e15 100644 --- a/packages/gitbook/src/components/SitePage/SitePage.tsx +++ b/packages/gitbook/src/components/SitePage/SitePage.tsx @@ -17,6 +17,7 @@ import { isPageIndexable, isSiteIndexable } from '@/lib/seo'; import { getResizedImageURL } from '@/lib/images'; import { resolveContentRef } from '@/lib/references'; +import { getSiteSectionTitle } from '@/lib/sites'; import { tcls } from '@/lib/tailwind'; import { getPageRSSURL } from '@/routes/rss'; import { PageContextProvider } from '../PageContext'; @@ -125,6 +126,7 @@ export async function generateSitePageViewport(context: GitBookSiteContext): Pro */ function getSiteStructureTitle(context: GitBookSiteContext): string | null { const { visibleSections: sections, siteSpace, visibleSiteSpaces: siteSpaces } = context; + const currentLanguage = siteSpace.space.language; const title = []; if ( @@ -132,7 +134,7 @@ function getSiteStructureTitle(context: GitBookSiteContext): string | null { sections.current.default === false && // Only if the current section is not the default one sections.list.filter((section) => section.object === 'site-section').length > 1 // Only if there are multiple sections ) { - title.push(sections.current.title); + title.push(getSiteSectionTitle(sections.current, currentLanguage)); } if ( siteSpaces.length > 1 && // Only if there are multiple variants diff --git a/packages/gitbook/src/components/SiteSections/encodeClientSiteSections.ts b/packages/gitbook/src/components/SiteSections/encodeClientSiteSections.ts index 500a98ada7..228bb03a2d 100644 --- a/packages/gitbook/src/components/SiteSections/encodeClientSiteSections.ts +++ b/packages/gitbook/src/components/SiteSections/encodeClientSiteSections.ts @@ -1,5 +1,5 @@ import type { GitBookSiteContext, SiteSections } from '@/lib/context'; -import { getSectionURL, getSiteSpaceURL } from '@/lib/sites'; +import { getSectionURL, getSiteSectionTitle, getSiteSpaceURL } from '@/lib/sites'; import type { SiteSection, SiteSectionGroup, SiteSpace } from '@gitbook/api'; import assertNever from 'assert-never'; @@ -99,9 +99,10 @@ function encodeChildren( } function encodeSection(context: GitBookSiteContext, section: SiteSection) { + const currentLanguage = context.siteSpace.space.language; return { id: section.id, - title: section.title, + title: getSiteSectionTitle(section, currentLanguage), description: section.description, icon: section.icon, object: section.object, diff --git a/packages/gitbook/src/lib/sites.ts b/packages/gitbook/src/lib/sites.ts index 4da397187c..b13d8111d4 100644 --- a/packages/gitbook/src/lib/sites.ts +++ b/packages/gitbook/src/lib/sites.ts @@ -1,5 +1,12 @@ import type { GitBookSiteContext } from '@/lib/context'; -import type { SiteSection, SiteSectionGroup, SiteSpace, SiteStructure } from '@gitbook/api'; +import { filterOutNullable } from '@/lib/typescript'; +import { + type SiteSection, + type SiteSectionGroup, + type SiteSpace, + type SiteStructure, + TranslationLanguage, +} from '@gitbook/api'; import { joinPath } from './paths'; import { flattenSectionsFromGroup } from './utils'; @@ -177,3 +184,42 @@ function findSiteSpaceByIdInSiteSpaces( ): SiteSpace | null { return siteSpaces.find(predicate) ?? null; } + +/** + * Check if a siteSection has variants with multiple languages. + */ +function hasMultipleLanguagesInVariants(siteSection: SiteSection): boolean { + if (!siteSection.siteSpaces || siteSection.siteSpaces.length < 2) { + return false; + } + + const languages = new Set( + siteSection.siteSpaces.map((space) => space.space.language).filter(filterOutNullable) + ); + + return languages.size > 1; +} + +/** + * Get the appropriate title for a siteSection. + * Uses localizedTitle if the section has variants with multiple languages, otherwise uses title. + */ +export function getSiteSectionTitle( + siteSection: SiteSection, + currentLanguage: TranslationLanguage | undefined +): string { + const hasMultipleLanguages = hasMultipleLanguagesInVariants(siteSection); + + if (hasMultipleLanguages && siteSection.localizedTitle) { + // Use localizedTitle if available and section has multiple languages + if (currentLanguage && siteSection.localizedTitle[currentLanguage]) { + return siteSection.localizedTitle[currentLanguage]; + } + // Fallback to 'en' if current language not found, or to title if 'en' not available + if (siteSection.localizedTitle[TranslationLanguage.En]) { + return siteSection.localizedTitle[TranslationLanguage.En]; + } + } + + return siteSection.title; +} diff --git a/packages/gitbook/src/routes/llms.ts b/packages/gitbook/src/routes/llms.ts index 4d1af9d7e2..c9e0b50c54 100644 --- a/packages/gitbook/src/routes/llms.ts +++ b/packages/gitbook/src/routes/llms.ts @@ -3,7 +3,7 @@ import { throwIfDataError } from '@/lib/data'; import type { GitBookLinker } from '@/lib/links'; import { joinPath } from '@/lib/paths'; import { type FlatPageEntry, getIndexablePages } from '@/lib/sitemap'; -import { getSiteStructureSections } from '@/lib/sites'; +import { getSiteSectionTitle, getSiteStructureSections } from '@/lib/sites'; import type { SiteSection, SiteSpace } from '@gitbook/api'; import assertNever from 'assert-never'; import type { ListItem, Paragraph, Root, RootContent } from 'mdast'; @@ -89,6 +89,7 @@ async function getNodesFromSections( withMarkdownPages: boolean; } ): Promise { + const currentLanguage = context.siteSpace.space.language; const all = await Promise.all( siteSections.map(async (siteSection): Promise => { const siteSpaceNodes = await getNodesFromSiteSpaces(context, siteSection.siteSpaces, { @@ -99,7 +100,9 @@ async function getNodesFromSections( { type: 'heading', depth: 2, - children: [{ type: 'text', value: siteSection.title }], + children: [ + { type: 'text', value: getSiteSectionTitle(siteSection, currentLanguage) }, + ], }, ...siteSpaceNodes, ];