feat: localize homepage markdown content (#567)

This commit is contained in:
shenlan 2025-10-18 10:40:14 +08:00 committed by GitHub
parent 9b17cca94a
commit de0d38ac7d
19 changed files with 223 additions and 69 deletions

View 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.

View 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.

View 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

View 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)

View 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.

View 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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,6 @@
---
title: "社区热议"
---
- **多云策略落地经验分享** —— 平台团队如何在 6 个月内完成资源分账。
- **Kubernetes 可观测性最佳实践** —— XScopeHub 联动 OpenTelemetry 的落地方案。
- **自动化合规检查清单** —— 社区贡献的 30+ 条可执行策略规则。

View File

@ -0,0 +1,6 @@
---
title: "产品与社区快讯"
---
- **2024-06-18 · 版本 1.8 发布** —— 发布策略集成 Terraform Cloud新增多集群蓝图与安全基线。
- **2024-05-22 · 演进直播** —— 从自动巡检到统一体验的运维一体化实践回顾。
- **2024-04-30 · 社区贡献者日** —— 录取 12 名新贡献者,共建插件生态与数据接入能力。

View File

@ -0,0 +1,9 @@
---
title: "云原生运营中心"
updated: "2024-06-18"
---
打造一体化的 **XControl** 控制平台,将资源治理、智能运维、策略协同、观测与互动型技术服务融合为一个可视可控的统一操作界面。
- 跨集群与多云环境的一体化策略治理
- 事件驱动的自动化运维与巡检
- 自适应的安全合规与审计追踪

View 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)

View File

@ -0,0 +1,6 @@
---
title: "推荐资源"
---
- 订阅《CloudNative Suite 月报》,掌握最新版本与案例。
- 下载《平台工程团队手册》,了解组织落地路线。
- 访问 [GitHub 组织](https://github.com/svc-design) 获取开源代码与 Roadmap。

View File

@ -0,0 +1,6 @@
---
title: "获取支持"
---
- 提交 [工单](/support/tickets) 或发送邮件至 <support@xcontrol.io>
- 扫码加入企业微信群,获取专属客户成功服务
- 预约专家会诊,获得架构评审与性能调优建议

View File

@ -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 }}>

View File

@ -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>
)
}

View File

@ -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>
)

View File

@ -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()
})
})