Skip to content

Translate OpenAPI blocks #3166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/light-moons-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@gitbook/react-openapi": patch
"gitbook": patch
---

Translate OpenAPI blocks
34 changes: 17 additions & 17 deletions packages/gitbook/e2e/customers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ const testCases: TestsCase[] = [
{ name: 'OpenAPI', url: '/snyk-api/reference/apps', run: waitForCookiesDialog },
],
},
{
name: 'Nexthink',
contentBaseURL: 'https://docs.nexthink.com',
tests: [
{
name: 'Home',
url: '/',
screenshot: { waitForTOCScrolling: false },
run: waitForCookiesDialog,
},
],
},
// {
// name: 'Nexthink',
// contentBaseURL: 'https://docs.nexthink.com',
// tests: [
// {
// name: 'Home',
// url: '/',
// screenshot: { waitForTOCScrolling: false },
// run: waitForCookiesDialog,
// },
// ],
// },
{
name: 'asiksupport-stg.dto.kemkes.go.id',
contentBaseURL: 'https://asiksupport-stg.dto.kemkes.go.id',
Expand Down Expand Up @@ -157,11 +157,11 @@ const testCases: TestsCase[] = [
contentBaseURL: 'https://wiki.redmodding.org',
tests: [{ name: 'Home', url: '/' }],
},
{
name: 'docs.cherry-ai.com',
contentBaseURL: 'https://docs.cherry-ai.com',
tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
},
// {
// name: 'docs.cherry-ai.com',
// contentBaseURL: 'https://docs.cherry-ai.com',
// tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
// },
{
name: 'docs.snyk.io',
contentBaseURL: 'https://docs.snyk.io',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async function OpenAPIOperationBody(props: BlockProps<AnyOpenAPIOperationsBlock>
return (
<BaseOpenAPIOperation
data={data}
context={getOpenAPIContext({ props, specUrl })}
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
className="openapi-block"
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async function OpenAPISchemasBody(props: BlockProps<OpenAPISchemasBlock>) {
<BaseOpenAPISchemas
schemas={data.schemas}
grouped={block.data.grouped}
context={getOpenAPIContext({ props, specUrl })}
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
className="openapi-block"
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async function OpenAPIWebhookBody(props: BlockProps<OpenAPIWebhookBlock>) {
return (
<BaseOpenAPIWebhook
data={data}
context={getOpenAPIContext({ props, specUrl })}
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
className="openapi-block"
/>
);
Expand Down
17 changes: 14 additions & 3 deletions packages/gitbook/src/components/DocumentView/OpenAPI/context.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { JSONDocument } from '@gitbook/api';
import { Icon } from '@gitbook/icons';
import type { OpenAPIContext } from '@gitbook/react-openapi';
import { type OpenAPIContextInput, checkIsValidLocale } from '@gitbook/react-openapi';

import { tcls } from '@/lib/tailwind';

Expand All @@ -11,21 +11,31 @@ import { Heading } from '../Heading';

import './scalar.css';
import './style.css';
import { DEFAULT_LOCALE, getCustomizationLocale } from '@/intl/server';
import type {
AnyOpenAPIOperationsBlock,
OpenAPISchemasBlock,
OpenAPIWebhookBlock,
} from '@/lib/openapi/types';
import type { GitBookAnyContext } from '@v2/lib/context';

/**
* Get the OpenAPI context to render a block.
*/
export function getOpenAPIContext(args: {
props: BlockProps<AnyOpenAPIOperationsBlock | OpenAPISchemasBlock | OpenAPIWebhookBlock>;
specUrl: string;
}): OpenAPIContext {
const { props, specUrl } = args;
context: GitBookAnyContext | undefined;
}): OpenAPIContextInput {
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 locale = checkIsValidLocale(customizationLocale) ? customizationLocale : DEFAULT_LOCALE;

return {
specUrl,
icons: {
Expand Down Expand Up @@ -73,5 +83,6 @@ export function getOpenAPIContext(args: {
defaultInteractiveOpened: props.context.mode === 'print',
id: block.meta?.id,
blockKey: block.key,
locale,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -229,19 +229,19 @@
}

.openapi-schema-required {
@apply text-warning-subtle text-[0.813rem];
@apply text-warning-subtle text-[0.813rem] lowercase;
}

.openapi-schema-optional {
@apply text-info-subtle text-[0.813rem];
@apply text-info-subtle text-[0.813rem] lowercase;
}

.openapi-schema-readonly {
@apply text-primary-subtle/9 text-[0.813rem];
@apply text-primary-subtle/9 text-[0.813rem] lowercase;
}

.openapi-schema-writeonly {
@apply text-success dark:text-success-subtle/9 text-[0.813rem];
@apply text-success dark:text-success-subtle/9 text-[0.813rem] lowercase;
}

.openapi-schema-type {
Expand Down
15 changes: 12 additions & 3 deletions packages/gitbook/src/intl/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,26 @@ import { type TranslationLanguage, languages } from './translations';

export * from './translate';

export const DEFAULT_LOCALE = 'en';

/**
* Get the locale of the customization.
*/
export function getCustomizationLocale(customization: SiteCustomizationSettings): string {
return customization.internationalization.locale;
}

/**
* Create the translation context for a space to use in the server components.
*/
export function getSpaceLanguage(customization: SiteCustomizationSettings): TranslationLanguage {
const fallback = languages.en;
const fallback = languages[DEFAULT_LOCALE];

const { locale } = customization.internationalization;
const locale = getCustomizationLocale(customization);

let language = fallback;
// @ts-ignore
if (locale !== 'en' && languages[locale]) {
if (locale !== DEFAULT_LOCALE && languages[locale]) {
// @ts-ignore
language = languages[locale];
}
Expand Down
Empty file.
1 change: 1 addition & 0 deletions packages/react-openapi/src/InteractiveSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export function InteractiveSection(props: {
) : null}
{header}
</SectionHeaderContent>
{/* biome-ignore lint/a11y/useKeyWithClickEvents: we prevent default here */}
<div
className={clsx(
'openapi-section-header-controls',
Expand Down
19 changes: 16 additions & 3 deletions packages/react-openapi/src/OpenAPICodeSample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {
import { OpenAPICodeSampleBody } from './OpenAPICodeSampleSelector';
import { ScalarApiButton } from './ScalarApiButton';
import { type CodeSampleGenerator, codeSampleGenerators } from './code-samples';
import { type OpenAPIContext, getOpenAPIClientContext } from './context';
import { generateMediaTypeExamples, generateSchemaExample } from './generateSchemaExample';
import { stringifyOpenAPI } from './stringifyOpenAPI';
import type { OpenAPIContext, OpenAPIOperationData } from './types';
import type { OpenAPIOperationData } from './types';
import { getDefaultServerURL } from './util/server';
import { checkIsReference } from './utils';

Expand Down Expand Up @@ -44,7 +45,12 @@ export function OpenAPICodeSample(props: {
}

return (
<OpenAPICodeSampleBody data={data} items={samples} selectIcon={context.icons.chevronDown} />
<OpenAPICodeSampleBody
context={getOpenAPIClientContext(context)}
data={data}
items={samples}
selectIcon={context.icons.chevronDown}
/>
);
}

Expand Down Expand Up @@ -215,7 +221,14 @@ function OpenAPICodeSampleFooter(props: {
) : (
<span />
)}
{!hideTryItPanel && <ScalarApiButton method={method} path={path} specUrl={specUrl} />}
{!hideTryItPanel && (
<ScalarApiButton
context={getOpenAPIClientContext(context)}
method={method}
path={path}
specUrl={specUrl}
/>
)}
</div>
);
}
Expand Down
18 changes: 14 additions & 4 deletions packages/react-openapi/src/OpenAPICodeSampleSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useStore } from 'zustand';
import { OpenAPIPath } from './OpenAPIPath';
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
import { StaticSection } from './StaticSection';
import type { OpenAPIClientContext } from './context';
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
import type { OpenAPIOperationData } from './types';

Expand All @@ -26,12 +27,13 @@ function OpenAPICodeSampleHeader(props: {
items: CodeSampleItem[];
data: OpenAPIOperationData;
selectIcon?: React.ReactNode;
context: OpenAPIClientContext;
}) {
const { data, items, selectIcon } = props;
const { data, items, selectIcon, context } = props;

return (
<>
<OpenAPIPath canCopy={false} withServer={false} data={data} />
<OpenAPIPath context={context} canCopy={false} withServer={false} data={data} />
{items.length > 1 ? (
<OpenAPISelect
icon={selectIcon}
Expand All @@ -56,8 +58,9 @@ export function OpenAPICodeSampleBody(props: {
items: CodeSampleItem[];
data: OpenAPIOperationData;
selectIcon?: React.ReactNode;
context: OpenAPIClientContext;
}) {
const { items, data, selectIcon } = props;
const { items, data, selectIcon, context } = props;
if (!items[0]) {
throw new Error('No items provided');
}
Expand All @@ -72,7 +75,14 @@ export function OpenAPICodeSampleBody(props: {

return (
<StaticSection
header={<OpenAPICodeSampleHeader selectIcon={selectIcon} data={data} items={items} />}
header={
<OpenAPICodeSampleHeader
context={context}
selectIcon={selectIcon}
data={data}
items={items}
/>
}
className="openapi-codesample"
>
<div id={selected.key as string} className="openapi-codesample-panel">
Expand Down
9 changes: 7 additions & 2 deletions packages/react-openapi/src/OpenAPICopyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import { useState } from 'react';
import { Button, type ButtonProps, Tooltip, TooltipTrigger } from 'react-aria-components';
import type { OpenAPIClientContext } from './context';
import { t } from './translate';

export function OpenAPICopyButton(
props: ButtonProps & {
value: string;
children: React.ReactNode;
context: OpenAPIClientContext;
label?: string;
/**
* Whether to show a tooltip.
Expand All @@ -15,7 +18,7 @@ export function OpenAPICopyButton(
withTooltip?: boolean;
}
) {
const { value, label, children, onPress, className, withTooltip = true } = props;
const { value, label, children, onPress, className, context, withTooltip = true } = props;
const [copied, setCopied] = useState(false);
const [isOpen, setIsOpen] = useState(false);

Expand Down Expand Up @@ -60,7 +63,9 @@ export function OpenAPICopyButton(
offset={4}
className="openapi-tooltip"
>
{copied ? 'Copied' : label || 'Copy to clipboard'}
{copied
? t(context.translation, 'copied')
: label || t(context.translation, 'copy_to_clipboard')}
</Tooltip>
</TooltipTrigger>
);
Expand Down
12 changes: 8 additions & 4 deletions packages/react-openapi/src/OpenAPIExample.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
import type { OpenAPIContext, OpenAPIUniversalContext } from './context';
import { json2xml } from './json2xml';
import { stringifyOpenAPI } from './stringifyOpenAPI';
import type { OpenAPIContext } from './types';
import { t } from './translate';

/**
* Display an example.
Expand All @@ -15,7 +16,7 @@ export function OpenAPIExample(props: {
const code = stringifyExample({ example, xml: syntax === 'xml' });

if (code === null) {
return <OpenAPIEmptyExample />;
return <OpenAPIEmptyExample context={context} />;
}

return context.renderCodeBlock({ code, syntax });
Expand All @@ -42,10 +43,13 @@ function stringifyExample(args: { example: OpenAPIV3.ExampleObject; xml: boolean
/**
* Empty response example.
*/
export function OpenAPIEmptyExample() {
export function OpenAPIEmptyExample(props: {
context: OpenAPIUniversalContext;
}) {
const { context } = props;
return (
<pre className="openapi-example-empty">
<p>No Content</p>
<p>{t(context.translation, 'no_content')}</p>
</pre>
);
}
Loading
Loading