@umami/shiso 1.17.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/chunks/App.js +147 -160
  2. package/dist/chunks/architectureDiagram-5GKGNRK7.js +1 -1
  3. package/dist/chunks/chunk-GMAD6QVW.js +1 -1
  4. package/dist/chunks/cose-bilkent-JH36ORCC.js +1 -1
  5. package/dist/chunks/dist.js +1 -1
  6. package/dist/chunks/docs.js +668 -22
  7. package/dist/chunks/ganttDiagram-EL5Y4UJY.js +1 -1
  8. package/dist/chunks/src.js +1 -1
  9. package/dist/entry-client.js +1 -1
  10. package/dist/entry-server.js +1 -1
  11. package/docs.schema.json +4 -0
  12. package/package.json +1 -1
  13. package/scripts/check-content.mjs +2 -0
  14. package/scripts/load-shiso-config.mjs +30 -1
  15. package/src/App.tsx +32 -22
  16. package/src/components/CodeBlock.tsx +3 -1
  17. package/src/components/Docs.tsx +3 -1
  18. package/src/components/Footer.tsx +3 -1
  19. package/src/components/Header.tsx +4 -3
  20. package/src/components/LanguageSwitcher.tsx +17 -4
  21. package/src/components/OpenApiOperation.tsx +22 -15
  22. package/src/components/PageActions.tsx +8 -7
  23. package/src/components/docs/Changelog.tsx +5 -5
  24. package/src/components/docs/CodeGroup.tsx +3 -1
  25. package/src/components/docs/Mermaid.tsx +8 -6
  26. package/src/components/docs/PropertiesTable.tsx +11 -6
  27. package/src/components/docs/Tabs.tsx +3 -1
  28. package/src/components/docs/Tree.tsx +3 -1
  29. package/src/components/docs/ZoomableImage.tsx +4 -2
  30. package/src/components/ui/dialog.tsx +7 -2
  31. package/src/components/ui/sheet.tsx +5 -2
  32. package/src/lib/label-context.tsx +7 -0
  33. package/src/lib/labels.ts +37 -0
  34. package/src/lib/openapi.ts +21 -8
  35. package/src/lib/site-config.ts +66 -2
  36. package/src/lib/site-model.ts +2 -32
  37. package/src/lib/standalone-pages.ts +15 -1
  38. package/src/lib/translations/de.json +74 -0
  39. package/src/lib/translations/en.json +74 -0
  40. package/src/lib/translations/es.json +74 -0
  41. package/src/lib/translations/fr.json +74 -0
  42. package/src/lib/translations/ja.json +74 -0
  43. package/src/lib/translations/zh-Hans.json +74 -0
  44. package/src/lib/translations/zh-Hant.json +74 -0
  45. package/src/lib/types.ts +20 -32
  46. package/types/config.d.ts +7 -1
  47. package/types/labels.d.ts +76 -0
@@ -1,4 +1,5 @@
1
1
  import type { ReactNode } from 'react';
2
+ import { useLabels } from '@/lib/label-context';
2
3
  import { Badge } from './Badge';
3
4
  import { decodeHtmlEntities, toElementArray } from './utils';
4
5
 
@@ -27,6 +28,7 @@ function displayValue(value: PropertiesTableRowProps['type']): string | undefine
27
28
  }
28
29
 
