feat: localize homepage markdown content (#567)
This commit is contained in:
parent
9b17cca94a
commit
de0d38ac7d
6
dashboard/content/homepage/en/community.md
Normal file
6
dashboard/content/homepage/en/community.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: "Community Highlights"
|
||||
---
|
||||
- **Multi-cloud policy roll-out stories** — How one platform team implemented chargeback in six months.
|
||||
- **Kubernetes observability best practices** — Deploying XScopeHub alongside OpenTelemetry collectors.
|
||||
- **Automated compliance checklists** — 30+ actionable policy rules contributed by the community.
|
||||
6
dashboard/content/homepage/en/news.md
Normal file
6
dashboard/content/homepage/en/news.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: "Product & Community Updates"
|
||||
---
|
||||
- **2024-06-18 · Release 1.8** — Terraform Cloud integration for policy delivery plus new multi-cluster blueprints and security baselines.
|
||||
- **2024-05-22 · Live Series** — A recap on evolving from automated inspections to a unified operations experience.
|
||||
- **2024-04-30 · Community Contributor Day** — Welcomed 12 new maintainers building plugins and data onboarding workflows.
|
||||
9
dashboard/content/homepage/en/operations.md
Normal file
9
dashboard/content/homepage/en/operations.md
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
title: "Cloud-Native Operations Hub"
|
||||
updated: "2024-06-18"
|
||||
---
|
||||
Build an integrated **XControl** control plane that unifies resource governance, intelligent operations, policy automation, observability, and interactive service workflows in a single, trusted workspace.
|
||||
|
||||
- Unified policy management across clusters and clouds
|
||||
- Event-driven runbooks for daily operations and inspections
|
||||
- Adaptive compliance monitoring with complete audit trails
|
||||
26
dashboard/content/homepage/en/products.md
Normal file
26
dashboard/content/homepage/en/products.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
title: "Product Spotlights"
|
||||
---
|
||||
### XCloudFlow
|
||||
A multi-cloud IaC workbench that brings Terraform, Pulumi, and GitOps models together for environment-as-a-service delivery.
|
||||
|
||||
- [Try the product](/demo?product=xcloudflow)
|
||||
- [Download builds](/download?product=xcloudflow)
|
||||
- [Read the docs](/docs/xcloudflow)
|
||||
|
||||
### XControl
|
||||
The unified control plane for platform teams with service orchestration, identity governance, and chargeback insights.
|
||||
|
||||
- [Try the product](/demo?product=xcontrol)
|
||||
- [Read the docs](/docs/xcontrol)
|
||||
|
||||
### XScopeHub
|
||||
An intelligent observability and alert collaboration hub offering topology views, root cause analysis, and multi-channel notifications.
|
||||
|
||||
- [Request a demo](/demo?product=xscopehub)
|
||||
- [Read the docs](/docs/xscopehub)
|
||||
|
||||
### XStream
|
||||
An event-driven data bus that connects CI/CD, change auditing, and operations telemetry in real time.
|
||||
|
||||
- [Quick start guide](/docs/xstream/getting-started)
|
||||
6
dashboard/content/homepage/en/resources.md
Normal file
6
dashboard/content/homepage/en/resources.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: "Recommended Resources"
|
||||
---
|
||||
- Subscribe to the CloudNative Suite Monthly to stay informed about new releases and case studies.
|
||||
- Download the Platform Engineering Playbook for organizational rollout guidance.
|
||||
- Visit the [GitHub organization](https://github.com/svc-design) for open-source code and the public roadmap.
|
||||
6
dashboard/content/homepage/en/support.md
Normal file
6
dashboard/content/homepage/en/support.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: "Get Support"
|
||||
---
|
||||
- File a [support ticket](/support/tickets) or email <support@xcontrol.io>.
|
||||
- Join the enterprise WeChat group via QR code to reach customer success engineers.
|
||||
- Schedule an expert session for architecture reviews and performance tuning advice.
|
||||
@ -1,9 +0,0 @@
|
||||
---
|
||||
title: "Platform Capabilities"
|
||||
section: "Features"
|
||||
updated: "2025-10-17"
|
||||
---
|
||||
- **Unified Governance** – Manage policy, compliance, and workload security from one control plane.
|
||||
- **Observability by Design** – Built-in traces, metrics, and dashboards surface insights without extra wiring.
|
||||
- **Workflow Automation** – Trigger GitOps pipelines, ChatOps handoffs, and remediation playbooks with ease.
|
||||
- **AI-Native Tooling** – Pair domain knowledge with generative copilots to deliver reliable platform outcomes.
|
||||
@ -1,7 +0,0 @@
|
||||
---
|
||||
title: "Stay in the Loop"
|
||||
section: "Footer"
|
||||
updated: "2025-10-17"
|
||||
---
|
||||
Thanks for building with us! Subscribe to release notes, join the community Slack, and follow our GitOps changelog to
|
||||
stay current on new dashboards, SDK updates, and automation recipes.
|
||||
@ -1,8 +0,0 @@
|
||||
---
|
||||
title: "CloudNative Workshop"
|
||||
section: "Intro"
|
||||
updated: "2025-10-17"
|
||||
---
|
||||
Welcome to **CloudNative Workshop**, an open platform that connects artificial intelligence practices with
|
||||
cloud-native workflows. Explore curated tutorials, in-depth guides, and real-world reference architectures to
|
||||
accelerate your journey toward autonomous infrastructure.
|
||||
6
dashboard/content/homepage/zh/community.md
Normal file
6
dashboard/content/homepage/zh/community.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: "社区热议"
|
||||
---
|
||||
- **多云策略落地经验分享** —— 平台团队如何在 6 个月内完成资源分账。
|
||||
- **Kubernetes 可观测性最佳实践** —— XScopeHub 联动 OpenTelemetry 的落地方案。
|
||||
- **自动化合规检查清单** —— 社区贡献的 30+ 条可执行策略规则。
|
||||
6
dashboard/content/homepage/zh/news.md
Normal file
6
dashboard/content/homepage/zh/news.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: "产品与社区快讯"
|
||||
---
|
||||
- **2024-06-18 · 版本 1.8 发布** —— 发布策略集成 Terraform Cloud,新增多集群蓝图与安全基线。
|
||||
- **2024-05-22 · 演进直播** —— 从自动巡检到统一体验的运维一体化实践回顾。
|
||||
- **2024-04-30 · 社区贡献者日** —— 录取 12 名新贡献者,共建插件生态与数据接入能力。
|
||||
9
dashboard/content/homepage/zh/operations.md
Normal file
9
dashboard/content/homepage/zh/operations.md
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
title: "云原生运营中心"
|
||||
updated: "2024-06-18"
|
||||
---
|
||||
打造一体化的 **XControl** 控制平台,将资源治理、智能运维、策略协同、观测与互动型技术服务融合为一个可视可控的统一操作界面。
|
||||
|
||||
- 跨集群与多云环境的一体化策略治理
|
||||
- 事件驱动的自动化运维与巡检
|
||||
- 自适应的安全合规与审计追踪
|
||||
26
dashboard/content/homepage/zh/products.md
Normal file
26
dashboard/content/homepage/zh/products.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
title: "产品专题"
|
||||
---
|
||||
### XCloudFlow
|
||||
多云 IaC 工作台,将 Terraform、Pulumi 等声明式模型统一治理,支持环境即服务交付与蓝绿发布。
|
||||
|
||||
- [产品体验](/demo?product=xcloudflow)
|
||||
- [下载链接](/download?product=xcloudflow)
|
||||
- [文档链接](/docs/xcloudflow)
|
||||
|
||||
### XControl
|
||||
面向平台工程团队的统一控制平面,提供服务编排、身份权限与计费观察能力。
|
||||
|
||||
- [产品体验](/demo?product=xcontrol)
|
||||
- [文档链接](/docs/xcontrol)
|
||||
|
||||
### XScopeHub
|
||||
智能观测与告警协同中心,支持拓扑可视化、根因分析与多渠道通知。
|
||||
|
||||
- [演示预约](/demo?product=xscopehub)
|
||||
- [文档链接](/docs/xscopehub)
|
||||
|
||||
### XStream
|
||||
事件驱动的数据总线,将 CI/CD、变更审计与运营数据实时汇聚。
|
||||
|
||||
- [快速接入指南](/docs/xstream/getting-started)
|
||||
6
dashboard/content/homepage/zh/resources.md
Normal file
6
dashboard/content/homepage/zh/resources.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: "推荐资源"
|
||||
---
|
||||
- 订阅《CloudNative Suite 月报》,掌握最新版本与案例。
|
||||
- 下载《平台工程团队手册》,了解组织落地路线。
|
||||
- 访问 [GitHub 组织](https://github.com/svc-design) 获取开源代码与 Roadmap。
|
||||
6
dashboard/content/homepage/zh/support.md
Normal file
6
dashboard/content/homepage/zh/support.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: "获取支持"
|
||||
---
|
||||
- 提交 [工单](/support/tickets) 或发送邮件至 <support@xcontrol.io>
|
||||
- 扫码加入企业微信群,获取专属客户成功服务
|
||||
- 预约专家会诊,获得架构评审与性能调优建议
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { createContext, useContext, useState } from 'react'
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
|
||||
export type Language = 'en' | 'zh'
|
||||
|
||||
@ -9,13 +9,29 @@ type LanguageContextType = {
|
||||
setLanguage: (lang: Language) => void
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'cloudnative-suite.language'
|
||||
|
||||
const LanguageContext = createContext<LanguageContextType>({
|
||||
language: 'en',
|
||||
language: 'zh',
|
||||
setLanguage: () => {},
|
||||
})
|
||||
|
||||
export function LanguageProvider({ children }: { children: React.ReactNode }) {
|
||||
const [language, setLanguage] = useState<Language>('en')
|
||||
const [language, setLanguage] = useState<Language>(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const stored = window.localStorage.getItem(STORAGE_KEY)
|
||||
if (stored === 'en' || stored === 'zh') {
|
||||
return stored
|
||||
}
|
||||
}
|
||||
return 'zh'
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.localStorage.setItem(STORAGE_KEY, language)
|
||||
}
|
||||
}, [language])
|
||||
|
||||
return (
|
||||
<LanguageContext.Provider value={{ language, setLanguage }}>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import type { MarkdownRenderResult } from '../../api/render-markdown'
|
||||
|
||||
@ -9,6 +10,8 @@ type MarkdownSectionProps = {
|
||||
className?: string
|
||||
headingLevel?: keyof JSX.IntrinsicElements
|
||||
prefetched?: MarkdownRenderResult
|
||||
headingClassName?: string
|
||||
contentClassName?: string
|
||||
onMetaChange?: (meta: Record<string, unknown>) => void
|
||||
loadingFallback?: ReactNode
|
||||
errorFallback?: ReactNode
|
||||
@ -47,6 +50,8 @@ export default function MarkdownSection({
|
||||
className,
|
||||
headingLevel = 'h2',
|
||||
prefetched,
|
||||
headingClassName,
|
||||
contentClassName,
|
||||
onMetaChange,
|
||||
loadingFallback = <div className="text-sm text-slate-500">Loading content…</div>,
|
||||
errorFallback = <div className="text-sm text-red-500">Failed to load content.</div>,
|
||||
@ -104,8 +109,15 @@ export default function MarkdownSection({
|
||||
|
||||
return (
|
||||
<section className={className} aria-label={title ?? undefined}>
|
||||
{title ? <HeadingTag className="text-2xl font-semibold text-slate-900">{title}</HeadingTag> : null}
|
||||
<div className="prose prose-slate mt-4 max-w-none" dangerouslySetInnerHTML={{ __html: html }} />
|
||||
{title ? (
|
||||
<HeadingTag className={clsx('text-2xl font-semibold text-slate-900', headingClassName)}>
|
||||
{title}
|
||||
</HeadingTag>
|
||||
) : null}
|
||||
<div
|
||||
className={clsx('prose prose-slate mt-4 max-w-none', contentClassName)}
|
||||
dangerouslySetInnerHTML={{ __html: html }}
|
||||
/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,35 +1,63 @@
|
||||
'use client'
|
||||
|
||||
import MarkdownSection from '../components/MarkdownSection'
|
||||
import { loadContentMeta, loadMarkdownSection } from '../lib/markdown'
|
||||
import { useLanguage, type Language } from '../../i18n/LanguageProvider'
|
||||
|
||||
export default async function MarkdownHomepage() {
|
||||
const [intro, features, footer, footerMeta] = await Promise.all([
|
||||
loadMarkdownSection('homepage/intro.md'),
|
||||
loadMarkdownSection('homepage/features.md'),
|
||||
loadMarkdownSection('homepage/footer.md'),
|
||||
loadContentMeta('homepage/footer.md'),
|
||||
])
|
||||
const SECTION_PATHS: Record<Language, {
|
||||
operations: string
|
||||
productSpotlight: string
|
||||
news: string
|
||||
support: string
|
||||
community: string
|
||||
resources: string
|
||||
}> = {
|
||||
zh: {
|
||||
operations: 'homepage/zh/operations.md',
|
||||
productSpotlight: 'homepage/zh/products.md',
|
||||
news: 'homepage/zh/news.md',
|
||||
support: 'homepage/zh/support.md',
|
||||
community: 'homepage/zh/community.md',
|
||||
resources: 'homepage/zh/resources.md',
|
||||
},
|
||||
en: {
|
||||
operations: 'homepage/en/operations.md',
|
||||
productSpotlight: 'homepage/en/products.md',
|
||||
news: 'homepage/en/news.md',
|
||||
support: 'homepage/en/support.md',
|
||||
community: 'homepage/en/community.md',
|
||||
resources: 'homepage/en/resources.md',
|
||||
},
|
||||
}
|
||||
|
||||
const lastUpdated = footerMeta.updatedAt
|
||||
? new Date(footerMeta.updatedAt).toLocaleDateString()
|
||||
: (typeof footer.meta.updated === 'string' ? footer.meta.updated : undefined)
|
||||
const DEFAULT_LANGUAGE: Language = 'zh'
|
||||
|
||||
export default function MarkdownHomepage() {
|
||||
const { language } = useLanguage()
|
||||
const sections = SECTION_PATHS[language] ?? SECTION_PATHS[DEFAULT_LANGUAGE]
|
||||
|
||||
return (
|
||||
<main className="flex flex-col gap-16 bg-white py-16">
|
||||
<header className="bg-slate-950 py-16 text-white">
|
||||
<div className="mx-auto flex max-w-4xl flex-col gap-10 px-6">
|
||||
<MarkdownSection src="homepage/intro.md" prefetched={intro} headingLevel="h1" />
|
||||
<div className="mx-auto flex max-w-4xl flex-col gap-6 px-6">
|
||||
<MarkdownSection
|
||||
src={sections.operations}
|
||||
headingLevel="h1"
|
||||
className="flex flex-col gap-4"
|
||||
headingClassName="text-3xl font-semibold text-white sm:text-4xl"
|
||||
contentClassName="prose-invert prose-headings:text-white prose-strong:text-white text-slate-100"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
<section className="mx-auto flex w-full max-w-5xl flex-col gap-12 px-6">
|
||||
<MarkdownSection src="homepage/features.md" prefetched={features} />
|
||||
<MarkdownSection src="homepage/footer.md" prefetched={footer} headingLevel="h3" />
|
||||
{lastUpdated ? (
|
||||
<p className="text-sm text-slate-500">
|
||||
Last updated: {lastUpdated}
|
||||
{footerMeta.author ? ` by ${footerMeta.author}` : ''}
|
||||
{footerMeta.message ? ` · “${footerMeta.message}”` : ''}
|
||||
</p>
|
||||
) : null}
|
||||
<section className="mx-auto flex w-full max-w-6xl flex-col gap-12 px-6">
|
||||
<MarkdownSection src={sections.productSpotlight} />
|
||||
<div className="grid gap-12 lg:grid-cols-[2fr_1fr]">
|
||||
<MarkdownSection src={sections.news} />
|
||||
<div className="flex flex-col gap-12">
|
||||
<MarkdownSection src={sections.support} />
|
||||
<MarkdownSection src={sections.resources} />
|
||||
</div>
|
||||
</div>
|
||||
<MarkdownSection src={sections.community} />
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
|
||||
@ -1,29 +1,33 @@
|
||||
import { expect, test } from '@playwright/test'
|
||||
|
||||
test.describe('Marketing homepage experience', () => {
|
||||
test('allows visitors to explore product solutions and contact options', async ({ page }) => {
|
||||
test('renders localized markdown content and switches language dynamically', async ({ page }) => {
|
||||
await page.goto('/')
|
||||
|
||||
await expect(page.getByRole('heading', { name: '打造一体化的 XControl 控制平面' })).toBeVisible()
|
||||
await expect(page.getByText('跨集群与多云环境的一体化策略治理')).toBeVisible()
|
||||
await expect(page.getByRole('heading', { level: 1, name: '云原生运营中心' })).toBeVisible()
|
||||
await expect(page.getByText('打造一体化的 XControl 控制平台', { exact: false })).toBeVisible()
|
||||
|
||||
await expect(page.getByRole('heading', { level: 2, name: 'XCloudFlow' })).toBeVisible()
|
||||
const primaryCta = page.getByRole('link', { name: '立刻体验' }).first()
|
||||
await expect(primaryCta).toHaveAttribute('href', /\/demo\/?\?product=xcloudflow/)
|
||||
await expect(page.getByRole('link', { name: '下载链接' })).toHaveAttribute(
|
||||
await expect(page.getByRole('heading', { level: 2, name: '产品专题' })).toBeVisible()
|
||||
await expect(page.getByRole('link', { name: '产品体验' }).first()).toHaveAttribute(
|
||||
'href',
|
||||
/\/download\/?\?product=xcloudflow/
|
||||
/\/demo\/?\?product=xcloudflow/
|
||||
)
|
||||
await expect(page.getByRole('link', { name: '文档链接' })).toHaveAttribute('href', /\/docs\/xcloudflow/)
|
||||
await expect(page.getByRole('link', { name: '下载链接' })).toHaveAttribute('href', /\/download\/?\?product=xcloudflow/)
|
||||
|
||||
await page.getByRole('button', { name: /XScopeHub/ }).first().click()
|
||||
await expect(page.getByRole('heading', { level: 2, name: 'XScopeHub' })).toBeVisible()
|
||||
await expect(page.getByText('智能告警关联与根因分析')).toBeVisible()
|
||||
await expect(primaryCta).toHaveAttribute('href', /\/demo\/?\?product=xscopehub/)
|
||||
await expect(page.getByRole('heading', { level: 2, name: '获取支持' })).toBeVisible()
|
||||
|
||||
await expect(page.getByRole('button', { name: '折叠保持联系面板' })).toBeVisible()
|
||||
const newsletterLink = page.getByRole('link', { name: '立即订阅' }).first()
|
||||
await expect(newsletterLink).toBeVisible()
|
||||
await expect(newsletterLink).toHaveAttribute('href', /newsletter/)
|
||||
const languageToggle = page.getByRole('combobox')
|
||||
await languageToggle.selectOption('en')
|
||||
|
||||
await expect(page.getByRole('heading', { level: 1, name: 'Cloud-Native Operations Hub' })).toBeVisible()
|
||||
await expect(page.getByRole('heading', { level: 2, name: 'Product Spotlights' })).toBeVisible()
|
||||
await expect(page.getByRole('link', { name: 'Try the product' }).first()).toHaveAttribute(
|
||||
'href',
|
||||
/\/demo\/?\?product=xcloudflow/
|
||||
)
|
||||
await expect(page.getByRole('heading', { level: 2, name: 'Get Support' })).toBeVisible()
|
||||
await expect(
|
||||
page.getByText('Join the enterprise WeChat group via QR code', { exact: false })
|
||||
).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user