Add Custom language option to OpenAPI code samples

OpenAPI code-sample blocks now show a language icon next to every option and add a "Custom language" entry that opens the assistant pre-filled (as a draft, not sent) with the current request staged as a reference and the prompt "Rewrite this in the following language: ", letting readers rewrite the request in any language.

- react-openapi: new OpenAPICodeSampleAssistant context the host app fills in; the selector renders per-language icons and the Custom action item (OpenAPISelect gains an action/onAction concept so it fires without changing the selection). Adds getCodeSampleIcon to the render context and 3 translation keys across all locales.
- gitbook: new setDraft controller method + inputDraft state so the chat input can be pre-filled without sending; OpenAPICodeSampleAIProvider bridges the GitBook assistant (logo, name, reference + draft action) into react-openapi, mounted once in SpaceLayout (covers site and embed); maps languages to icons.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Zeno Kapitein
2026-06-12 18:29:12 +02:00
parent 5b36c59061
commit 581eabe383
22 changed files with 430 additions and 20 deletions
@@ -0,0 +1,6 @@
---
"@gitbook/react-openapi": minor
"gitbook": patch
---
OpenAPI code samples: add a "Custom" option to the language dropdown that opens the assistant pre-filled (as a draft) to rewrite the request in any language, and show a language icon next to every option.
@@ -101,6 +101,12 @@ export type AIChatState = {
* References staged on the next user message.
*/
references: AIChatReference[];
/**
* Draft text to pre-fill into the chat input without sending it. Consumed by
* the input on the next render and then reset to `null`.
*/
inputDraft: string | null;
};
export type AIChatEvent =
@@ -124,6 +130,8 @@ export type AIChatController = {
close: () => void;
/** Post a message to the session */
postMessage: (input: { message: string }) => void;
/** Pre-fill the chat input with a draft message, without sending it */
setDraft: (value: string | null) => void;
/** Clear the conversation */
clear: () => void;
/** Stage a reference on the next message */
@@ -156,6 +164,7 @@ const globalState = zustand.create<AIChatState>(() => {
error: false,
initialQuery: null,
references: [],
inputDraft: null,
};
});
@@ -566,6 +575,7 @@ export function AIChatProvider(props: {
error: false,
initialQuery: null,
references: [],
inputDraft: null,
}));
// Reset ask parameter to empty string (keeps chat open but clears content)
@@ -615,6 +625,10 @@ export function AIChatProvider(props: {
notify(eventsRef.current.get('focus'), {});
}, []);
const onSetDraft = React.useCallback((value: string | null) => {
globalState.setState((state) => ({ ...state, inputDraft: value }));
}, []);
const onEvent = React.useCallback(
<T extends AIChatEvent['type']>(
event: T,
@@ -640,6 +654,7 @@ export function AIChatProvider(props: {
close: onClose,
clear: onClear,
postMessage: onPostMessage,
setDraft: onSetDraft,
addReference: onAddReference,
removeReference: onRemoveReference,
clearReferences: onClearReferences,
@@ -651,6 +666,7 @@ export function AIChatProvider(props: {
onClose,
onClear,
onPostMessage,
onSetDraft,
onAddReference,
onRemoveReference,
onClearReferences,
@@ -1,7 +1,7 @@
import { t, tString, useLanguage } from '@/intl/client';
import { tcls } from '@/lib/tailwind';
import { Icon } from '@gitbook/icons';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useAIChatController, useAIChatState } from '../AI/useAIChat';
import { HoverCard, HoverCardRoot, HoverCardTrigger } from '../primitives';
@@ -23,6 +23,16 @@ export function AIChatInput(props: {
const chatController = useAIChatController();
const inputRef = useRef<HTMLTextAreaElement>(null);
const [value, setValue] = useState('');
// Consume a draft requested via the controller (e.g. "rewrite this code sample"),
// pre-filling the input without sending it.
useEffect(() => {
if (chat.inputDraft != null) {
setValue(chat.inputDraft);
chatController.setDraft(null);
}
}, [chat.inputDraft, chatController]);
useEffect(() => {
if (chat.opened && !disabled && !loading) {
@@ -63,6 +73,8 @@ export function AIChatInput(props: {
sizing="large"
label="Assistant chat input"
placeholder={tString(language, 'ai_chat_input_placeholder')}
value={value}
onValueChange={setValue}
onSubmit={(val) => onSubmit(val as string)}
submitButton={{
size: 'small',
@@ -0,0 +1,62 @@
'use client';
import { useAIChatController, useAIConfig } from '@/components/AI';
import { AIChatIcon, getAIChatName } from '@/components/AIChat';
import { useLanguage } from '@/intl/client';
import { CustomizationAIMode } from '@gitbook/api';
import {
type OpenAPICodeSampleAssistant,
OpenAPICodeSampleAssistantProvider,
} from '@gitbook/react-openapi';
import { useMemo } from 'react';
/**
* Bridge the GitBook AI assistant into the OpenAPI code sample selector, so it can
* offer a "Custom" option that opens the assistant pre-filled to rewrite a sample.
*
* When the assistant is not enabled, the provider passes `null` and the option is hidden.
*/
export function OpenAPICodeSampleAIProvider(props: { children: React.ReactNode }) {
const { children } = props;
const config = useAIConfig();
const language = useLanguage();
const chatController = useAIChatController();
const assistant = useMemo<OpenAPICodeSampleAssistant | null>(() => {
if (config.aiMode !== CustomizationAIMode.Assistant) {
return null;
}
return {
label: config.assistantName ?? getAIChatName(language, config.trademark),
icon: (
<AIChatIcon
state="default"
trademark={config.trademark}
className="size-4 shrink-0"
/>
),
onRewrite: ({ id, code, syntax, label, prompt }) => {
if (!code.trim()) {
return;
}
chatController.addReference({
type: 'code-block',
id,
label: label ?? 'Code',
content: code,
syntax,
});
chatController.setDraft(prompt);
chatController.open();
chatController.focus();
},
};
}, [config.aiMode, config.assistantName, config.trademark, language, chatController]);
return (
<OpenAPICodeSampleAssistantProvider value={assistant}>
{children}
</OpenAPICodeSampleAssistantProvider>
);
}
@@ -1,5 +1,5 @@
import type { JSONDocument } from '@gitbook/api';
import { Icon } from '@gitbook/icons';
import { Icon, type IconName } from '@gitbook/icons';
import { type OpenAPIContextInput, checkIsValidLocale } from '@gitbook/react-openapi';
import type { BlockProps } from '../Block';
@@ -71,6 +71,9 @@ export function getOpenAPIContext(args: {
blockStyle="max-w-full"
/>
),
getCodeSampleIcon: (sample) => (
<Icon icon={getCodeSampleIconName(sample)} className="size-4 shrink-0" />
),
renderHeading: (headingProps) => (
<Heading
document={props.document}
@@ -101,3 +104,47 @@ export function getOpenAPIContext(args: {
locale,
};
}
/**
* Resolve the icon shown next to a code sample language in the selector.
* Falls back to a generic code icon for unknown languages.
*/
function getCodeSampleIconName(sample: { id?: string; syntax: string; label: string }): IconName {
const key = (sample.id ?? sample.syntax).toLowerCase();
switch (key) {
case 'http':
return 'globe';
case 'curl':
case 'bash':
case 'sh':
case 'shell':
case 'zsh':
return 'square-terminal';
case 'javascript':
case 'js':
case 'jsx':
case 'mjs':
case 'cjs':
case 'node':
return 'js';
case 'python':
case 'py':
return 'python';
case 'go':
case 'golang':
return 'golang';
case 'rust':
case 'rs':
return 'rust';
case 'php':
return 'php';
case 'java':
return 'java';
case 'swift':
return 'swift';
case 'json':
return 'brackets-curly';
default:
return 'code';
}
}
@@ -436,6 +436,11 @@
@apply flex flex-row items-center;
}
/* Single code sample (no dropdown): icon next to the language label. */
.openapi-codesample-label {
@apply flex items-center gap-1.5;
}
.openapi-response-media-types-examples-footer-content {
@apply flex flex-row items-center gap-2.5;
}
@@ -628,7 +633,7 @@ body:has(.openapi-select-popover) {
}
.openapi-select > button > span.react-aria-SelectValue {
@apply shrink truncate flex items-center;
@apply shrink truncate flex items-center gap-1.5;
}
.openapi-select > button > .react-aria-SelectValue [slot="description"] {
@@ -657,7 +662,7 @@ body:has(.openapi-select-popover) {
}
.openapi-select-item {
@apply text-sm flex items-center cursor-pointer px-1.5 overflow-hidden py-1 text-tint ring-0 border-none rounded straight-corners:rounded-none circular-corners:rounded-md !outline-none;
@apply text-sm flex items-center gap-2 cursor-pointer px-1.5 overflow-hidden py-1 text-tint ring-0 border-none rounded straight-corners:rounded-none circular-corners:rounded-md !outline-none;
@apply hover:bg-tint-hover hover:theme-gradient:bg-tint-12/1 hover:text-tint-strong contrast-more:hover:ring-1 contrast-more:hover:ring-inset contrast-more:hover:ring-current;
}
@@ -665,8 +670,13 @@ body:has(.openapi-select-popover) {
@apply flex flex-col gap-1 justify-start items-start;
}
/* The "Custom" AI rewrite option: assistant icon next to a stacked title + description. */
.openapi-select-item-custom .openapi-select-item-text {
@apply flex flex-col min-w-0 gap-0.5;
}
.openapi-select-item [slot="description"] {
@apply text-xs text-tint-subtle;
@apply text-xs text-tint-subtle whitespace-normal;
}
.openapi-select button .openapi-markdown,
@@ -17,6 +17,7 @@ import type { RenderAIMessageOptions } from '../AI';
import { AIChat } from '../AIChat';
import { AdaptiveVisitorContextProvider } from '../Adaptive';
import { Announcement } from '../Announcement';
import { OpenAPICodeSampleAIProvider } from '../DocumentView/OpenAPI/OpenAPICodeSampleAIProvider';
import { SpacesDropdown, TranslationsDropdown } from '../Header/SpacesDropdown';
import { InsightsProvider, VisitorProvider } from '../Insights';
import { SearchContainer, getSearchBaseProps } from '../Search';
@@ -91,7 +92,9 @@ export function SpaceLayoutServerContext(props: SpaceLayoutProps) {
>
<InsightsProvider enabled={withTracking} eventUrl={eventUrl.toString()}>
<AIChatProvider renderMessageOptions={aiChatRenderMessageOptions}>
{children}
<OpenAPICodeSampleAIProvider>
{children}
</OpenAPICodeSampleAIProvider>
</AIChatProvider>
</InsightsProvider>
</VisitorProvider>
@@ -165,11 +165,18 @@ function generateCodeSamples(props: {
);
return codeSampleGenerators.map((generator) => {
const icon = context.getCodeSampleIcon?.({
id: generator.id,
syntax: generator.syntax,
label: generator.label,
});
if (mediaTypeRendererFactories.length > 0) {
const renderers = mediaTypeRendererFactories.map((generate) => generate(generator));
return {
key: `default-${generator.id}`,
label: generator.label,
icon,
syntax: generator.syntax,
body: (
<OpenAPIMediaTypeExamplesBody
method={data.method}
@@ -186,6 +193,8 @@ function generateCodeSamples(props: {
return {
key: `default-${generator.id}`,
label: generator.label,
icon,
syntax: generator.syntax,
body: context.renderCodeBlock({
code: generator.generate({
url: { origin: serverUrlOrigin, path },
@@ -299,6 +308,8 @@ function getCustomCodeSamples(props: {
let customCodeSamples: null | Array<{
key: string;
label: string;
icon?: React.ReactNode;
syntax?: string;
body: React.ReactNode;
}> = null;
@@ -312,6 +323,11 @@ function getCustomCodeSamples(props: {
.map((sample, index) => ({
key: `custom-sample-${sample.lang}-${index}`,
label: sample.label || sample.lang,
icon: context.getCodeSampleIcon?.({
syntax: sample.lang,
label: sample.label || sample.lang,
}),
syntax: sample.lang,
body: context.renderCodeBlock({
code: sample.source,
syntax: sample.lang,
@@ -0,0 +1,59 @@
'use client';
import { createContext, useContext } from 'react';
/**
* A request to rewrite a code sample with the assistant.
*/
export interface OpenAPICodeSampleRewriteInput {
/** Stable identifier of the code sample, used to stage the reference. */
id: string;
/** Source code of the currently displayed code sample. */
code: string;
/** Syntax/language of the code sample (e.g. `bash`, `python`). */
syntax?: string;
/** Human-readable label of the code sample (e.g. `cURL`). */
label?: string;
/** Localized prompt to pre-fill in the assistant input as a draft. */
prompt: string;
}
/**
* Capability provided by the host app to let the assistant rewrite a code sample.
* When present, the code sample selector exposes a "Custom" option.
*/
export interface OpenAPICodeSampleAssistant {
/** Display name of the assistant (e.g. `GitBook Assistant`). */
label: string;
/** Logo/icon of the assistant, displayed next to the "Custom" option. */
icon: React.ReactNode;
/**
* Open the assistant with the given code sample staged as a reference and the
* prompt pre-filled as a draft (not sent).
*/
onRewrite: (input: OpenAPICodeSampleRewriteInput) => void;
}
const OpenAPICodeSampleAssistantContext = createContext<OpenAPICodeSampleAssistant | null>(null);
/**
* Provide the assistant capability to the code sample selector.
* Pass `null` to disable the "Custom" rewrite option.
*/
export function OpenAPICodeSampleAssistantProvider(props: {
value: OpenAPICodeSampleAssistant | null;
children: React.ReactNode;
}) {
return (
<OpenAPICodeSampleAssistantContext.Provider value={props.value}>
{props.children}
</OpenAPICodeSampleAssistantContext.Provider>
);
}
/**
* Access the assistant capability, or `null` when no assistant is available.
*/
export function useOpenAPICodeSampleAssistant() {
return useContext(OpenAPICodeSampleAssistantContext);
}
@@ -1,15 +1,25 @@
'use client';
import { useCallback } from 'react';
import { Fragment, useCallback, useRef } from 'react';
import type { Key } from 'react-aria';
import { Separator, Text } from 'react-aria-components';
import { useStore } from 'zustand';
import { useOpenAPICodeSampleAssistant } from './OpenAPICodeSampleAssistant';
import { OpenAPIPath } from './OpenAPIPath';
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
import { StaticSection } from './StaticSection';
import type { OpenAPIClientContext } from './context';
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
import { tString } from './translate';
import type { OpenAPIOperationData } from './types';
/**
* Key of the synthetic "Custom" option that opens the assistant to rewrite the
* code sample. Chosen to avoid colliding with generated (`default-*`) or custom
* (`custom-sample-*`) sample keys.
*/
const CUSTOM_CODE_SAMPLE_KEY = 'gitbook-ai-rewrite';
function useCodeSampleState(initialKey: Key = 'default') {
const store = useStore(getOrCreateStoreByKey('codesample', initialKey));
return {
@@ -19,36 +29,104 @@ function useCodeSampleState(initialKey: Key = 'default') {
}
type CodeSampleItem = OpenAPISelectItem & {
icon?: React.ReactNode;
syntax?: string;
body: React.ReactNode;
footer?: React.ReactNode;
};
function OpenAPICodeSampleHeader(props: {
items: CodeSampleItem[];
selected: CodeSampleItem;
data: OpenAPIOperationData;
selectIcon?: React.ReactNode;
context: OpenAPIClientContext;
getSelectedCode: () => string;
}) {
const { data, items, selectIcon, context } = props;
const { data, items, selected, selectIcon, context, getSelectedCode } = props;
const assistant = useOpenAPICodeSampleAssistant();
// When an assistant is available, append a "Custom" option that opens it
// pre-filled to rewrite the currently displayed sample in any language.
const customItem: CodeSampleItem | null = assistant
? {
key: CUSTOM_CODE_SAMPLE_KEY,
label: tString(context.translation, 'code_sample_custom'),
action: true,
body: null,
}
: null;
const allItems = customItem ? [...items, customItem] : items;
const onCustomRewrite = () => {
if (!assistant) {
return;
}
const code = getSelectedCode();
if (!code.trim()) {
return;
}
assistant.onRewrite({
id: `${context.blockKey ?? 'openapi'}-${String(selected.key)}`,
code,
syntax: selected.syntax,
label: typeof selected.label === 'string' ? selected.label : undefined,
prompt: tString(context.translation, 'code_sample_rewrite_prompt'),
});
};
return (
<>
<OpenAPIPath context={context} canCopy={false} withServer={false} data={data} />
{items.length > 1 ? (
{allItems.length > 1 ? (
<OpenAPISelect
icon={selectIcon}
items={items}
items={allItems}
stateKey="codesample"
placement="bottom end"
onAction={onCustomRewrite}
>
{items.map((item) => (
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
{item.label}
</OpenAPISelectItem>
))}
{allItems.map((item) =>
item.key === CUSTOM_CODE_SAMPLE_KEY && assistant ? (
<Fragment key={item.key}>
<Separator className="border-tint-subtle border-t" />
<OpenAPISelectItem
id={item.key}
value={item}
textValue={tString(context.translation, 'code_sample_custom')}
className="openapi-select-item-custom"
>
{assistant.icon}
<span className="openapi-select-item-text">
<Text slot="label">{item.label}</Text>
<Text slot="description">
{tString(
context.translation,
'code_sample_custom_description',
assistant.label
)}
</Text>
</span>
</OpenAPISelectItem>
</Fragment>
) : (
<OpenAPISelectItem
key={item.key}
id={item.key}
value={item}
textValue={typeof item.label === 'string' ? item.label : undefined}
>
{item.icon ?? null}
<Text slot="label">{item.label}</Text>
</OpenAPISelectItem>
)
)}
</OpenAPISelect>
) : items[0] ? (
<span className="openapi-codesample-label">{items[0].label}</span>
<span className="openapi-codesample-label">
{items[0].icon ?? null}
{items[0].label}
</span>
) : null}
</>
);
@@ -66,6 +144,7 @@ export function OpenAPICodeSampleBody(props: {
}
const state = useCodeSampleState(items[0]?.key);
const panelRef = useRef<HTMLDivElement>(null);
const selected = items.find((item) => item.key === state.key) || items[0];
@@ -81,14 +160,48 @@ export function OpenAPICodeSampleBody(props: {
selectIcon={selectIcon}
data={data}
items={items}
selected={selected}
getSelectedCode={() => readPanelCodeText(panelRef.current)}
/>
}
className="openapi-codesample"
>
<div id={selected.key as string} className="openapi-codesample-panel">
<div ref={panelRef} id={selected.key as string} className="openapi-codesample-panel">
{selected.body ? selected.body : null}
{selected.footer ? selected.footer : null}
</div>
</StaticSection>
);
}
/**
* Extract the plain code text of the code block currently displayed in a panel.
* Mirrors the host code block rendering, where empty lines are represented with
* a span of class "ew".
*/
function readPanelCodeText(panel: HTMLElement | null): string {
const code = panel?.querySelector('code');
if (!code) {
return '';
}
let text = '';
const iterate = (node: Node) => {
if (node instanceof HTMLBRElement) {
text += '\n';
} else if (node instanceof HTMLSpanElement) {
if (node.classList.contains('ew')) {
text += '\n';
} else {
text += node.innerText;
}
} else if (node instanceof HTMLElement) {
node.childNodes.forEach(iterate);
} else {
text += node.textContent ?? '';
}
};
iterate(code);
return text;
}
+28 -3
View File
@@ -20,6 +20,11 @@ import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
export type OpenAPISelectItem = {
key: Key;
label: string | React.ReactNode;
/**
* If `true`, selecting this item runs `onAction` instead of changing the selection,
* leaving the current selection unchanged (e.g. an item that opens a dialog).
*/
action?: boolean;
};
interface OpenAPISelectProps<T extends OpenAPISelectItem> extends Omit<SelectProps<T>, 'children'> {
@@ -31,6 +36,10 @@ interface OpenAPISelectProps<T extends OpenAPISelectItem> extends Omit<SelectPro
* Icon to display in the select button.
*/
icon?: React.ReactNode | null;
/**
* Called when an item flagged with `action` is selected. The selection is not changed.
*/
onAction?: (key: Key) => void;
}
export function useSelectState(stateKey = 'select-state', initialKey: Key = 'default') {
@@ -42,8 +51,19 @@ export function useSelectState(stateKey = 'select-state', initialKey: Key = 'def
}
export function OpenAPISelect<T extends OpenAPISelectItem>(props: OpenAPISelectProps<T>) {
const { icon, items, children, className, placement, stateKey, value, onChange, defaultValue } =
props;
const {
icon,
items,
children,
className,
placement,
stateKey,
value,
onChange,
defaultValue,
onAction,
...selectProps
} = props;
const state = useSelectState(stateKey, defaultValue ?? items[0]?.key);
@@ -52,9 +72,14 @@ export function OpenAPISelect<T extends OpenAPISelectItem>(props: OpenAPISelectP
return (
<Select
aria-label="OpenAPI Select"
{...props}
{...selectProps}
value={value ?? selected?.key}
onChange={(key) => {
// Action items trigger a side effect without changing the selection.
if (key !== null && items.find((item) => item.key === key)?.action) {
onAction?.(key);
return;
}
onChange?.(key);
state.setKey(key);
}}
+13
View File
@@ -72,6 +72,19 @@ export interface OpenAPIContext
*/
renderDocument: (props: { document: object }) => React.ReactNode;
/**
* Render the icon associated with a code sample language, displayed in the
* code sample language selector. Optional: when omitted, options render without an icon.
*/
getCodeSampleIcon?: (sample: {
/** Identifier of the built-in generator (e.g. `curl`, `javascript`), if any. */
id?: string;
/** Syntax/language of the sample (e.g. `bash`, `python`). */
syntax: string;
/** Human-readable label of the sample (e.g. `cURL`). */
label: string;
}) => React.ReactNode;
/**
* Public specification URL, used by Scalar's "Test it" modal.
* When null, the "Test it" button is hidden.
+1
View File
@@ -2,6 +2,7 @@ export * from './schemas';
export * from './OpenAPIOperation';
export * from './OpenAPIWebhook';
export * from './OpenAPIOperationContext';
export * from './OpenAPICodeSampleAssistant';
export * from './OpenAPIPrefillContextProvider';
export * from './resolveOpenAPIOperation';
export * from './resolveOpenAPIWebhook';
@@ -43,4 +43,7 @@ export const de = {
or: 'oder',
and: 'und',
possible_values: 'Mögliche Werte',
code_sample_custom: 'Benutzerdefinierte Sprache',
code_sample_custom_description: 'Mit ${1} umschreiben',
code_sample_rewrite_prompt: 'Schreibe dies in die folgende Sprache um: ',
};
@@ -43,4 +43,7 @@ export const en = {
properties: 'Properties',
or: 'or',
and: 'and',
code_sample_custom: 'Custom language',
code_sample_custom_description: 'Rewrite with ${1}',
code_sample_rewrite_prompt: 'Rewrite this in the following language: ',
};
@@ -43,4 +43,7 @@ export const es = {
or: 'o',
and: 'y',
possible_values: 'Valores posibles',
code_sample_custom: 'Lenguaje personalizado',
code_sample_custom_description: 'Reescribir con ${1}',
code_sample_rewrite_prompt: 'Reescribe esto en el siguiente lenguaje: ',
};
@@ -43,4 +43,7 @@ export const fr = {
or: 'ou',
and: 'et',
possible_values: 'Valeurs possibles',
code_sample_custom: 'Langage personnalisé',
code_sample_custom_description: 'Réécrire avec ${1}',
code_sample_rewrite_prompt: 'Réécrivez ceci dans le langage suivant : ',
};
@@ -43,4 +43,7 @@ export const ja = {
or: 'または',
and: 'および',
possible_values: '可能な値',
code_sample_custom: 'カスタム言語',
code_sample_custom_description: '${1} で書き換える',
code_sample_rewrite_prompt: '次の言語に書き換えてください: ',
};
@@ -43,4 +43,7 @@ export const nl = {
or: 'of',
and: 'en',
possible_values: 'Mogelijke waarden',
code_sample_custom: 'Aangepaste taal',
code_sample_custom_description: 'Herschrijven met ${1}',
code_sample_rewrite_prompt: 'Herschrijf dit in de volgende taal: ',
};
@@ -43,4 +43,7 @@ export const no = {
or: 'eller',
and: 'og',
possible_values: 'Mulige verdier',
code_sample_custom: 'Egendefinert språk',
code_sample_custom_description: 'Skriv om med ${1}',
code_sample_rewrite_prompt: 'Skriv om dette til følgende språk: ',
};
@@ -43,4 +43,7 @@ export const pt_br = {
or: 'ou',
and: 'e',
possible_values: 'Valores possíveis',
code_sample_custom: 'Linguagem personalizada',
code_sample_custom_description: 'Reescrever com ${1}',
code_sample_rewrite_prompt: 'Reescreva isto na seguinte linguagem: ',
};
@@ -43,4 +43,7 @@ export const zh = {
or: '或',
and: '和',
possible_values: '可能的值',
code_sample_custom: '自定义语言',
code_sample_custom_description: '使用 ${1} 重写',
code_sample_rewrite_prompt: '用以下语言重写: ',
};