From 99be37eda0dda37f0c342aec8668fdd258a62ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 27 Aug 2025 15:58:01 +0200 Subject: [PATCH 1/5] Use space language in priority --- .../[siteURL]/[siteData]/(content)/layout.tsx | 2 +- .../[siteURL]/[siteData]/(content)/layout.tsx | 2 +- .../AI/server-actions/AIToolCallsSummary.tsx | 8 ++-- .../DocumentView/InlineLink/InlineLink.tsx | 5 +-- .../Embeddable/EmbeddableRootLayout.tsx | 2 +- .../gitbook/src/components/Header/Header.tsx | 2 +- .../gitbook/src/components/PDF/PDFPage.tsx | 5 +-- .../src/components/PDF/PDFRootLayout.tsx | 8 +--- .../src/components/PageAside/PageAside.tsx | 2 +- .../src/components/PageBody/PageBody.tsx | 2 +- .../PageBody/PageFooterNavigation.tsx | 4 +- .../RootLayout/CustomizationRootLayout.tsx | 13 ++++--- .../TableOfContents/TableOfContents.tsx | 3 +- .../components/TableOfContents/Trademark.tsx | 18 ++++----- packages/gitbook/src/intl/server.ts | 39 +++++++++++++++---- .../gitbook/src/intl/translations/index.ts | 10 ++--- 16 files changed, 65 insertions(+), 60 deletions(-) diff --git a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/(content)/layout.tsx b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/(content)/layout.tsx index a8fd4e5511..eea0f85164 100644 --- a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/(content)/layout.tsx +++ b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/(content)/layout.tsx @@ -22,7 +22,7 @@ export default async function SiteDynamicLayout({ const withTracking = shouldTrackEvents(await headers()); return ( - + + @@ -102,7 +102,7 @@ function DescriptionForMCPToolCall(props: { }) { const { toolCall, context } = props; - const language = getSpaceLanguage(context.customization); + const language = getSpaceLanguage(context); return (

@@ -130,7 +130,7 @@ async function DescriptionForSearchToolCall(props: { }) { const { toolCall, context } = props; - const language = getSpaceLanguage(context.customization); + const language = getSpaceLanguage(context); // Resolve all hrefs for search results in parallel const searchResultsWithHrefs = await Promise.all( @@ -241,7 +241,7 @@ function DescriptionForGetPagesToolCall(props: { }) { const { toolCall, context } = props; - const language = getSpaceLanguage(context.customization); + const language = getSpaceLanguage(context); return (

diff --git a/packages/gitbook/src/components/DocumentView/InlineLink/InlineLink.tsx b/packages/gitbook/src/components/DocumentView/InlineLink/InlineLink.tsx index 33e7304037..e0869e3e9b 100644 --- a/packages/gitbook/src/components/DocumentView/InlineLink/InlineLink.tsx +++ b/packages/gitbook/src/components/DocumentView/InlineLink/InlineLink.tsx @@ -20,10 +20,7 @@ export async function InlineLink(props: InlineProps) { : null; const { contentContext } = context; - const language = - contentContext && 'customization' in contentContext - ? getSpaceLanguage(contentContext.customization) - : languages.en; + const language = contentContext ? getSpaceLanguage(contentContext) : languages.en; if (!contentContext || !resolved) { return ( diff --git a/packages/gitbook/src/components/Embeddable/EmbeddableRootLayout.tsx b/packages/gitbook/src/components/Embeddable/EmbeddableRootLayout.tsx index 3e2ecd1ffa..fec1269d44 100644 --- a/packages/gitbook/src/components/Embeddable/EmbeddableRootLayout.tsx +++ b/packages/gitbook/src/components/Embeddable/EmbeddableRootLayout.tsx @@ -21,7 +21,7 @@ export async function EmbeddableRootLayout({ children, }: React.PropsWithChildren) { return ( - + diff --git a/packages/gitbook/src/components/PDF/PDFPage.tsx b/packages/gitbook/src/components/PDF/PDFPage.tsx index d29790f25f..3918283c14 100644 --- a/packages/gitbook/src/components/PDF/PDFPage.tsx +++ b/packages/gitbook/src/components/PDF/PDFPage.tsx @@ -57,7 +57,7 @@ export async function PDFPage(props: { const customization = 'customization' in baseContext ? baseContext.customization : defaultCustomization(); - const language = getSpaceLanguage(customization); + const language = getSpaceLanguage(baseContext); // Compute the pages to render const { pages, total } = selectPages(baseContext.revision.pages, pdfParams); @@ -149,8 +149,7 @@ export async function PDFPage(props: { trademark={ customization.trademark.enabled ? ( ) : null diff --git a/packages/gitbook/src/components/PDF/PDFRootLayout.tsx b/packages/gitbook/src/components/PDF/PDFRootLayout.tsx index 8b290e2d1e..498adbeb88 100644 --- a/packages/gitbook/src/components/PDF/PDFRootLayout.tsx +++ b/packages/gitbook/src/components/PDF/PDFRootLayout.tsx @@ -1,6 +1,5 @@ import { CustomizationRootLayout } from '@/components/RootLayout'; import type { GitBookSiteContext, GitBookSpaceContext } from '@/lib/context'; -import { defaultCustomization } from '@/lib/utils'; import { CustomizationThemeMode } from '@gitbook/api'; /** @@ -13,12 +12,7 @@ export async function PDFRootLayout(props: { const { context, children } = props; return ( - + {children} ); diff --git a/packages/gitbook/src/components/PageAside/PageAside.tsx b/packages/gitbook/src/components/PageAside/PageAside.tsx index 3ab7b22204..bc0570c418 100644 --- a/packages/gitbook/src/components/PageAside/PageAside.tsx +++ b/packages/gitbook/src/components/PageAside/PageAside.tsx @@ -128,7 +128,7 @@ export function PageAside(props: { function PageAsideHeader(props: { context: GitBookSiteContext }) { const { context } = props; - const language = getSpaceLanguage(context.customization); + const language = getSpaceLanguage(context); return (

{ const rgbValue = hexToRgb(value); // Check the original hex value - // @ts-expect-error const contrastValue = withContrast ? hexToRgb(colorContrast(value)) : undefined; // Add contrast if needed return `--${name}-${key}: ${rgbValue}; ${ contrastValue ? `--contrast-${name}-${key}: ${contrastValue};` : '' diff --git a/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx b/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx index 426c6e9cb3..0d5f1c3fec 100644 --- a/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx +++ b/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx @@ -115,8 +115,7 @@ export async function TableOfContents(props: { /> {customization.trademark.enabled ? ( ) : null} diff --git a/packages/gitbook/src/components/TableOfContents/Trademark.tsx b/packages/gitbook/src/components/TableOfContents/Trademark.tsx index 487568555a..bd077d84cf 100644 --- a/packages/gitbook/src/components/TableOfContents/Trademark.tsx +++ b/packages/gitbook/src/components/TableOfContents/Trademark.tsx @@ -1,21 +1,17 @@ -import type { - SiteCustomizationSettings, - SiteInsightsTrademarkPlacement, - Space, -} from '@gitbook/api'; +import type { SiteInsightsTrademarkPlacement } from '@gitbook/api'; import { Icon } from '@gitbook/icons'; import { getSpaceLanguage, t } from '@/intl/server'; import { tcls } from '@/lib/tailwind'; +import type { GitBookSpaceContext } from '@/lib/context'; import { Link } from '../primitives'; /** * Trademark link to the GitBook. */ export function Trademark(props: { - space: Space; - customization: SiteCustomizationSettings; + context: GitBookSpaceContext; placement: SiteInsightsTrademarkPlacement; }) { return ( @@ -71,12 +67,12 @@ export function Trademark(props: { * Trademark link to the GitBook. */ export function TrademarkLink(props: { - space: Space; - customization: SiteCustomizationSettings; + context: GitBookSpaceContext; placement: SiteInsightsTrademarkPlacement; }) { - const { space, customization, placement } = props; - const language = getSpaceLanguage(customization); + const { context, placement } = props; + const { space } = context; + const language = getSpaceLanguage(context); const url = new URL('https://www.gitbook.com'); url.searchParams.set('utm_source', 'content'); diff --git a/packages/gitbook/src/intl/server.ts b/packages/gitbook/src/intl/server.ts index b13076f41d..3b53fcca26 100644 --- a/packages/gitbook/src/intl/server.ts +++ b/packages/gitbook/src/intl/server.ts @@ -1,25 +1,44 @@ -import type { SiteCustomizationSettings } from '@gitbook/api'; - +import type { GitBookAnyContext } from '@/lib/context'; import { type TranslationLanguage, languages } from './translations'; export * from './translate'; -export const DEFAULT_LOCALE = 'en'; +type TranslationLocale = keyof typeof languages; + +export const DEFAULT_LOCALE: TranslationLocale = 'en'; /** - * Get the locale of the customization. + * Get the locale to use for a space. */ -export function getCustomizationLocale(customization: SiteCustomizationSettings): string { - return customization.internationalization.locale; +export function getSpaceLocale(context: GitBookAnyContext): TranslationLocale { + const { space } = context; + const customization = 'site' in context ? context.customization : null; + + // If the language is configured in the space, use it in priority + if (space.language) { + if (checkIsValidLocale(space.language)) { + return space.language; + } + return DEFAULT_LOCALE; + } + + // Otherwise fallback to the deprecated customization settings + if (customization) { + return checkIsValidLocale(customization.internationalization.locale) + ? customization.internationalization.locale + : DEFAULT_LOCALE; + } + + return DEFAULT_LOCALE; } /** * Create the translation context for a space to use in the server components. */ -export function getSpaceLanguage(customization: SiteCustomizationSettings): TranslationLanguage { +export function getSpaceLanguage(context: GitBookAnyContext): TranslationLanguage { const fallback = languages[DEFAULT_LOCALE]; - const locale = getCustomizationLocale(customization); + const locale = getSpaceLocale(context); let language = fallback; // @ts-ignore @@ -33,3 +52,7 @@ export function getSpaceLanguage(customization: SiteCustomizationSettings): Tran ...language, }; } + +function checkIsValidLocale(locale: string): locale is TranslationLocale { + return locale in languages; +} diff --git a/packages/gitbook/src/intl/translations/index.ts b/packages/gitbook/src/intl/translations/index.ts index 06fc412689..be95003160 100644 --- a/packages/gitbook/src/intl/translations/index.ts +++ b/packages/gitbook/src/intl/translations/index.ts @@ -1,5 +1,3 @@ -import type { CustomizationLocale } from '@gitbook/api'; - import { de } from './de'; import { en } from './en'; import { es } from './es'; @@ -14,11 +12,7 @@ import { zh } from './zh'; export * from './types'; -export const languages: { - [key: string]: TranslationLanguage; -} & { - [locale in CustomizationLocale]: TranslationLanguage; -} = { +export const languages = { de, en, fr, @@ -29,4 +23,6 @@ export const languages: { no, 'pt-br': pt_br, ru, +} satisfies { + [key: string]: TranslationLanguage; }; From 77ba9278b29888b08330bdbaf320d6abd18a4f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 27 Aug 2025 16:08:23 +0200 Subject: [PATCH 2/5] Fix color type --- packages/colors/src/transformations.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/colors/src/transformations.ts b/packages/colors/src/transformations.ts index 82d09e0118..e6773039c6 100644 --- a/packages/colors/src/transformations.ts +++ b/packages/colors/src/transformations.ts @@ -419,7 +419,10 @@ export function dpsContrast(a: RGBColor, b: RGBColor) { return contrast < 7.5 ? 0 : contrast; } -export function colorContrast(background: string, foreground: string[] = [LIGHT_BASE, DARK_BASE]) { +export function colorContrast( + background: string, + foreground: string[] = [LIGHT_BASE, DARK_BASE] +): string { const bg = hexToRgbArray(background); const best: { color?: RGBColor; contrast: number } = { @@ -436,5 +439,5 @@ export function colorContrast(background: string, foreground: string[] = [LIGHT_ } } - return best.color ? rgbArrayToHex(best.color) : foreground[0]; + return best.color ? rgbArrayToHex(best.color) : foreground[0] || LIGHT_BASE; } From 618d362b2ca2308567ad1ce1baaaaa47b535404e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 27 Aug 2025 16:09:15 +0200 Subject: [PATCH 3/5] Changeset --- .changeset/chilly-otters-beam.md | 5 +++++ .changeset/red-countries-confess.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/chilly-otters-beam.md create mode 100644 .changeset/red-countries-confess.md diff --git a/.changeset/chilly-otters-beam.md b/.changeset/chilly-otters-beam.md new file mode 100644 index 0000000000..77341e6bf8 --- /dev/null +++ b/.changeset/chilly-otters-beam.md @@ -0,0 +1,5 @@ +--- +"@gitbook/colors": patch +--- + +Fix return type for `colorContrast` diff --git a/.changeset/red-countries-confess.md b/.changeset/red-countries-confess.md new file mode 100644 index 0000000000..e334b3280c --- /dev/null +++ b/.changeset/red-countries-confess.md @@ -0,0 +1,5 @@ +--- +"gitbook": patch +--- + +Use space language as source of truth for UI locale From a853ac12a59faf2f78020a4c3fb58aeae161ea88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 27 Aug 2025 16:12:02 +0200 Subject: [PATCH 4/5] Fix types --- .../src/components/DocumentView/OpenAPI/context.tsx | 7 ++----- packages/gitbook/src/intl/server.ts | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/gitbook/src/components/DocumentView/OpenAPI/context.tsx b/packages/gitbook/src/components/DocumentView/OpenAPI/context.tsx index 2dc088f248..70be2eb457 100644 --- a/packages/gitbook/src/components/DocumentView/OpenAPI/context.tsx +++ b/packages/gitbook/src/components/DocumentView/OpenAPI/context.tsx @@ -11,7 +11,7 @@ import { Heading } from '../Heading'; import './scalar.css'; import './style.css'; -import { DEFAULT_LOCALE, getCustomizationLocale } from '@/intl/server'; +import { DEFAULT_LOCALE, getSpaceLocale } from '@/intl/server'; import type { GitBookAnyContext } from '@/lib/context'; import type { AnyOpenAPIOperationsBlock, @@ -30,10 +30,7 @@ export function getOpenAPIContext(args: { const { props, specUrl, context } = args; const { block } = props; - const customization = context && 'customization' in context ? context.customization : null; - const customizationLocale = customization - ? getCustomizationLocale(customization) - : DEFAULT_LOCALE; + const customizationLocale = context ? getSpaceLocale(context) : DEFAULT_LOCALE; const locale = checkIsValidLocale(customizationLocale) ? customizationLocale : DEFAULT_LOCALE; return { diff --git a/packages/gitbook/src/intl/server.ts b/packages/gitbook/src/intl/server.ts index 3b53fcca26..a6ed78a6ce 100644 --- a/packages/gitbook/src/intl/server.ts +++ b/packages/gitbook/src/intl/server.ts @@ -5,7 +5,7 @@ export * from './translate'; type TranslationLocale = keyof typeof languages; -export const DEFAULT_LOCALE: TranslationLocale = 'en'; +export const DEFAULT_LOCALE = 'en' satisfies TranslationLocale; /** * Get the locale to use for a space. From a8b191253598c83591b5cd53d6479a864053e3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 27 Aug 2025 16:19:50 +0200 Subject: [PATCH 5/5] Lint --- .../gitbook/src/components/TableOfContents/TableOfContents.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx b/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx index 0d5f1c3fec..9afcc33d10 100644 --- a/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx +++ b/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx @@ -16,7 +16,7 @@ export async function TableOfContents(props: { innerHeader?: React.ReactNode; // Displayed outside the scrollable TOC, directly above the page list }) { const { innerHeader, context, header } = props; - const { space, customization, revision } = context; + const { customization, revision } = context; const pages = await encodeClientTableOfContents(context, revision.pages, revision.pages);