From d16645121d7c7c773456459e1a53bcf5236b28d0 Mon Sep 17 00:00:00 2001 From: Sinan Eldem Date: Fri, 4 Sep 2026 04:51:36 +0300 Subject: [PATCH] docs(i18n): add turkish to the documentation site --- docs/app/[lang]/api/search/route.ts | 3 +- docs/app/[lang]/changelog/page.tsx | 13 +- docs/app/[lang]/community/page.tsx | 13 +- docs/app/[lang]/contributors/page.tsx | 9 +- docs/app/[lang]/databases/page.tsx | 14 +- docs/app/[lang]/docs/[[...slug]]/page.tsx | 4 +- docs/app/[lang]/docs/layout.tsx | 4 +- docs/app/[lang]/drivers/DriversClient.tsx | 47 +- docs/app/[lang]/drivers/page.tsx | 7 +- docs/app/[lang]/issue/page.tsx | 9 +- docs/app/[lang]/layout.tsx | 12 +- docs/app/[lang]/page.tsx | 101 +++- docs/app/[lang]/sponsors/page.tsx | 25 +- .../contributors/ContributorsExperience.tsx | 35 +- .../issues/IssueSubmissionClient.tsx | 57 +- docs/components/landing/ChangelogList.tsx | 79 ++- docs/components/landing/ChangelogRuntime.tsx | 11 +- docs/components/landing/ContributorsWall.tsx | 13 +- .../landing/ExpandableDatabaseGrid.tsx | 17 +- docs/components/landing/InstallTabs.tsx | 60 ++- docs/components/landing/LandingFooter.tsx | 7 +- docs/components/landing/LandingNav.tsx | 34 +- .../components/landing/SponsorContactForm.tsx | 30 +- docs/content/docs/1panel.tr.mdx | 111 ++++ docs/content/docs/ai-assistant.tr.mdx | 190 +++++++ docs/content/docs/changelog.tr.mdx | 8 + docs/content/docs/cli.tr.mdx | 244 +++++++++ docs/content/docs/cloud-sync.tr.mdx | 176 +++++++ docs/content/docs/config-export.tr.mdx | 84 +++ docs/content/docs/connection-import.tr.mdx | 124 +++++ docs/content/docs/contributing.cn.mdx | 2 + docs/content/docs/contributing.mdx | 8 +- docs/content/docs/contributing.tr.mdx | 497 ++++++++++++++++++ docs/content/docs/data-grid.tr.mdx | 180 +++++++ docs/content/docs/data-transfer.tr.mdx | 127 +++++ docs/content/docs/database-backup.tr.mdx | 162 ++++++ docs/content/docs/database-docs.tr.mdx | 108 ++++ docs/content/docs/database-export.tr.mdx | 124 +++++ docs/content/docs/database-lab.tr.mdx | 163 ++++++ docs/content/docs/databases.tr.mdx | 114 ++++ docs/content/docs/driver-management.tr.mdx | 113 ++++ docs/content/docs/faq.tr.mdx | 33 ++ docs/content/docs/field-lineage.tr.mdx | 91 ++++ docs/content/docs/getting-started.tr.mdx | 275 ++++++++++ docs/content/docs/keyboard-shortcuts.tr.mdx | 80 +++ docs/content/docs/mcp.tr.mdx | 276 ++++++++++ docs/content/docs/meta.tr.json | 51 ++ docs/content/docs/mongodb.tr.mdx | 130 +++++ docs/content/docs/object-browser.tr.mdx | 102 ++++ docs/content/docs/plugins.tr.mdx | 304 +++++++++++ docs/content/docs/production-safety.tr.mdx | 165 ++++++ docs/content/docs/query-editor.tr.mdx | 187 +++++++ docs/content/docs/redis.tr.mdx | 137 +++++ docs/content/docs/schema-browser.tr.mdx | 153 ++++++ docs/content/docs/schema-diff.tr.mdx | 211 ++++++++ .../docs/specialized-workspaces.tr.mdx | 154 ++++++ docs/content/docs/sql-file.tr.mdx | 123 +++++ docs/content/docs/sql-formatter.tr.mdx | 113 ++++ docs/content/docs/sql-quick-actions.tr.mdx | 72 +++ docs/content/docs/sql-snippets.tr.mdx | 78 +++ docs/content/docs/ssh-tunnel.tr.mdx | 183 +++++++ docs/content/docs/table-import.tr.mdx | 137 +++++ docs/content/docs/table-structure.tr.mdx | 144 +++++ docs/content/docs/web-api.tr.mdx | 349 ++++++++++++ docs/content/docs/what-is-dbx.tr.mdx | 153 ++++++ docs/lib/changelog.ts | 26 +- docs/lib/downloadLinks.ts | 40 +- docs/lib/i18n.ts | 28 +- docs/lib/issueSubmission.ts | 7 +- docs/lib/metadata.ts | 5 +- docs/lib/structuredData.ts | 18 +- docs/scripts/generate-sitemap.mjs | 33 +- docs/scripts/languages.mjs | 26 + docs/scripts/verify-doc-content.mjs | 36 +- docs/scripts/verify-static-export.mjs | 20 +- 75 files changed, 6922 insertions(+), 157 deletions(-) create mode 100644 docs/content/docs/1panel.tr.mdx create mode 100644 docs/content/docs/ai-assistant.tr.mdx create mode 100644 docs/content/docs/changelog.tr.mdx create mode 100644 docs/content/docs/cli.tr.mdx create mode 100644 docs/content/docs/cloud-sync.tr.mdx create mode 100644 docs/content/docs/config-export.tr.mdx create mode 100644 docs/content/docs/connection-import.tr.mdx create mode 100644 docs/content/docs/contributing.tr.mdx create mode 100644 docs/content/docs/data-grid.tr.mdx create mode 100644 docs/content/docs/data-transfer.tr.mdx create mode 100644 docs/content/docs/database-backup.tr.mdx create mode 100644 docs/content/docs/database-docs.tr.mdx create mode 100644 docs/content/docs/database-export.tr.mdx create mode 100644 docs/content/docs/database-lab.tr.mdx create mode 100644 docs/content/docs/databases.tr.mdx create mode 100644 docs/content/docs/driver-management.tr.mdx create mode 100644 docs/content/docs/faq.tr.mdx create mode 100644 docs/content/docs/field-lineage.tr.mdx create mode 100644 docs/content/docs/getting-started.tr.mdx create mode 100644 docs/content/docs/keyboard-shortcuts.tr.mdx create mode 100644 docs/content/docs/mcp.tr.mdx create mode 100644 docs/content/docs/meta.tr.json create mode 100644 docs/content/docs/mongodb.tr.mdx create mode 100644 docs/content/docs/object-browser.tr.mdx create mode 100644 docs/content/docs/plugins.tr.mdx create mode 100644 docs/content/docs/production-safety.tr.mdx create mode 100644 docs/content/docs/query-editor.tr.mdx create mode 100644 docs/content/docs/redis.tr.mdx create mode 100644 docs/content/docs/schema-browser.tr.mdx create mode 100644 docs/content/docs/schema-diff.tr.mdx create mode 100644 docs/content/docs/specialized-workspaces.tr.mdx create mode 100644 docs/content/docs/sql-file.tr.mdx create mode 100644 docs/content/docs/sql-formatter.tr.mdx create mode 100644 docs/content/docs/sql-quick-actions.tr.mdx create mode 100644 docs/content/docs/sql-snippets.tr.mdx create mode 100644 docs/content/docs/ssh-tunnel.tr.mdx create mode 100644 docs/content/docs/table-import.tr.mdx create mode 100644 docs/content/docs/table-structure.tr.mdx create mode 100644 docs/content/docs/web-api.tr.mdx create mode 100644 docs/content/docs/what-is-dbx.tr.mdx create mode 100644 docs/scripts/languages.mjs diff --git a/docs/app/[lang]/api/search/route.ts b/docs/app/[lang]/api/search/route.ts index 72b9282631..7135000978 100644 --- a/docs/app/[lang]/api/search/route.ts +++ b/docs/app/[lang]/api/search/route.ts @@ -1,6 +1,7 @@ import { createFromSource } from "fumadocs-core/search/server"; import { createCjkSearchTokenizer } from "@/lib/cjkSearchTokenizer"; import { source } from "@/lib/source"; +import { i18n } from "@/lib/i18n"; export const revalidate = false; export const { staticGET: GET } = createFromSource(source, { @@ -10,5 +11,5 @@ export const { staticGET: GET } = createFromSource(source, { }); export function generateStaticParams() { - return [{ lang: "en" }, { lang: "cn" }]; + return i18n.languages.map((lang) => ({ lang })); } diff --git a/docs/app/[lang]/changelog/page.tsx b/docs/app/[lang]/changelog/page.tsx index 89fb288368..4d7245a569 100644 --- a/docs/app/[lang]/changelog/page.tsx +++ b/docs/app/[lang]/changelog/page.tsx @@ -1,15 +1,20 @@ import { LandingNav } from "@/components/landing/LandingNav"; import { LandingFooter } from "@/components/landing/LandingFooter"; import { ChangelogRuntime } from "@/components/landing/ChangelogRuntime"; -import { loadChangelogBootstrap } from "@/lib/changelog"; +import { changelogDataLang, loadChangelogBootstrap } from "@/lib/changelog"; import { buildMetadata } from "@/lib/metadata"; import type { Metadata } from "next"; +import { resolveLang } from "@/lib/i18n"; const i18n = { en: { title: "Changelog", desc: "Track every release — features, improvements, and fixes.", }, + tr: { + title: "Değişiklik Günlüğü", + desc: "Her sürümü takip edin — yeni özellikler, iyileştirmeler ve düzeltmeler.", + }, cn: { title: "更新日志", desc: "追踪每次发布 — 新功能、改进和修复。", @@ -18,7 +23,7 @@ const i18n = { export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { const { lang } = await params; - const l = lang === "cn" ? "cn" : "en"; + const l = resolveLang(lang); const t = i18n[l]; return buildMetadata({ @@ -31,9 +36,9 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: str export default async function ChangelogPage({ params }: { params: Promise<{ lang: string }> }) { const { lang } = await params; - const l = lang === "cn" ? "cn" : "en"; + const l = resolveLang(lang); const t = i18n[l]; - const initialData = await loadChangelogBootstrap(l); + const initialData = await loadChangelogBootstrap(changelogDataLang(l)); return (
diff --git a/docs/app/[lang]/community/page.tsx b/docs/app/[lang]/community/page.tsx index dfddbbb048..3f5720c10d 100644 --- a/docs/app/[lang]/community/page.tsx +++ b/docs/app/[lang]/community/page.tsx @@ -2,6 +2,7 @@ import { LandingNav } from "@/components/landing/LandingNav"; import { LandingFooter } from "@/components/landing/LandingFooter"; import { buildMetadata } from "@/lib/metadata"; import type { Metadata } from "next"; +import { resolveLang } from "@/lib/i18n"; const channels = [ { @@ -51,6 +52,14 @@ const i18n = { wechat: { name: "WeChat Group", desc: "Join via Tencent Docs invite link." }, github: { name: "GitHub Discussions", desc: "Bug reports, feature requests, and long-form discussions." }, }, + tr: { + title: "Topluluk", + desc: "DBX topluluğuna katılın — soru sorun, geri bildirim paylaşın ve diğer kullanıcılarla tanışın.", + discord: { name: "Discord", desc: "Anlık sohbet, soru-cevap ve özellik tartışmaları." }, + qq: { name: "QQ Grubu", desc: "Grup numarası: 1087880322" }, + wechat: { name: "WeChat Grubu", desc: "Tencent Docs davet bağlantısıyla katılın." }, + github: { name: "GitHub Discussions", desc: "Hata bildirimleri, özellik istekleri ve uzun tartışmalar." }, + }, cn: { title: "交流群", desc: "加入 DBX 社区 — 提问、反馈、与其他用户交流。", @@ -63,7 +72,7 @@ const i18n = { export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { const { lang } = await params; - const l = lang === "cn" ? "cn" : "en"; + const l = resolveLang(lang); const t = i18n[l]; return buildMetadata({ @@ -76,7 +85,7 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: str export default async function CommunityPage({ params }: { params: Promise<{ lang: string }> }) { const { lang } = await params; - const l = lang === "cn" ? "cn" : "en"; + const l = resolveLang(lang); const t = i18n[l]; return ( diff --git a/docs/app/[lang]/contributors/page.tsx b/docs/app/[lang]/contributors/page.tsx index 96c6da27b6..7c736bbe5c 100644 --- a/docs/app/[lang]/contributors/page.tsx +++ b/docs/app/[lang]/contributors/page.tsx @@ -5,12 +5,17 @@ import { LandingFooter } from "@/components/landing/LandingFooter"; import { LandingNav } from "@/components/landing/LandingNav"; import type { ContributorActivityData } from "@/lib/contributorActivity"; import { buildMetadata } from "@/lib/metadata"; +import { resolveLang } from "@/lib/i18n"; const pageMetadata = { en: { title: "DBX Contributors", description: "Explore the people building DBX and download a certificate generated from public GitHub activity.", }, + tr: { + title: "DBX Katkıda Bulunanlar", + description: "DBX'i birlikte geliştiren açık kaynak katkıcılarını görün ve herkese açık GitHub etkinliğinden üretilen bir katkı sertifikası indirin.", + }, cn: { title: "DBX 贡献者", description: "查看共同建设 DBX 的开源贡献者,并根据公开 GitHub 活动生成贡献证书。", @@ -19,7 +24,7 @@ const pageMetadata = { export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { const { lang } = await params; - const locale = lang === "cn" ? "cn" : "en"; + const locale = resolveLang(lang); const metadata = pageMetadata[locale]; return buildMetadata({ @@ -32,7 +37,7 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: str export default async function ContributorsPage({ params }: { params: Promise<{ lang: string }> }) { const { lang } = await params; - const locale = lang === "cn" ? "cn" : "en"; + const locale = resolveLang(lang); const data = contributorSnapshot as ContributorActivityData; return ( diff --git a/docs/app/[lang]/databases/page.tsx b/docs/app/[lang]/databases/page.tsx index e393cdfb66..b507d31175 100644 --- a/docs/app/[lang]/databases/page.tsx +++ b/docs/app/[lang]/databases/page.tsx @@ -8,6 +8,7 @@ import { RevealSection } from "@/components/landing/RevealSection"; import { ExpandableDatabaseGrid } from "@/components/landing/ExpandableDatabaseGrid"; import { databaseSupport } from "@/data/databaseSupport"; import { buildMetadata } from "@/lib/metadata"; +import { resolveLang } from "@/lib/i18n"; const i18n = { en: { @@ -19,6 +20,15 @@ const i18n = { footer: "Want to learn more about what works with each engine?", footerLink: "Read the feature matrix", }, + tr: { + title: "Desteklenen Veritabanları", + desc: "DBX 90+ veritabanı motorunu destekler: Rust yerel sürücüleri, MySQL/PostgreSQL uyumlu türler ve JDBC uzantıları.", + ctaTitle: "Kullandığınız veritabanını göremiyor musunuz?", + ctaDesc: "Yeni bir veritabanı motoru için GitHub Discussions'ta bir başlık açın. Üreticiler ve topluluk kullanıcıları katkı verebilir.", + ctaLink: "GitHub'da talep edin", + footer: "Her motorun hangi özellikleri desteklediğini merak ediyor musunuz?", + footerLink: "Özellik matrisine bakın", + }, cn: { title: "支持的数据库", desc: "DBX 支持 90+ 种数据库引擎。涵盖 Rust 原生驱动、MySQL/PostgreSQL 兼容类型和 JDBC 扩展。", @@ -32,7 +42,7 @@ const i18n = { export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { const { lang } = await params; - const l = lang === "cn" ? "cn" : "en"; + const l = resolveLang(lang); const t = i18n[l]; return buildMetadata({ @@ -45,7 +55,7 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: str export default async function DatabasesPage({ params }: { params: Promise<{ lang: string }> }) { const { lang } = await params; - const l = lang === "cn" ? "cn" : "en"; + const l = resolveLang(lang); const t = i18n[l]; return ( diff --git a/docs/app/[lang]/docs/[[...slug]]/page.tsx b/docs/app/[lang]/docs/[[...slug]]/page.tsx index 8cbd9cb47c..cb84c3e115 100644 --- a/docs/app/[lang]/docs/[[...slug]]/page.tsx +++ b/docs/app/[lang]/docs/[[...slug]]/page.tsx @@ -11,7 +11,7 @@ import { ImageZoom } from "fumadocs-ui/components/image-zoom"; import { Accordion, Accordions } from "fumadocs-ui/components/accordion"; import type { MDXContent } from "mdx/types"; import type { TOCItemType } from "fumadocs-core/toc"; -import { buildMetadata, SITE_URL } from "@/lib/metadata"; +import { buildMetadata, getHtmlLang, SITE_URL } from "@/lib/metadata"; export async function generateMetadata({ params }: { params: Promise<{ lang: string; slug?: string[] }> }): Promise { const { lang, slug } = await params; @@ -80,7 +80,7 @@ export default async function Page({ params }: { params: Promise<{ lang: string; "@type": "TechArticle", headline: title, description, - inLanguage: lang === "cn" ? "zh-CN" : "en", + inLanguage: getHtmlLang(lang), isPartOf: { "@type": "WebSite", url: SITE_URL, diff --git a/docs/app/[lang]/docs/layout.tsx b/docs/app/[lang]/docs/layout.tsx index 0ab67c5fff..ac464f3237 100644 --- a/docs/app/[lang]/docs/layout.tsx +++ b/docs/app/[lang]/docs/layout.tsx @@ -3,12 +3,12 @@ import { DocsLayout } from "fumadocs-ui/layouts/docs"; import { RootProvider } from "fumadocs-ui/provider/next"; import { DocsSidebarFooter, DocsSidebarLanguageButton } from "@/components/DocsSidebarFooter"; import { StaticSearchDialog } from "@/components/StaticSearchDialog"; -import { i18nUI } from "@/lib/i18n"; +import { i18nUI, resolveLang } from "@/lib/i18n"; import { source } from "@/lib/source"; export default async function Layout({ params, children }: { params: Promise<{ lang: string }>; children: ReactNode }) { const { lang } = await params; - const locale = lang === "cn" ? "cn" : "en"; + const locale = resolveLang(lang); return ( Sürücü Yöneticisi > JDBC Sürücüleri > Yerel Kurulum bölümünden içe aktarın.", + bundles: "Çevrimdışı Paketler", + bundlesDesc: "Agent kaydını, veritabanı sürücülerini, yerel agent'ları ve uygun JRE'yi içeren, platforma özgü ZIP paketleri.", + drivers: "Veritabanı Sürücüleri", + driversDesc: "Java agent'ları için tek sürücülük .tar.zst paketleri. Hedef makinede yoksa uygun JRE'yi ayrıca kurun.", + nativeAgents: "Yerel Agent'lar", + nativeAgentsDesc: "DuckDB, Oracle, KingbaseES, XuguDB ve RabbitMQ için platforma özgü .tar.zst paketleri. Paketi doğrudan Sürücü Yöneticisi'nde içe aktarın.", + jre: "Java Çalışma Zamanı (JRE)", + jreDesc: "SQL Server ve Dameng gibi Java agent tabanlı veritabanı sürücülerinin kullandığı JRE paketleri.", + download: "İndir", + installMethod: "Kurulum", + version: "Sürüm", + size: "Boyut", + requiresJre: "JRE gerektirir", + platform: "Platform", + filename: "Dosya", + search: "Sürücü, platform, sürüm ara...", + noResults: "Eşleşen indirme yok.", + showing: "Gösterilen", + of: "/", + clearSearch: "Aramayı temizle", + mirrorHint: "CNB aynası, Çin anakarası ağlarında GitHub sürüm dosyaları için kullanılabilir.", + downloadSources: "İndirme kaynağı", + sources: { + github: "GitHub", + cnb: "CNB", + official: "Resmî", + }, + downloadHint: "İnternet erişimi olmayan ortamlar için: platformunuza uygun paketi internet bağlantısı olan bir makinede indirin, çevrimdışı makineye taşıyın ve DBX'te Ayarlar > Sürücü Yöneticisi bölümünden içe aktarın. Sürücü ve JRE sekmelerini yalnızca tek tek dosyalara ihtiyacınız olduğunda kullanın.", + }, en: { title: "Offline Driver Downloads", subtitle: "Download database drivers and JRE packages for offline use. Search for the exact resource your air-gapped environment needs.", @@ -102,6 +139,12 @@ type NativeAgentGroup = { options: NativeAgentDisplayEntry[]; }; +const OFFLINE_HEADING: Record = { + en: "Offline Usage", + cn: "离线使用说明", + tr: "Çevrimdışı kullanım", +}; + type DriverTranslations = (typeof i18n)["en"]; function DownloadLinks({ url, t }: { url: string; t: DriverTranslations }) { @@ -131,7 +174,7 @@ function matchesSearch(values: Array, query: string export function DriversClient({ initialCatalog }: { initialCatalog: AgentDownloadCatalog }) { const params = useParams(); const rawLang = params?.lang as string | undefined; - const lang: "en" | "cn" = rawLang === "cn" ? "cn" : "en"; + const lang = resolveLang(rawLang ?? "en"); const t = i18n[lang]; const catalog = initialCatalog; @@ -480,7 +523,7 @@ export function DriversClient({ initialCatalog }: { initialCatalog: AgentDownloa
- {lang === "cn" ? "离线使用说明" : "Offline Usage"} + {OFFLINE_HEADING[lang]}

{t.downloadHint}

diff --git a/docs/app/[lang]/drivers/page.tsx b/docs/app/[lang]/drivers/page.tsx index 49126be6ed..5bbdc0fc3e 100644 --- a/docs/app/[lang]/drivers/page.tsx +++ b/docs/app/[lang]/drivers/page.tsx @@ -2,12 +2,17 @@ import { DriversClient } from "./DriversClient"; import { fetchAgentDownloadCatalog } from "@/lib/agentRegistrySource"; import { buildMetadata } from "@/lib/metadata"; import type { Metadata } from "next"; +import { resolveLang } from "@/lib/i18n"; const pageMeta = { en: { title: "Offline Driver Downloads", description: "Download DBX offline driver bundles, database drivers, and JRE packages for air-gapped environments across macOS, Linux, and Windows.", }, + tr: { + title: "Çevrimdışı Sürücü İndirmeleri", + description: "İnternet erişimi olmayan ortamlar için DBX çevrimdışı sürücü paketlerini, veritabanı sürücülerini ve JRE paketlerini macOS, Linux ve Windows için indirin.", + }, cn: { title: "离线驱动下载", description: "下载 DBX 离线驱动整包、数据库驱动和 JRE 离线包,覆盖 macOS、Linux、Windows 平台。", @@ -16,7 +21,7 @@ const pageMeta = { export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { const { lang } = await params; - const l = lang === "cn" ? "cn" : "en"; + const l = resolveLang(lang); const meta = pageMeta[l]; return buildMetadata({ diff --git a/docs/app/[lang]/issue/page.tsx b/docs/app/[lang]/issue/page.tsx index 810e2f63ea..fd30b1c50a 100644 --- a/docs/app/[lang]/issue/page.tsx +++ b/docs/app/[lang]/issue/page.tsx @@ -3,12 +3,17 @@ import { LandingFooter } from "@/components/landing/LandingFooter"; import { LandingNav } from "@/components/landing/LandingNav"; import { IssueSubmissionClient } from "@/components/issues/IssueSubmissionClient"; import { buildMetadata } from "@/lib/metadata"; +import { resolveLang } from "@/lib/i18n"; const metadata = { en: { title: "Submit an Issue", description: "Describe a DBX problem or suggestion without a GitHub account. Review the AI-polished draft before publishing.", }, + tr: { + title: "Issue Gönder", + description: "GitHub hesabı olmadan bir DBX sorununu veya önerisini anlatın. Yayımlamadan önce yapay zekânın düzenlediği taslağı inceleyin.", + }, cn: { title: "匿名提交 Issue", description: "无需 GitHub 账号,简短描述 DBX 问题或建议,由 AI 整理并在你确认后公开提交。", @@ -17,7 +22,7 @@ const metadata = { export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { const { lang } = await params; - const locale = lang === "cn" ? "cn" : "en"; + const locale = resolveLang(lang); const pageMetadata = buildMetadata({ title: metadata[locale].title, description: metadata[locale].description, @@ -36,7 +41,7 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: str export default async function IssuePage({ params }: { params: Promise<{ lang: string }> }) { const { lang } = await params; - const locale = lang === "cn" ? "cn" : "en"; + const locale = resolveLang(lang); return (
diff --git a/docs/app/[lang]/layout.tsx b/docs/app/[lang]/layout.tsx index a0a3729e96..3b2ea928ca 100644 --- a/docs/app/[lang]/layout.tsx +++ b/docs/app/[lang]/layout.tsx @@ -4,6 +4,7 @@ import type { Metadata, Viewport } from "next"; import { RouteProgress } from "@/components/RouteProgress"; import { buildMetadata, DEFAULT_DESCRIPTION, getHtmlLang, SITE_NAME, SITE_URL } from "@/lib/metadata"; import { buildSiteStructuredData } from "@/lib/structuredData"; +import { i18n, resolveLang } from "@/lib/i18n"; const LOCALE_MAP: Record = { en: { @@ -16,11 +17,16 @@ const LOCALE_MAP: Record }): Promise { const { lang } = await params; - const l = lang === "cn" ? "cn" : "en"; + const l = resolveLang(lang); const meta = LOCALE_MAP[l]; const pageMetadata = buildMetadata({ @@ -57,7 +63,7 @@ export const viewport: Viewport = { export default async function LangLayout({ params, children }: { params: Promise<{ lang: string }>; children: ReactNode }) { const { lang } = await params; - const locale = lang === "cn" ? "cn" : "en"; + const locale = resolveLang(lang); const siteStructuredData = buildSiteStructuredData(); return ( @@ -82,5 +88,5 @@ export default async function LangLayout({ params, children }: { params: Promise } export function generateStaticParams() { - return [{ lang: "en" }, { lang: "cn" }]; + return i18n.languages.map((lang) => ({ lang })); } diff --git a/docs/app/[lang]/page.tsx b/docs/app/[lang]/page.tsx index a8613033c9..3ab299541e 100644 --- a/docs/app/[lang]/page.tsx +++ b/docs/app/[lang]/page.tsx @@ -18,6 +18,7 @@ import { fetchLatestReleaseInfo } from "@/lib/latestRelease"; import { buildMetadata, getHtmlLang } from "@/lib/metadata"; import { buildSoftwareApplicationStructuredData } from "@/lib/structuredData"; import { ArrowRight, Bot, Database, FileCode, GitCompare, Network, Search, Shield, Table, Terminal, Zap } from "lucide-react"; +import { resolveLang, type DocsLang } from "@/lib/i18n"; function formatStars(count: number) { if (count >= 1000) { @@ -29,6 +30,12 @@ function formatStars(count: number) { function metrics(starLabel: string) { return { + tr: [ + { value: "~20 MB", label: "masaüstü yükleyici" }, + { value: "90+", label: "veritabanı motoru" }, + { value: "2 mod", label: "masaüstü ve Docker" }, + { value: starLabel, label: "GitHub yıldızı, tümüyle açık kaynak" }, + ], en: [ { value: "~20 MB", label: "desktop installer" }, { value: "90+", label: "database engines" }, @@ -45,6 +52,32 @@ function metrics(starLabel: string) { } const workflows = { + tr: [ + { + icon: Terminal, + title: "SQL yazın ve çalıştırın", + desc: "Meta veri farkındalıklı tamamlama, biçimlendirme, geçmiş ve seçili SQL çalıştırma sunan bir CodeMirror 6 düzenleyicisi.", + href: "/tr/docs/query-editor", + }, + { + icon: Table, + title: "Veriye gözatın ve düzenleyin", + desc: "Sanallaştırılmış ızgaralar, satır içi düzenleme, WHERE/ORDER BY denetimleri, SQL önizlemesi ve dışa aktarma araçları.", + href: "/tr/docs/data-grid", + }, + { + icon: Search, + title: "Şemaları keşfedin", + desc: "Veritabanları, şemalar, tablolar, sütunlar, dizinler, yabancı anahtarlar ve tetikleyiciler arasında sade bir kenar çubuğundan gezinin.", + href: "/tr/docs/schema-browser", + }, + { + icon: GitCompare, + title: "Karşılaştırın ve taşıyın", + desc: "Şema karşılaştırma, tablo içe aktarma, veritabanı dışa aktarma, SQL dosyası çalıştırma ve motorlar arası veri aktarımı.", + href: "/tr/docs/schema-diff", + }, + ], en: [ { icon: Terminal, @@ -100,6 +133,14 @@ const workflows = { }; const capabilities = { + tr: [ + { icon: Database, label: "Yerel Rust sürücüleri, JDBC çalışma zamanı gerekmez" }, + { icon: Shield, label: "SSH tünelleri, şifreli yapılandırma dışa aktarımı, yıkıcı işlem korumaları" }, + { icon: Bot, label: "Yapay zekâ asistanı ve Claude Code, Cursor ile agent'lar için MCP sunucusu" }, + { icon: Network, label: "Daha derin analiz için ER diyagramları, şema karşılaştırma ve alan soy ağacı" }, + { icon: FileCode, label: "CSV, Excel, SQL dosyaları, tam dışa aktarım ve motorlar arası aktarım" }, + { icon: Zap, label: "Aynı projeden masaüstü uygulaması ve kendi sunucunuzda web dağıtımı" }, + ], en: [ { icon: Database, label: "Native Rust drivers, no JDBC runtime" }, { icon: Shield, label: "SSH tunnels, encrypted config export, destructive action guards" }, @@ -281,7 +322,55 @@ const testimonials = { ], }; +const METRICS_LABEL: Record = { + en: "DBX key metrics", + cn: "DBX 核心指标", + tr: "DBX temel ölçümleri", +}; + +// Testimonials are direct quotes from named people, so the Turkish page reuses +// the English set verbatim instead of translating what they said. +const localizedTestimonials = { ...testimonials, tr: testimonials.en }; + const i18nText = { + tr: { + heroTitle: "90+ veritabanını 20 MB ile yönetin!", + heroSubtitle: "DBX; bağlantı yönetimini, SQL düzenlemeyi, veri tablolarını, şema araçlarını, yapay zekâ desteğini ve kendi sunucunuzda barındırmayı tek bir hafif üründe toplar.", + download: "DBX'i indir", + downloadName: "DBX'i indir", + readDocs: "Dokümanları okuyun", + docsStart: "Buradan başlayın", + docsStartDesc: "DBX'i kurun, ilk bağlantınızı oluşturun ve temel iş akışını öğrenin.", + workflowsTitle: "Temel iş akışları", + workflowsDesc: "Dokümanlar, bir veritabanı istemcisinde gerçekten yaptığınız işlere göre düzenlenmiştir.", + supportTitle: "90+ veritabanını destekler", + supportDesc: "SQL, NoSQL, vektör, zaman serisi ve gömülü veritabanlarını, mesaj kuyruklarını ve uyumlu motorları tek yerden bağlayın.", + supportLink: "Tümünü görüntüle", + testimonialsTitle: "DBX ne işe yarar", + testimonialsDesc: "DBX'in kolaylaştırmak için tasarlandığı günlük veritabanı iş akışlarına daha yakından bir bakış.", + capabilitiesTitle: "Gerçek veritabanı işleri için tasarlandı", + contributorsTitle: "Toplulukla birlikte geliştirildi", + contributorsDesc: "DBX tümüyle açık kaynaktır. Her özellik, düzeltme ve sürücü bir katkıcıyla başlar.", + sponsorLabel: "Sponsorlar ve İş Ortakları", + qiniuSponsorDesc: "Qiniu Cloud, DBX'e nesne depolama, CDN ve diğer bulut altyapı kaynaklarını sağlıyor.", + qiniuSponsorAction: "Qiniu Cloud'u ziyaret edin", + rainyunSponsorDesc: "RainYun; bulut sunucular, fiziksel sunucular, oyun barındırma ve geliştirici dostu altyapı hizmetleri sunan bir bulut servis sağlayıcısıdır.", + rainyunSponsorAction: "RainYun'u ziyaret edin", + easysearchSponsorDesc: "Easysearch, Elasticsearch API'leriyle uyumlu kurumsal düzeyde dağıtık bir arama motorudur; tam metin, vektör ve coğrafi aramayı, gerçek zamanlı analitiği ve yapay zekâ yeteneklerini tek platformda birleştirir.", + easysearchSponsorAction: "Easysearch'ü ziyaret edin", + atlasCloudSponsorDesc: "Atlas Cloud, geliştiricilere sohbet, görsel, video ve ses alanlarında 400+ yapay zekâ modeli için tek ve birleşik bir API sunar.", + atlasCloudSponsorAction: "Atlas Cloud'u ziyaret edin", + trustasiaSponsorDesc: "TrustAsia, DBX için bulut tabanlı kod imzalama hizmeti sağlayarak otomatik CI/CD derlemeleriyle güvenilir yazılım üretilmesini sağlıyor.", + trustasiaSponsorAction: "TrustAsia'yı ziyaret edin", + jalapenoSponsorDesc: "Jalapeño Cloud, yapay zekâ altyapısı ve belirteç hesaplama platformudur; DBX'e özel giriş noktasıyla ücretsiz kredi ve yükleme bonusu sunar.", + jalapenoSponsorAction: "Jalapeño Cloud'u ziyaret edin", + astraflowSponsorDesc: "UCloud, Çin'in STAR Market borsasına kote ilk genel bulut sağlayıcısıdır; 28 küresel bölgede bulut sunucu, veritabanı ve CDN hizmeti verir. AstraFlow platformu 200+ yaygın büyük dil modeline tek tıklamayla erişim sağlar.", + astraflowSponsorAction: "UCloud'u ziyaret edin", + footerTitle: "DBX'i denemeye hazır mısınız?", + footerDesc: "Yerel çalışma için masaüstü uygulamasını kullanın ya da tarayıcıdan erişim için Docker sürümünü dağıtın.", + release: "En son sürüm", + docker: "Docker kurulumu", + }, en: { heroTitle: "20 MB to manage 90+ databases!", heroSubtitle: "DBX brings connections, SQL editing, data grids, schema tools, AI assistance, and self-hosted access into one lightweight product.", @@ -361,6 +450,10 @@ const i18nText = { }; const landingMeta = { + tr: { + title: "DBX - 90+ veritabanını 20 MB ile yönetin!", + description: "DBX; bağlantı yönetimini, SQL düzenlemeyi, veri tablolarını, şema araçlarını, yapay zekâ desteğini ve kendi sunucunuzda barındırmayı tek bir hafif üründe toplar.", + }, en: { title: "DBX - 20 MB to manage 90+ databases!", description: "DBX brings connections, SQL editing, data grids, schema tools, AI assistance, and self-hosted access into one lightweight product.", @@ -373,7 +466,7 @@ const landingMeta = { export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { const { lang } = await params; - const l = lang === "cn" ? "cn" : "en"; + const l = resolveLang(lang); const meta = landingMeta[l]; return buildMetadata({ @@ -387,7 +480,7 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: str export default async function LandingPage({ params }: { params: Promise<{ lang: string }> }) { const { lang } = await params; - const l = lang === "cn" ? "cn" : "en"; + const l = resolveLang(lang); const t = i18nText[l]; const workflowItems = workflows[l]; const capabilityItems = capabilities[l]; @@ -398,7 +491,7 @@ export default async function LandingPage({ params }: { params: Promise<{ lang: const initialLatestRelease = await fetchLatestReleaseInfo(); const contributors = contributorsFromActivity(contributorData.contributors); const initialDownloadVersion = initialLatestRelease?.version ?? appVersion; - const testimonialItems = testimonials[l]; + const testimonialItems = localizedTestimonials[l]; const softwareStructuredData = buildSoftwareApplicationStructuredData(l, initialDownloadVersion); const sponsorItems = [ { @@ -483,7 +576,7 @@ export default async function LandingPage({ params }: { params: Promise<{ lang: {/* Metrics */} - + {metricItems.map((item) => (
{item.value} diff --git a/docs/app/[lang]/sponsors/page.tsx b/docs/app/[lang]/sponsors/page.tsx index a9a6376483..35e46624e1 100644 --- a/docs/app/[lang]/sponsors/page.tsx +++ b/docs/app/[lang]/sponsors/page.tsx @@ -4,6 +4,7 @@ import { LandingFooter } from "@/components/landing/LandingFooter"; import { LandingNav } from "@/components/landing/LandingNav"; import { SponsorContactForm } from "@/components/landing/SponsorContactForm"; import { buildMetadata } from "@/lib/metadata"; +import { resolveLang } from "@/lib/i18n"; const i18n = { en: { @@ -26,6 +27,26 @@ const i18n = { becomeTitle: "Sponsorship inquiries", becomeDesc: "If you would like to support DBX with funding, infrastructure, developer tools, or services, tell us about the idea and how to reach you.", }, + tr: { + title: "Sponsorlar ve İş Ortakları", + desc: "DBX'i finansman, kaynak, hizmet ve iş birliğiyle destekleyen sponsorlara ve iş ortaklarına teşekkür ederiz.", + qiniuSponsorDesc: "Qiniu Cloud, DBX'e nesne depolama, CDN ve diğer bulut altyapı kaynaklarını sağlıyor.", + qiniuSponsorAction: "Qiniu Cloud'u ziyaret edin", + rainyunSponsorDesc: "RainYun; bulut sunucular, fiziksel sunucular, oyun barındırma ve geliştirici dostu altyapı hizmetleri sunan bir bulut servis sağlayıcısıdır.", + rainyunSponsorAction: "RainYun'u ziyaret edin", + easysearchSponsorDesc: "Easysearch, Elasticsearch API'leriyle uyumlu kurumsal düzeyde dağıtık bir arama motorudur; tam metin, vektör ve coğrafi aramayı, gerçek zamanlı analitiği ve yapay zekâ yeteneklerini tek platformda birleştirir.", + easysearchSponsorAction: "Easysearch'ü ziyaret edin", + atlasCloudSponsorDesc: "Atlas Cloud, geliştiricilere sohbet, görsel, video ve ses alanlarında 400+ yapay zekâ modeli için tek ve birleşik bir API sunar.", + atlasCloudSponsorAction: "Atlas Cloud'u ziyaret edin", + trustasiaSponsorDesc: "TrustAsia, DBX için bulut tabanlı kod imzalama hizmeti sağlayarak otomatik CI/CD derlemeleriyle güvenilir yazılım üretilmesini sağlıyor.", + trustasiaSponsorAction: "TrustAsia'yı ziyaret edin", + jalapenoSponsorDesc: "Jalapeño Cloud, yapay zekâ altyapısı ve belirteç hesaplama platformudur; DBX'e özel giriş noktasıyla ücretsiz kredi ve yükleme bonusu sunar.", + jalapenoSponsorAction: "Jalapeño Cloud'u ziyaret edin", + astraflowSponsorDesc: "UCloud, Çin'in STAR Market borsasına kote ilk genel bulut sağlayıcısıdır; 28 küresel bölgede bulut sunucu, veritabanı ve CDN hizmeti verir. AstraFlow platformu 200+ yaygın büyük dil modeline tek tıklamayla erişim sağlar.", + astraflowSponsorAction: "UCloud'u ziyaret edin", + becomeTitle: "Sponsorluk başvurusu", + becomeDesc: "DBX'i finansman, altyapı, geliştirici araçları veya hizmetlerle desteklemek isterseniz fikrinizi ve size nasıl ulaşabileceğimizi yazın.", + }, cn: { title: "赞助商与合作伙伴", desc: "感谢赞助商与合作伙伴通过资金、资源、服务和协作等方式支持 DBX 持续发展。", @@ -50,7 +71,7 @@ const i18n = { export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { const { lang } = await params; - const locale = lang === "cn" ? "cn" : "en"; + const locale = resolveLang(lang); const t = i18n[locale]; return buildMetadata({ @@ -63,7 +84,7 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: str export default async function SponsorsPage({ params }: { params: Promise<{ lang: string }> }) { const { lang } = await params; - const locale = lang === "cn" ? "cn" : "en"; + const locale = resolveLang(lang); const t = i18n[locale]; const sponsorItems = [ { diff --git a/docs/components/contributors/ContributorsExperience.tsx b/docs/components/contributors/ContributorsExperience.tsx index f79360bbf4..b869e2bffd 100644 --- a/docs/components/contributors/ContributorsExperience.tsx +++ b/docs/components/contributors/ContributorsExperience.tsx @@ -4,8 +4,9 @@ import { useEffect, useMemo, useState } from "react"; import { Award, Download, ExternalLink, Github, LogOut, Search, ShieldCheck } from "lucide-react"; import type { ContributorActivity, ContributorActivityData } from "@/lib/contributorActivity"; import styles from "./ContributorsExperience.module.css"; +import type { DocsLang } from "@/lib/i18n"; -type Language = "en" | "cn"; +type Language = DocsLang; type AuthUser = { login: string; avatarUrl: string; profileUrl: string }; type AuthState = { status: "loading" } | { status: "signed-out" } | { status: "signed-in"; user: AuthUser }; @@ -38,6 +39,34 @@ const copy = { mergedLabel: "MERGED PULL REQUESTS", verified: "GITHUB IDENTITY VERIFIED", }, + tr: { + title: "Emeğin DBX'in bir parçası.", + intro: "Kimliğini doğrulamak ve DBX'e merge edilen pull request'lerin için paylaşılabilir bir sertifika almak üzere GitHub ile giriş yap.", + signIn: "GitHub ile doğrula", + signingIn: "GitHub oturumu denetleniyor…", + contributors: "doğrulanmış katkıcı", + merged: "merge edilmiş pull request", + commits: "depo commit'i", + stars: "GitHub yıldızı", + directory: "Katkıcı dizini", + search: "GitHub kullanıcı adı ara", + showAll: "Tüm katkıcıları göster", + showLess: "Daha az katkıcı göster", + noResults: "Eşleşen katkıcı yok.", + signedInAs: "Doğrulanan hesap", + claim: "Sertifikamı aç", + notEligible: "Bu GitHub hesabının DBX'te merge edilmiş bir pull request'i henüz yok.", + contributionCallout: "Odaklı bir pull request gönder ve bu duvara katıl.", + viewGuide: "Katkı kılavuzu", + signOut: "Çıkış yap", + certificate: "Açık kaynak katkıcısı", + awardedTo: "Sunulan kişi", + certificateBody: "DBX'e kabul edilip merge edilen çalışman için; veritabanı araçlarını herkes için daha iyi hâle getirdiğin için teşekkürler.", + download: "Sertifikayı indir", + close: "Kapat", + mergedLabel: "MERGE EDİLEN PULL REQUEST", + verified: "GITHUB KİMLİĞİ DOĞRULANDI", + }, cn: { title: "你的代码,已经成为 DBX 的一部分。", intro: "使用 GitHub 验证身份,领取一张属于你的贡献者证书,记录被 DBX 接受并合并的 Pull Request。", @@ -68,8 +97,10 @@ const copy = { }, } as const; +const NUMBER_LOCALE: Record = { en: "en-US", cn: "zh-CN", tr: "tr-TR" }; + function formatNumber(value: number, lang: Language) { - return new Intl.NumberFormat(lang === "cn" ? "zh-CN" : "en-US").format(value); + return new Intl.NumberFormat(NUMBER_LOCALE[lang]).format(value); } function initials(login: string) { diff --git a/docs/components/issues/IssueSubmissionClient.tsx b/docs/components/issues/IssueSubmissionClient.tsx index f195900033..199338602a 100644 --- a/docs/components/issues/IssueSubmissionClient.tsx +++ b/docs/components/issues/IssueSubmissionClient.tsx @@ -2,6 +2,7 @@ import { ArrowUpRight, Check, CheckCircle2, ImagePlus, LoaderCircle, RotateCcw, ShieldAlert, Sparkles, X } from "lucide-react"; import { useEffect, useRef, useState, type ClipboardEvent, type DragEvent } from "react"; +import type { DocsLang } from "@/lib/i18n"; type IssueType = "bug" | "feature" | "question" | "compatibility"; @@ -31,6 +32,58 @@ type SubmitResponse = { }; const copy = { + tr: { + draftStep: "Sorunu anlatın", + previewStep: "Issue önizlemesi", + descriptionLabel: "Ne oldu?", + descriptionHint: "Ne yaptığınızı, ne gördüğünüzü ve ne beklediğinizi yazın. Sürüm veya ortam ayrıntısını bilmiyorsanız tahmin etmeyin.", + descriptionPlaceholder: "Örnek: macOS'ta bir PostgreSQL şemasını genişletince hata vermeden sürekli yükleniyor. Yeniden başlatmak ve yeniden bağlanmak işe yaramadı.", + attachments: "Ekran görüntüleri (isteğe bağlı)", + attachmentHint: "Ekran görüntülerini buraya seçin, sürükleyin ya da yapıştırın. PNG, JPEG veya WebP; en fazla 3 görsel ve her biri 5 MB.", + addImages: "Ekran görüntüsü ekle", + generate: "Issue taslağı hazırla", + generateAgain: "Yeniden hazırla", + generating: "Taslak hazırlanıyor", + type: "Tür", + titleLabel: "Başlık", + bodyLabel: "GitHub gövdesi", + bodyHint: "Tümüyle düzenlenebilir. Gönderim sırasında görseller ve anonim kaynak notu sona eklenir.", + summary: "Taslak özeti", + confirm: "Başlığı, gövdeyi ve ekran görüntülerini inceledim; bunların GitHub'da herkese açık görüneceğini anlıyorum.", + submit: "Onayla ve herkese açık Issue oluştur", + submitting: "Issue oluşturuluyor", + publicWarning: "Göndermeden önce", + publicNotice: "Parola, belirteç, bağlantı dizesi, maskelenmemiş IP, müşteri verisi veya kişisel veri yüklemeyin. Issue ve görseller herkese açık olacak.", + successTitle: "Issue oluşturuldu", + successText: (number: number) => `GitHub Issue #${number} oluşturuldu. Sonraki gelişmeler o herkese açık sayfada kaydedilecek.`, + openIssue: "GitHub Issue'yu aç", + another: "Bir tane daha gönder", + removeImage: "Görseli kaldır", + errors: { + DESCRIPTION_REQUIRED: "Önce sorunu anlatın.", + DESCRIPTION_TOO_SHORT: "Açıklama en az 4 karakter içermelidir.", + DESCRIPTION_TOO_LONG: "Açıklamayı 6.000 karakterin altında tutun.", + TOO_MANY_IMAGES: "En fazla 3 görsel yükleyebilirsiniz.", + IMAGE_TOO_LARGE: "Her görsel en fazla 5 MB olmalıdır.", + IMAGES_TOO_LARGE: "Görsellerin toplamı en fazla 12 MB olmalıdır.", + IMAGE_TYPE_UNSUPPORTED: "Yalnızca gerçek PNG, JPEG ve WebP görselleri desteklenir.", + RATE_LIMITED: "Çok fazla taslak denemesi yapıldı. Lütfen daha sonra tekrar deneyin.", + AI_NOT_CONFIGURED: "Yapay zekâ taslak servisi henüz yapılandırılmadı.", + AI_REQUEST_FAILED: "Yapay zekâ servisi geçici olarak kullanılamıyor. Daha sonra tekrar deneyin.", + AI_REQUEST_TIMEOUT: "Ekran görüntüsü çözümlemesi çok uzun sürdü. Tekrar deneyin ya da daha az görsel ekleyin.", + AI_RESPONSE_INVALID: "Yapay zekâ geçersiz bir taslak döndürdü. Yeniden üretin.", + DRAFT_EXPIRED: "Bu taslağın süresi doldu. Yenisini üretin.", + DRAFT_SUBMITTING: "Bu taslak zaten gönderiliyor.", + DRAFT_IMAGES_CHANGED: "Taslak üretildikten sonra ekran görüntüleri değişti. Taslağı yeniden üretin.", + ISSUE_SESSION_EXPIRED: "Geçici oturumun süresi doldu. Taslağı yeniden üretin.", + GITHUB_ISSUE_CREATE_FAILED: "GitHub Issue'yu oluşturamadı. Daha sonra tekrar deneyin.", + ISSUE_IMAGE_UPLOAD_FAILED: "Ekran görüntüleri yüklenemedi. Daha sonra tekrar deneyin.", + ISSUE_REQUEST_FAILED: "Gönderim başarısız. Daha sonra tekrar deneyin.", + }, + genericError: "İstek başarısız oldu. Daha sonra tekrar deneyin.", + clientImageError: "En fazla 3 PNG, JPEG veya WebP görseli kullanın; her biri en çok 5 MB.", + retryAfter: (minutes: number) => `Yaklaşık ${minutes} dakika sonra tekrar deneyin.`, + }, cn: { draftStep: "问题描述", previewStep: "Issue 预览", @@ -144,14 +197,14 @@ const issuePrefixes: Record = { compatibility: "[Compatibility]", }; -function responseErrorMessage(lang: "cn" | "en", code: string, retryAfter?: number): string { +function responseErrorMessage(lang: DocsLang, code: string, retryAfter?: number): string { const t = copy[lang]; const base = t.errors[code as keyof typeof t.errors] ?? t.genericError; if (code !== "RATE_LIMITED" || !retryAfter) return base; return `${base} ${t.retryAfter(Math.max(1, Math.ceil(retryAfter / 60)))}`; } -export function IssueSubmissionClient({ lang }: { lang: "cn" | "en" }) { +export function IssueSubmissionClient({ lang }: { lang: DocsLang }) { const t = copy[lang]; const [description, setDescription] = useState(""); const [images, setImages] = useState([]); diff --git a/docs/components/landing/ChangelogList.tsx b/docs/components/landing/ChangelogList.tsx index 81bfefac78..76c46ba289 100644 --- a/docs/components/landing/ChangelogList.tsx +++ b/docs/components/landing/ChangelogList.tsx @@ -3,13 +3,53 @@ import { marked, Renderer } from "marked"; import type { ChangelogIndexEntry, ChangelogRelease } from "@/lib/changelog"; import { Tag } from "lucide-react"; +import type { DocsLang } from "@/lib/i18n"; const sectionLabels: Record> = { - added: { en: "New Features", cn: "新功能" }, - improved: { en: "Improvements", cn: "改进" }, - fixed: { en: "Bug Fixes", cn: "问题修复" }, - changed: { en: "Changes", cn: "变更" }, - removed: { en: "Removed", cn: "移除" }, + added: { en: "New Features", cn: "新功能", tr: "Yeni Özellikler" }, + improved: { en: "Improvements", cn: "改进", tr: "İyileştirmeler" }, + fixed: { en: "Bug Fixes", cn: "问题修复", tr: "Hata Düzeltmeleri" }, + changed: { en: "Changes", cn: "变更", tr: "Değişiklikler" }, + removed: { en: "Removed", cn: "移除", tr: "Kaldırılanlar" }, +}; + +const listText: Record string; tocSubtitle: string; currentContents: string }> = { + en: { + publishedOn: "Published on", + download: "Download", + seeGitHub: "See GitHub Release for details", + loading: "Loading release…", + releaseList: "Release list", + versions: "Versions", + content: "Changelog content", + tocTitle: (tag) => `In ${tag}`, + tocSubtitle: "Release contents", + currentContents: "Current release contents", + }, + cn: { + publishedOn: "发布于", + download: "下载", + seeGitHub: "查看 GitHub Release 获取详情", + loading: "正在加载版本…", + releaseList: "版本列表", + versions: "版本列表", + content: "更新日志内容", + tocTitle: (tag) => `在 ${tag} 中`, + tocSubtitle: "更新内容", + currentContents: "当前版本目录", + }, + tr: { + publishedOn: "Yayımlanma", + download: "İndir", + seeGitHub: "Ayrıntılar için GitHub Release sayfasına bakın", + loading: "Sürüm yükleniyor…", + releaseList: "Sürüm listesi", + versions: "Sürümler", + content: "Değişiklik günlüğü içeriği", + tocTitle: (tag) => `${tag} içinde`, + tocSubtitle: "Sürüm içeriği", + currentContents: "Geçerli sürüm içeriği", + }, }; // 与 .github/scripts/sync-changelog.mjs 的 SECTION_MAP 保持一致, @@ -27,12 +67,14 @@ const sectionTypeByTitle: Record = { Removed: "removed", }; -function formatDate(dateStr: string, lang: string) { +const DATE_LOCALE: Record = { en: "en-US", cn: "zh-CN", tr: "tr-TR" }; + +function formatDate(dateStr: string, lang: DocsLang) { const date = new Date(dateStr); if (lang === "cn") { return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`; } - return date.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }); + return date.toLocaleDateString(DATE_LOCALE[lang], { year: "numeric", month: "long", day: "numeric" }); } function releaseId(tag: string) { @@ -105,7 +147,7 @@ function renderMarkdown(release: ChangelogRelease) { // TOC 必须与 renderMarkdown 数的是同一批 h3(跳过代码围栏里的 "### "), // 否则 parseBody 过滤掉无条目小节后,锚点序号会整体错位。 -function buildTocEntries(release: ChangelogRelease, lang: string) { +function buildTocEntries(release: ChangelogRelease, lang: DocsLang) { const markdown = releaseDisplayMarkdown(release); const titles: string[] = []; let inFence = false; @@ -127,14 +169,14 @@ function buildTocEntries(release: ChangelogRelease, lang: string) { })); } -function ReleaseCard({ release, lang, isLoading, errorMessage }: { release: ChangelogRelease | null; lang: "en" | "cn"; isLoading: boolean; errorMessage?: string }) { - const text = lang === "cn" ? { publishedOn: "发布于", download: "下载", seeGitHub: "查看 GitHub Release 获取详情" } : { publishedOn: "Published on", download: "Download", seeGitHub: "See GitHub Release for details" }; +function ReleaseCard({ release, lang, isLoading, errorMessage }: { release: ChangelogRelease | null; lang: DocsLang; isLoading: boolean; errorMessage?: string }) { + const text = listText[lang]; if (!release) { return (
- {errorMessage || (isLoading ? (lang === "cn" ? "正在加载版本…" : "Loading release…") : text.seeGitHub)} + {errorMessage || (isLoading ? text.loading : text.seeGitHub)}
); @@ -166,17 +208,18 @@ function ReleaseCard({ release, lang, isLoading, errorMessage }: { release: Chan ); } -export function ChangelogList({ releaseIndex, selectedTag, release, lang, isLoading, errorMessage, onSelectRelease }: { releaseIndex: ChangelogIndexEntry[]; selectedTag: string; release: ChangelogRelease | null; lang: "en" | "cn"; isLoading: boolean; errorMessage?: string; onSelectRelease: (tag: string) => void }) { +export function ChangelogList({ releaseIndex, selectedTag, release, lang, isLoading, errorMessage, onSelectRelease }: { releaseIndex: ChangelogIndexEntry[]; selectedTag: string; release: ChangelogRelease | null; lang: DocsLang; isLoading: boolean; errorMessage?: string; onSelectRelease: (tag: string) => void }) { + const t = listText[lang]; const activeTag = release?.tag || selectedTag || releaseIndex[0]?.tag; const tocEntries = release ? buildTocEntries(release, lang) : []; return (
-