Skip to content
Closed
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
56 changes: 47 additions & 9 deletions src/components/api/ApiPage.astro
Original file line number Diff line number Diff line change
@@ -1,30 +1,46 @@
---
import DocsLayout from "@/layouts/DocsLayout.astro";
import ApiOperation from "./ApiOperation.astro";
import { Aside } from "@/components/ui/aside";
import { Badge } from "@/components/ui/badge";
import { Breadcrumbs } from "@/components/ui/breadcrumbs";
import { getPrevNext } from "@cloudflare/nimbus-docs";
import { withBase } from "@/lib/base";
import { buildRailFromConfig, type ConfigItem } from "@/lib/sidebar-lazy";
import { loadGeneratedNavigation } from "@/lib/sidebar-navigation.server";
import { getApiOperations, type RoutedApiOperationPage } from "@/lib/openapi";
import { AVAILABLE_LOCALES } from "@/content.config";
import { localeInfo, localePath, notTranslatedNotice } from "@/util/locales";
import navigation from "@/generated/sidebar.items.json";

interface Props {
page: RoutedApiOperationPage;
currentLocale?: string;
activeLocales?: string[];
fallback?: boolean;
}

const { page } = Astro.props;
const {
page,
currentLocale = "en",
activeLocales = AVAILABLE_LOCALES,
fallback = false,
} = Astro.props;
const { source, view } = page;
const browserPath = withBase(`/${source.route}`);
const sidebar = buildRailFromConfig(navigation as ConfigItem[], browserPath);
const localized = currentLocale.toLowerCase() !== "en";
const browserPath = withBase(localePath(`/${source.route}`, currentLocale));
const navItems = localized
? loadGeneratedNavigation(currentLocale).items
: navigation as ConfigItem[];
const sidebar = buildRailFromConfig(navItems, browserPath, localized ? [currentLocale] : []);
const prevNext = await getPrevNext(browserPath, { sidebarTree: sidebar });
const isCloud = source.collection === "cloud";
const overview = isCloud
? withBase("/products/cloud/features/admin-features/api/api-overview")
: withBase("/clickstack/api-reference");
? withBase(localePath("/products/cloud/features/admin-features/api/api-overview", currentLocale))
: withBase(localePath("/clickstack/api-reference", currentLocale));
const firstInTag = getApiOperations(source.collection)
.find((candidate) => candidate.tagSlug === source.tagSlug) ?? source;
const tagRoute = withBase(`/${firstInTag.route}`);
const tagRoute = withBase(localePath(`/${firstInTag.route}`, currentLocale));
const breadcrumbs = [
{ label: isCloud ? "Cloud API" : "ClickStack", href: overview },
{ label: "API reference", href: overview },
Expand All @@ -39,6 +55,21 @@ const headings = [
];
const markdownPath = withBase(`/${source.route}.md`);
const markdownUrl = Astro.site ? new URL(markdownPath, Astro.site).href : markdownPath;
const language = localeInfo(currentLocale);
const canonicalUrl = localized && Astro.site
? new URL(withBase(`/${source.route}/`), Astro.site).href
: undefined;
const alternates = Astro.site
? [
{ hreflang: "en", href: new URL(withBase(`/${source.route}/`), Astro.site).href },
{ hreflang: "x-default", href: new URL(withBase(`/${source.route}/`), Astro.site).href },
...activeLocales.map((locale) => ({
hreflang: localeInfo(locale).hreflang,
href: new URL(withBase(localePath(`/${source.route}/`, locale)), Astro.site!).href,
})),
]
: [];
const fallbackNotice = notTranslatedNotice(currentLocale);
---

<DocsLayout
Expand All @@ -51,9 +82,11 @@ const markdownUrl = Astro.site ? new URL(markdownPath, Astro.site).href : markdo
mode="wide"
markdownUrl={markdownUrl}
editUrl={source.editUrl}
lang="en"
currentLocale="en"
activeLocales={[]}
lang={language.hreflang}
alternates={alternates}
canonicalUrl={canonicalUrl}
currentLocale={currentLocale}
activeLocales={activeLocales}
>
<div slot="page-title" class="ch-api-heading">
<Breadcrumbs items={breadcrumbs} class="ch-api-breadcrumbs" />
Expand All @@ -63,6 +96,11 @@ const markdownUrl = Astro.site ? new URL(markdownPath, Astro.site).href : markdo
{view.deprecated && <Badge text="Deprecated" variant="warning" size="medium" />}
</div>
</div>
{fallback && (
<Aside type="note" label={fallbackNotice.label} title={fallbackNotice.title}>
{fallbackNotice.message}
</Aside>
)}
<ApiOperation
page={view}
authSchemes={source.document.components?.securitySchemes}
Expand Down
26 changes: 13 additions & 13 deletions src/pages/[locale]/[...slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
localeCollectionName,
type LocaleCollectionName,
} from "../../content.config";
import { localeRouteName } from "../../util/locales";
import { localeRouteName, notTranslatedNotice } from "../../util/locales";
import { getRemoteEditUrl } from "../../lib/remotes";
import { loadGeneratedNavigation } from "../../lib/sidebar-navigation.server";
import { translatedPageIds } from "../../lib/locale-content.server";
Expand All @@ -48,6 +48,11 @@ export async function getStaticPaths() {
cacheKey?: string;
}>;
for (const p of localePaths) {
const slug = p.params.slug;
if (
slug?.startsWith("products/cloud/api-reference/")
|| slug?.startsWith("clickstack/api-reference/")
) continue;
paths.push({
params: { locale, slug: p.params.slug },
props: p.props,
Expand All @@ -66,7 +71,12 @@ export async function getStaticPaths() {
const translated = translatedPageIds(sourceLocale, SECTIONS);
const navigationDigest = loadGeneratedNavigation(sourceLocale).digest;
for (const entry of english) {
if (translated.has(entry.id) || entry.id === "index") continue;
if (
translated.has(entry.id)
|| entry.id === "index"
|| entry.id.startsWith("products/cloud/api-reference/")
|| entry.id.startsWith("clickstack/api-reference/")
) continue;
paths.push({
params: { locale, slug: entry.id },
props: { entry, fallback: true },
Expand Down Expand Up @@ -128,17 +138,7 @@ const alternates = Astro.site
]
: [];
const htmlLang = HREFLANG[localeCode] ?? localeParam;
const NOT_TRANSLATED: Record<string, { label: string; title: string; message: string }> = {
ar: { label: "ملاحظة", title: "لم تُترجم بعد", message: "هذه الصفحة لم تُترجم بعد؛ يتم عرض النسخة الإنجليزية." },
es: { label: "Nota", title: "Aún no traducida", message: "Esta página aún no está traducida; se muestra la versión en inglés." },
fr: { label: "Remarque", title: "Pas encore traduit", message: "Cette page n'est pas encore traduite ; la version anglaise est affichée." },
ja: { label: "注記", title: "未翻訳", message: "このページはまだ翻訳されていません。英語版を表示しています。" },
ko: { label: "참고", title: "아직 번역되지 않음", message: "이 페이지는 아직 번역되지 않았습니다. 영어 버전이 표시됩니다." },
"pt-br": { label: "Observação", title: "Ainda não traduzida", message: "Esta página ainda não foi traduzida; a versão em inglês é exibida." },
ru: { label: "Примечание", title: "Перевод пока недоступен", message: "Эта страница ещё не переведена; показана английская версия." },
zh: { label: "注意", title: "尚未翻译", message: "此页面尚未翻译,显示的是英文版本。" },
};
const fallbackNotice = NOT_TRANSLATED[localeCode] ?? { label: "Note", title: "Not translated yet", message: "This page is not translated yet; the English version is shown." };
const fallbackNotice = notTranslatedNotice(localeCode);
---

<DocsLayout
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
import ApiPage from "@/components/api/ApiPage.astro";
import { apiPageCacheKey, getApiOperations, getRoutedApiOperation } from "@/lib/openapi";
import { readScope } from "@/lib/scope";
import { loadGeneratedNavigation } from "@/lib/sidebar-navigation.server";
import { localeRouteName } from "@/util/locales";

export const prerender = true;

export function getStaticPaths() {
const scope = readScope();
if (scope.remotePreview) return [];
return scope.locales.flatMap((locale) => {
const routeLocale = localeRouteName(locale);
const navigationDigest = loadGeneratedNavigation(routeLocale).digest;
return getApiOperations("clickstack").map((page) => ({
params: { locale: routeLocale, tag: page.tagSlug, operation: page.slug },
cacheKey: `${apiPageCacheKey(page)}:locale:${routeLocale}:nav:${navigationDigest}`,
}));
});
}

const scope = readScope();
const locale = Astro.params.locale!;
const page = await getRoutedApiOperation("clickstack", Astro.params.tag!, Astro.params.operation!);
---

<ApiPage page={page} currentLocale={locale} activeLocales={scope.availableLocales} fallback />
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
import ApiPage from "@/components/api/ApiPage.astro";
import { apiPageCacheKey, getApiOperations, getRoutedApiOperation } from "@/lib/openapi";
import { readScope } from "@/lib/scope";
import { loadGeneratedNavigation } from "@/lib/sidebar-navigation.server";
import { localeRouteName } from "@/util/locales";

export const prerender = true;

export function getStaticPaths() {
const scope = readScope();
if (scope.remotePreview) return [];
return scope.locales.flatMap((locale) => {
const routeLocale = localeRouteName(locale);
const navigationDigest = loadGeneratedNavigation(routeLocale).digest;
return getApiOperations("cloud").map((page) => ({
params: { locale: routeLocale, tag: page.tagSlug, operation: page.slug },
cacheKey: `${apiPageCacheKey(page)}:locale:${routeLocale}:nav:${navigationDigest}`,
}));
});
}

const scope = readScope();
const locale = Astro.params.locale!;
const page = await getRoutedApiOperation("cloud", Astro.params.tag!, Astro.params.operation!);
---

<ApiPage page={page} currentLocale={locale} activeLocales={scope.availableLocales} fallback />
25 changes: 25 additions & 0 deletions src/util/locales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,35 @@ export const LOCALE_METADATA = {

export type LocaleCode = keyof typeof LOCALE_METADATA;

export interface NotTranslatedNotice {
label: string;
title: string;
message: string;
}

const NOT_TRANSLATED: Partial<Record<LocaleCode, NotTranslatedNotice>> = {
ar: { label: "ملاحظة", title: "لم تُترجم بعد", message: "هذه الصفحة لم تُترجم بعد؛ يتم عرض النسخة الإنجليزية." },
es: { label: "Nota", title: "Aún no traducida", message: "Esta página aún no está traducida; se muestra la versión en inglés." },
fr: { label: "Remarque", title: "Pas encore traduit", message: "Cette page n'est pas encore traduite ; la version anglaise est affichée." },
ja: { label: "注記", title: "未翻訳", message: "このページはまだ翻訳されていません。英語版を表示しています。" },
ko: { label: "참고", title: "아직 번역되지 않음", message: "이 페이지는 아직 번역되지 않았습니다. 영어 버전이 표시됩니다." },
"pt-br": { label: "Observação", title: "Ainda não traduzida", message: "Esta página ainda não foi traduzida; a versão em inglês é exibida." },
ru: { label: "Примечание", title: "Перевод пока недоступен", message: "Эта страница ещё не переведена; показана английская версия." },
zh: { label: "注意", title: "尚未翻译", message: "此页面尚未翻译,显示的是英文版本。" },
};

export function localeInfo(locale: string) {
return LOCALE_METADATA[locale.toLowerCase() as LocaleCode] ?? LOCALE_METADATA.en;
}

export function notTranslatedNotice(locale: string): NotTranslatedNotice {
return NOT_TRANSLATED[locale.toLowerCase() as LocaleCode] ?? {
label: "Note",
title: "Not translated yet",
message: "This page is not translated yet; the English version is shown.",
};
}

/** Canonical locale segment used by the deployed Mintlify documentation. */
export function localeRouteName(locale: string): string {
const code = locale.toLowerCase();
Expand Down