29
30
  export function PropertiesTable({ children }: PropertiesTableProps) {
31
+ const labels = useLabels();
30
32
  const rows = toElementArray<PropertiesTableRowProps>(children);
31
33
 
32
34
  if (!rows.length) {
@@ -40,9 +42,9 @@ export function PropertiesTable({ children }: PropertiesTableProps) {
40
42
  <table className="m-0 table-fixed [overflow-wrap:anywhere]">
41
43
  <thead>
42
44
  <tr>
43
- <th className="w-[30%]">Name</th>
44
- <th className="w-[15%]">Type</th>
45
- <th>Description</th>
45
+ <th className="w-[30%]">{labels.fieldName}</th>
46
+ <th className="w-[15%]">{labels.fieldType}</th>
47
+ <th>{labels.fieldDescription}</th>
46
48
  </tr>
47
49
  </thead>
48
50
  <tbody>{rows}</tbody>
@@ -60,6 +62,7 @@ function PropertiesTableRow({
60
62
  children,
61
63
  details,
62
64
  }: PropertiesTableRowProps) {
65
+ const labels = useLabels();
63
66
  const defaultValue = displayValue(value);
64
67
 
65
68
  return (
@@ -70,17 +73,19 @@ function PropertiesTableRow({
70
73
  {name}
71
74
  {required ? (
72
75
  <Badge size="sm" tone="primary">
73
- required
76
+ {labels.fieldRequired}
74
77
  </Badge>
75
78
  ) : null}
76
- {deprecated ? <Badge size="sm">deprecated</Badge> : null}
79
+ {deprecated ? <Badge size="sm">{labels.fieldDeprecated}</Badge> : null}
77
80
  </span>
78
81
  </td>
79
82
  <td>{displayValue(type)}</td>
80
83
  <td className="[&_p]:m-0">
81
84
  {children}
82
85
  {defaultValue ? (
83
- <span className="mt-1 block text-muted-foreground">Default: {defaultValue}</span>
86
+ <span className="mt-1 block text-muted-foreground">
87
+ {labels.fieldDefault} {defaultValue}
88
+ </span>
84
89
  ) : null}
85
90
  </td>
86
91
  </tr>
@@ -1,6 +1,7 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import { useEffect, useMemo, useState } from 'react';
3
3
  import { TabsContent, TabsList, Tabs as TabsPrimitive, TabsTrigger } from '@/components/ui/tabs';
4
+ import { useLabels } from '@/lib/label-context';
4
5
  import { slugify, toElementArray } from './utils';
5
6
 
6
7
  interface TabChildProps {
@@ -18,6 +19,7 @@ export interface TabsProps {
18
19
  }
19
20
 
20
21
  export function Tabs({ children, group }: TabsProps) {
22
+ const labels = useLabels();
21
23
  const tabs = useMemo(() => {
22
24
  const counts = new Map<string, number>();
23
25
 
@@ -94,7 +96,7 @@ export function Tabs({ children, group }: TabsProps) {
94
96
  <TabsList
95
97
  variant="line"
96
98
  className="w-full justify-start gap-5 overflow-x-auto overflow-y-hidden p-0 shadow-[inset_0_-1px_0_var(--border)] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
97
- aria-label="Content tabs"
99
+ aria-label={labels.contentTabs}
98
100
  >
99
101
  {tabs.map(tab => (
100
102
  <TabsTrigger
@@ -14,6 +14,7 @@ import {
14
14
  Folder as FolderIcon,
15
15
  FolderOpen,
16
16
  } from '@/components/icons';
17
+ import { useLabels } from '@/lib/label-context';
17
18
  import { styles } from './styles';
18
19
 
19
20
  export interface TreeFolderProps {
@@ -208,6 +209,7 @@ function visibleFocusables(root: HTMLElement): HTMLElement[] {
208
209
  }
209
210
 
210
211
  function TreeRoot({ children, style, className }: TreeProps) {
212
+ const labels = useLabels();
211
213
  const rootRef = useRef<HTMLDivElement>(null);
212
214
  const searchRef = useRef({ text: '', timer: 0 });
213
215
 
@@ -334,7 +336,7 @@ function TreeRoot({ children, style, className }: TreeProps) {
334
336
  onKeyDown={onKeyDown}
335
337
  >
336
338
  {/* biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: tree is the required ARIA role for this widget */}
337
- <ul role="tree" aria-label="File tree" className={styles.treeList}>
339
+ <ul role="tree" aria-label={labels.fileTree} className={styles.treeList}>
338
340
  {content}
339
341
  </ul>
340
342
  </div>
@@ -1,5 +1,6 @@
1
1
  import type { ImgHTMLAttributes } from 'react';
2
2
  import { Dialog, DialogContent, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
3
+ import { useLabels } from '@/lib/label-context';
3
4
  import { cn } from '@/lib/utils';
4
5
 
5
6
  export interface ZoomableImageProps extends ImgHTMLAttributes<HTMLImageElement> {
@@ -13,6 +14,7 @@ export function ZoomableImage({
13
14
  src,
14
15
  ...props
15
16
  }: ZoomableImageProps) {
17
+ const labels = useLabels();
16
18
  const image = (
17
19
  <img
18
20
  {...props}
@@ -26,7 +28,7 @@ export function ZoomableImage({
26
28
  return image;
27
29
  }
28
30
 
29
- const label = alt ? `View “${alt}” full size` : 'View image full size';
31
+ const label = alt ? labels.viewNamedImage.replace('{alt}', () => alt) : labels.viewImage;
30
32
 
31
33
  return (
32
34
  <Dialog>
@@ -47,7 +49,7 @@ export function ZoomableImage({
47
49
  overlayClassName="bg-black/80 supports-backdrop-filter:backdrop-blur-sm"
48
50
  className="flex max-h-[calc(100vh-2rem)] w-auto max-w-[calc(100vw-2rem)] items-center justify-center bg-transparent p-0 ring-0 shadow-none sm:max-w-[calc(100vw-2rem)]"
49
51
  >
50
- <DialogTitle className="sr-only">{alt || 'Full-size image'}</DialogTitle>
52
+ <DialogTitle className="sr-only">{alt || labels.fullSizeImage}</DialogTitle>
51
53
  <img
52
54
  src={src}
53
55
  srcSet={props.srcSet}
@@ -2,6 +2,7 @@ import { Dialog as DialogPrimitive } from '@base-ui/react/dialog';
2
2
  import { XIcon } from 'lucide-react';
3
3
  import type * as React from 'react';
4
4
  import { Button } from '@/components/ui/button';
5
+ import { useLabels } from '@/lib/label-context';
5
6
  import { cn } from '@/lib/utils';
6
7
 
7
8
  function Dialog({ ...props }: DialogPrimitive.Root.Props) {
@@ -43,6 +44,7 @@ function DialogContent({
43
44
  showCloseButton?: boolean;
44
45
  overlayClassName?: string;
45
46
  }) {
47
+ const labels = useLabels();
46
48
  return (
47
49
  <DialogPortal>
48
50
  <DialogOverlay className={overlayClassName} />
@@ -61,7 +63,7 @@ function DialogContent({
61
63
  render={<Button variant="ghost" className="absolute top-2 right-2" size="icon-sm" />}
62
64
  >
63
65
  <XIcon />
64
- <span className="sr-only">Close</span>
66
+ <span className="sr-only">{labels.close}</span>
65
67
  </DialogPrimitive.Close>
66
68
  )}
67
69
  </DialogPrimitive.Popup>
@@ -83,6 +85,7 @@ function DialogFooter({
83
85
  }: React.ComponentProps<'div'> & {
84
86
  showCloseButton?: boolean;
85
87
  }) {
88
+ const labels = useLabels();
86
89
  return (
87
90
  <div
88
91
  data-slot="dialog-footer"
@@ -94,7 +97,9 @@ function DialogFooter({
94
97
  >
95
98
  {children}
96
99
  {showCloseButton && (
97
- <DialogPrimitive.Close render={<Button variant="outline" />}>Close</DialogPrimitive.Close>
100
+ <DialogPrimitive.Close render={<Button variant="outline" />}>
101
+ {labels.close}
102
+ </DialogPrimitive.Close>
98
103
  )}
99
104
  </div>
100
105
  );
@@ -1,4 +1,6 @@
1
- 'use client';
1
+ import { useLabels } from '@/lib/label-context';
2
+
3
+ ('use client');
2
4
 
3
5
  import { Dialog as SheetPrimitive } from '@base-ui/react/dialog';
4
6
  import { XIcon } from 'lucide-react';
@@ -45,6 +47,7 @@ function SheetContent({
45
47
  side?: 'top' | 'right' | 'bottom' | 'left';
46
48
  showCloseButton?: boolean;
47
49
  }) {
50
+ const labels = useLabels();
48
51
  return (
49
52
  <SheetPortal>
50
53
  <SheetOverlay />
@@ -64,7 +67,7 @@ function SheetContent({
64
67
  render={<Button variant="ghost" className="absolute top-3 right-3" size="icon-sm" />}
65
68
  >
66
69
  <XIcon />
67
- <span className="sr-only">Close</span>
70
+ <span className="sr-only">{labels.close}</span>
68
71
  </SheetPrimitive.Close>
69
72
  )}
70
73
  </SheetPrimitive.Popup>
@@ -0,0 +1,7 @@
1
+ import { createContext, useContext } from 'react';
2
+ import { englishLabels } from '@/lib/labels';
3
+
4
+ export const LabelContext = createContext(englishLabels);
5
+ export function useLabels() {
6
+ return useContext(LabelContext);
7
+ }
@@ -0,0 +1,37 @@
1
+ import { resolveLocale } from '@/lib/locale';
2
+ import de from '@/lib/translations/de.json';
3
+ import en from '@/lib/translations/en.json';
4
+ import es from '@/lib/translations/es.json';
5
+ import fr from '@/lib/translations/fr.json';
6
+ import ja from '@/lib/translations/ja.json';
7
+ import zhHans from '@/lib/translations/zh-Hans.json';
8
+ import zhHant from '@/lib/translations/zh-Hant.json';
9
+ import type { ThemeLabels, Translations } from '@/lib/types';
10
+
11
+ export const englishLabels: ThemeLabels = en;
12
+ const dictionaries: Record<string, ThemeLabels> = {
13
+ en,
14
+ de,
15
+ es,
16
+ fr,
17
+ ja,
18
+ 'zh-Hans': zhHans,
19
+ 'zh-Hant': zhHant,
20
+ };
21
+
22
+ /** Merge from least to most specific; overrides win at each locale level. */
23
+ export function resolveLabels(locale: string, overrides: Translations = {}): ThemeLabels {
24
+ const tag = new Intl.Locale(resolveLocale(locale, 'en'));
25
+ const script = tag.script || (tag.language === 'zh' ? tag.maximize().script : undefined);
26
+ const candidates = new Set([
27
+ 'en',
28
+ tag.language,
29
+ ...(script ? [`${tag.language}-${script}`] : []),
30
+ tag.baseName,
31
+ ]);
32
+ const labels = { ...englishLabels };
33
+ for (const candidate of candidates) {
34
+ Object.assign(labels, dictionaries[candidate], overrides[candidate]);
35
+ }
36
+ return labels;
37
+ }
@@ -1,3 +1,4 @@
1
+ import { englishLabels } from '@/lib/labels';
1
2
  /**
2
3
  * Runtime helpers for OpenAPI reference pages. Operation data is produced at
3
4
  * build time by scripts/generate-openapi.mjs and reaches the client through
@@ -7,7 +8,7 @@
7
8
  import type { BadgeColor } from '@/components/docs/Badge';
8
9
  import { OPENAPI_OPERATIONS } from '@/lib/openapi.generated';
9
10
  import { createSlugger } from '@/lib/slug';
10
- import type { NormalizedOperation, TocEntry } from '@/lib/types';
11
+ import type { NormalizedOperation, ThemeLabels, TocEntry } from '@/lib/types';
11
12
 
12
13
  export const METHOD_COLORS: Record<string, BadgeColor> = {
13
14
  GET: 'green',
@@ -46,12 +47,15 @@ export function hasParameters(operation: NormalizedOperation): boolean {
46
47
  }
47
48
 
48
49
  /** Parameter groups shared by the renderer and table of contents. */
49
- export function operationParameterSections(operation: NormalizedOperation) {
50
+ export function operationParameterSections(
51
+ operation: NormalizedOperation,
52
+ labels: ThemeLabels = englishLabels,
53
+ ) {
50
54
  const groups = [
51
- { location: 'header', name: 'Headers' },
52
- { location: 'path', name: 'Path parameters' },
53
- { location: 'query', name: 'Query parameters' },
54
- { location: 'cookie', name: 'Cookie parameters' },
55
+ { location: 'header', name: labels.apiHeaders },
56
+ { location: 'path', name: labels.apiPathParameters },
57
+ { location: 'query', name: labels.apiQueryParameters },
58
+ { location: 'cookie', name: labels.apiCookieParameters },
55
59
  ] as const;
56
60
 
57
61
  return groups.filter(
@@ -66,7 +70,10 @@ export function operationParameterSections(operation: NormalizedOperation) {
66
70
  * truth for section ids: the component, the table of contents, the content
67
71
  * checker, and the search indexer all derive their anchors from these labels.
68
72
  */
69
- export function operationSections(operation: NormalizedOperation): TocEntry[] {
73
+ export function operationSections(
74
+ operation: NormalizedOperation,
75
+ labels: ThemeLabels = englishLabels,
76
+ ): TocEntry[] {
70
77
  const slugger = createSlugger();
71
78
  const names = [
72
79
  ...operationParameterSections(operation).map(section => section.name),
@@ -75,5 +82,11 @@ export function operationSections(operation: NormalizedOperation): TocEntry[] {
75
82
  operation.samples.length ? 'Code samples' : undefined,
76
83
  ].filter((name): name is string => Boolean(name));
77
84
 
78
- return names.map(name => ({ name, id: slugger.slug(name), size: 2 }));
85
+ const translated = [
86
+ ...operationParameterSections(operation, labels).map(section => section.name),
87
+ ...(operation.requestBody ? [labels.apiRequestBody] : []),
88
+ ...(operation.responses.length ? [labels.apiResponses] : []),
89
+ ...(operation.samples.length ? [labels.apiCodeSamples] : []),
90
+ ];
91
+ return names.map((name, index) => ({ name: translated[index], id: slugger.slug(name), size: 2 }));
79
92
  }
@@ -8,6 +8,7 @@ import {
8
8
  getPageByPathname as getSitePageByPathname,
9
9
  normalizeDocsSite,
10
10
  } from '@/lib/docs-config';
11
+ import { resolveLabels } from '@/lib/labels';
11
12
  import { getTextDirection, resolveLocale } from '@/lib/locale';
12
13
  import { DOCS_PREFIX, stripBase } from '@/lib/paths';
13
14
  import { resolveSiteModel } from '@/lib/site-model';
@@ -53,10 +54,60 @@ export function getStandalonePage(pathname: string): StandalonePage | null {
53
54
  return getStandalonePageByPathname(standalonePages, stripBase(pathname));
54
55
  }
55
56
 
56
- /** Scope that owns the current pathname; the default scope for unknown paths. */
57
+ /** Landing scope of a language: its default version. */
58
+ function getLanguageScope(language: string): DocsScope | undefined {
59
+ return docsSite.scopes.find(scope => scope.language === language && scope.isLanguageDefault);
60
+ }
61
+
62
+ /**
63
+ * Scope that owns the current pathname. A standalone page tagged with a
64
+ * `language` belongs to that language's landing scope; unknown paths and
65
+ * untagged standalone pages belong to the default scope.
66
+ */
57
67
  export function getScopeByPathname(pathname: string): DocsScope {
58
68
  const page = getPageByPathname(pathname);
59
- return page ? getScopeForPage(docsSite, page) : getDefaultScope(docsSite);
69
+
70
+ if (page) {
71
+ return getScopeForPage(docsSite, page);
72
+ }
73
+
74
+ const standalone = getStandalonePage(pathname);
75
+ const languageScope = standalone?.language ? getLanguageScope(standalone.language) : undefined;
76
+
77
+ return languageScope || getDefaultScope(docsSite);
78
+ }
79
+
80
+ /**
81
+ * The same standalone page in another language, matched by `key`. Untagged
82
+ * pages count as the default scope's language.
83
+ */
84
+ export function getStandaloneCounterpart(
85
+ page: StandalonePage,
86
+ language: string,
87
+ ): StandalonePage | null {
88
+ const defaultLanguage = getDefaultScope(docsSite).language;
89
+
90
+ return (
91
+ standalonePages.find(
92
+ candidate =>
93
+ candidate.key === page.key && (candidate.language || defaultLanguage) === language,
94
+ ) || null
95
+ );
96
+ }
97
+
98
+ /**
99
+ * Home link for the current pathname: the "/" standalone page in the current
100
+ * language when one exists, else "/", else the docs home when nothing owns "/".
101
+ */
102
+ export function getHomeHref(pathname: string): string {
103
+ const root = standalonePages.find(page => page.path === '/');
104
+
105
+ if (!root) {
106
+ return docsHomeUrl;
107
+ }
108
+
109
+ const language = getScopeByPathname(pathname).language;
110
+ return (language && getStandaloneCounterpart(root, language)?.path) || '/';
60
111
  }
61
112
 
62
113
  /** Document language and direction for a pathname, from its scope's language. */
@@ -65,6 +116,19 @@ export function getLocaleByPathname(pathname: string): { lang: string; dir: 'ltr
65
116
  return { lang, dir: getTextDirection(lang) };
66
117
  }
67
118
 
119
+ /** Resolve UI text on every route, including standalone pages and language switches. */
120
+ export function getSiteModelByPathname(pathname: string) {
121
+ const { lang } = getLocaleByPathname(pathname);
122
+ const labels = resolveLabels(lang, shisoConfig.translations);
123
+ const customPrompt =
124
+ typeof siteConfig.search === 'object' ? siteConfig.search.prompt?.trim() : undefined;
125
+ return {
126
+ ...siteModel,
127
+ labels,
128
+ search: { ...siteModel.search, prompt: customPrompt || labels.searchPlaceholder },
129
+ };
130
+ }
131
+
68
132
  export const siteName = siteModel.name;
69
133
 
70
134
  /** Trailing-slash-insensitive route key for redirect matching. */
@@ -1,4 +1,5 @@
1
1
  import { resolveCodeBlockConfig } from '@/lib/code-blocks';
2
+ import { resolveLabels, englishLabels as SHISO_THEME_LABELS } from '@/lib/labels';
2
3
  import { toAbsoluteUrl, toHref } from '@/lib/paths';
3
4
  import { resolveSearchConfig } from '@/lib/search/config';
4
5
  import type {
@@ -18,37 +19,6 @@ import type {
18
19
  ThemeLabels,
19
20
  } from '@/lib/types';
20
21
 
21
- const SHISO_THEME_LABELS: ThemeLabels = {
22
- menu: 'Menu',
23
- documentationNavigation: 'Documentation navigation',
24
- sections: 'Sections',
25
- tableOfContents: 'On this page',
26
- tableOfContentsNavigation: 'Table of contents',
27
- searchTitle: 'Search',
28
- searching: 'Searching...',
29
- searchUnavailable: 'Search unavailable',
30
- noResults: 'No results',
31
- lastUpdated: 'Last updated on',
32
- relatedTopics: 'Related topics',
33
- previousPage: 'Previous',
34
- nextPage: 'Next',
35
- notFound: 'Page not found',
36
- dismissBanner: 'Dismiss banner',
37
- toggleTheme: 'Toggle theme',
38
- moreOptions: 'More options',
39
- copied: 'Copied',
40
- expand: 'Expand',
41
- collapse: 'Collapse',
42
- copyPage: 'Copy page',
43
- copyPageDescription: 'Copy this page as Markdown',
44
- viewMarkdown: 'View as Markdown',
45
- viewMarkdownDescription: 'Open this page as plain Markdown',
46
- openInChatGPT: 'Open in ChatGPT',
47
- openInClaude: 'Open in Claude',
48
- openInPerplexity: 'Open in Perplexity',
49
- askQuestionsAboutPage: 'Ask questions about this page',
50
- };
51
-
52
22
  function isInternalHref(href: string): boolean {
53
23
  return /^(?:#|\/|\.\.?\/)/.test(href);
54
24
  }
@@ -141,7 +111,7 @@ export function resolveSiteModel(
141
111
  showTimestamp: config.metadata?.timestamp === true,
142
112
  drilldown: config.interaction?.drilldown,
143
113
  locale: shiso?.locale || 'en-US',
144
- labels: SHISO_THEME_LABELS,
114
+ labels: resolveLabels(shiso?.locale || 'en-US', shiso?.translations),
145
115
  docs,
146
116
  };
147
117
  }
@@ -108,7 +108,21 @@ export function normalizeStandalonePages(
108
108
  );
109
109
  }
110
110
 
111
- pages.push({ path, filePath, title: item?.title?.trim() || undefined });
111
+ const language = item?.language?.trim() || undefined;
112
+
113
+ if (language && !site.scopes.some(scope => scope.language === language)) {
114
+ throw invalid(
115
+ `standalone page "${path}" uses language "${language}", which is not declared in navigation.languages.`,
116
+ );
117
+ }
118
+
119
+ // Language-independent identity: "ja/home" in language "ja" is the "home" page.
120
+ const key =
121
+ language && fileSlug.startsWith(`${language}/`)
122
+ ? fileSlug.slice(language.length + 1)
123
+ : fileSlug;
124
+
125
+ pages.push({ path, filePath, title: item?.title?.trim() || undefined, language, key });
112
126
  }
113
127
 
114
128
  return pages;
@@ -0,0 +1,74 @@
1
+ {
2
+ "menu": "Menü",
3
+ "documentationNavigation": "Dokumentationsnavigation",
4
+ "sections": "Abschnitte",
5
+ "tableOfContents": "Auf dieser Seite",
6
+ "tableOfContentsNavigation": "Inhaltsverzeichnis",
7
+ "searchTitle": "Suchen",
8
+ "searching": "Suche läuft...",
9
+ "searchUnavailable": "Suche nicht verfügbar",
10
+ "noResults": "Keine Ergebnisse",
11
+ "lastUpdated": "Zuletzt aktualisiert am",
12
+ "relatedTopics": "Verwandte Themen",
13
+ "previousPage": "Zurück",
14
+ "nextPage": "Weiter",
15
+ "notFound": "Seite nicht gefunden",
16
+ "dismissBanner": "Banner schließen",
17
+ "toggleTheme": "Design wechseln",
18
+ "moreOptions": "Weitere Optionen",
19
+ "copied": "Kopiert",
20
+ "expand": "Aufklappen",
21
+ "collapse": "Zuklappen",
22
+ "copyPage": "Seite kopieren",
23
+ "copyPageDescription": "Diese Seite als Markdown kopieren",
24
+ "viewMarkdown": "Als Markdown anzeigen",
25
+ "viewMarkdownDescription": "Diese Seite als reinen Markdown-Text öffnen",
26
+ "openInChatGPT": "In ChatGPT öffnen",
27
+ "openInClaude": "In Claude öffnen",
28
+ "openInPerplexity": "In Perplexity öffnen",
29
+ "askQuestionsAboutPage": "Fragen zu dieser Seite stellen",
30
+ "searchPlaceholder": "Suchen...",
31
+ "poweredBy": "Erstellt mit",
32
+ "copyCode": "Code kopieren",
33
+ "editPage": "Diese Seite bearbeiten",
34
+ "feedbackPrompt": "War diese Seite hilfreich?",
35
+ "feedbackYes": "Ja",
36
+ "feedbackNo": "Nein",
37
+ "feedbackSuccess": "Vielen Dank für dein Feedback!",
38
+ "feedbackError": "Feedback konnte nicht gesendet werden. Bitte versuche es erneut.",
39
+ "close": "Schließen",
40
+ "filterUpdates": "Aktualisierungen nach Tag filtern",
41
+ "clear": "Zurücksetzen",
42
+ "noUpdates": "Keine Aktualisierungen gefunden.",
43
+ "codeSnippets": "Codebeispiele",
44
+ "contentTabs": "Inhaltsregisterkarten",
45
+ "fileTree": "Dateibaum",
46
+ "diagram": "Diagramm",
47
+ "diagramError": "Dieses Diagramm konnte nicht angezeigt werden.",
48
+ "zoomOut": "Verkleinern",
49
+ "resetView": "Ansicht zurücksetzen",
50
+ "zoomIn": "Vergrößern",
51
+ "viewImage": "Bild in voller Größe anzeigen",
52
+ "viewNamedImage": "„{alt}“ in voller Größe anzeigen",
53
+ "fullSizeImage": "Bild in voller Größe",
54
+ "diagramSource": "Diagrammquelltext",
55
+ "fieldName": "Name",
56
+ "fieldType": "Typ",
57
+ "fieldDescription": "Beschreibung",
58
+ "fieldRequired": "erforderlich",
59
+ "fieldDeprecated": "veraltet",
60
+ "fieldDefault": "Standard:",
61
+ "fieldOptions": "Optionen:",
62
+ "allowedTypes": "Zulässige Typen",
63
+ "properties": "Eigenschaften",
64
+ "apiHeaders": "Header",
65
+ "apiPathParameters": "Pfadparameter",
66
+ "apiQueryParameters": "Abfrageparameter",
67
+ "apiCookieParameters": "Cookie-Parameter",
68
+ "apiRequestBody": "Anfragetext",
69
+ "apiResponses": "Antworten",
70
+ "apiCodeSamples": "Codebeispiele",
71
+ "apiExampleRequest": "Beispielanfrage",
72
+ "apiExampleResponse": "Beispiel für {status}",
73
+ "apiCredentials": "Anmeldedaten, z. B."
74
+ }
@@ -0,0 +1,74 @@
1
+ {
2
+ "menu": "Menu",
3
+ "documentationNavigation": "Documentation navigation",
4
+ "sections": "Sections",
5
+ "tableOfContents": "On this page",
6
+ "tableOfContentsNavigation": "Table of contents",
7
+ "searchTitle": "Search",
8
+ "searching": "Searching...",
9
+ "searchUnavailable": "Search unavailable",
10
+ "noResults": "No results",
11
+ "lastUpdated": "Last updated on",
12
+ "relatedTopics": "Related topics",
13
+ "previousPage": "Previous",
14
+ "nextPage": "Next",
15
+ "notFound": "Page not found",
16
+ "dismissBanner": "Dismiss banner",
17
+ "toggleTheme": "Toggle theme",
18
+ "moreOptions": "More options",
19
+ "copied": "Copied",
20
+ "expand": "Expand",
21
+ "collapse": "Collapse",
22
+ "copyPage": "Copy page",
23
+ "copyPageDescription": "Copy this page as Markdown",
24
+ "viewMarkdown": "View as Markdown",
25
+ "viewMarkdownDescription": "Open this page as plain Markdown",
26
+ "openInChatGPT": "Open in ChatGPT",
27
+ "openInClaude": "Open in Claude",
28
+ "openInPerplexity": "Open in Perplexity",
29
+ "askQuestionsAboutPage": "Ask questions about this page",
30
+ "searchPlaceholder": "Search...",
31
+ "poweredBy": "Powered by",
32
+ "copyCode": "Copy code",
33
+ "editPage": "Edit this page",
34
+ "feedbackPrompt": "Was this page helpful?",
35
+ "feedbackYes": "Yes",
36
+ "feedbackNo": "No",
37
+ "feedbackSuccess": "Thanks for your feedback!",
38
+ "feedbackError": "Could not send feedback. Please try again.",
39
+ "close": "Close",
40
+ "filterUpdates": "Filter updates by tag",
41
+ "clear": "Clear",
42
+ "noUpdates": "No updates found.",
43
+ "codeSnippets": "Code snippets",
44
+ "contentTabs": "Content tabs",
45
+ "fileTree": "File tree",
46
+ "diagram": "Diagram",
47
+ "diagramError": "Could not render this diagram.",
48
+ "zoomOut": "Zoom out",
49
+ "resetView": "Reset view",
50
+ "zoomIn": "Zoom in",
51
+ "viewImage": "View image full size",
52
+ "viewNamedImage": "View “{alt}” full size",
53
+ "fullSizeImage": "Full-size image",
54
+ "diagramSource": "Diagram source",
55
+ "fieldName": "Name",
56
+ "fieldType": "Type",
57
+ "fieldDescription": "Description",
58
+ "fieldRequired": "required",
59
+ "fieldDeprecated": "deprecated",
60
+ "fieldDefault": "Default:",
61
+ "fieldOptions": "Options:",
62
+ "allowedTypes": "Allowed types",
63
+ "properties": "properties",
64
+ "apiHeaders": "Headers",
65
+ "apiPathParameters": "Path parameters",
66
+ "apiQueryParameters": "Query parameters",
67
+ "apiCookieParameters": "Cookie parameters",
68
+ "apiRequestBody": "Request body",
69
+ "apiResponses": "Responses",
70
+ "apiCodeSamples": "Code samples",
71
+ "apiExampleRequest": "Example request",
72
+ "apiExampleResponse": "{status} example",
73
+ "apiCredentials": "Authentication credentials, e.g."
74
+ }