From 124596ae9475118af3ed3f278311cd8273fe8c1f Mon Sep 17 00:00:00 2001 From: Zopt <100939012+Zopt@users.noreply.github.com> Date: Tue, 16 Sep 2025 16:37:48 +0800 Subject: [PATCH] first commit --- .eslintrc.json | 3 + .gitignore | 39 + .prettierrc | 11 + README.md | 273 + STATIC_DEPLOYMENT.md | 166 + WINDOWS_BUILD_GUIDE.md | 185 + app/[locale]/about/page.tsx | 90 + app/[locale]/layout.tsx | 14 + app/[locale]/news/[id]/not-found.tsx | 147 + app/[locale]/news/[id]/page.tsx | 467 + app/[locale]/news/page.tsx | 380 + app/[locale]/page.tsx | 65 + app/[locale]/products/page.tsx | 266 + app/[locale]/support/page.tsx | 259 + app/[locale]/test-seo/page.tsx | 34 + app/about/page.tsx | 7 + app/favicon.ico | Bin 0 -> 15406 bytes app/globals.css | 168 + app/layout.tsx | 198 + app/news/[id]/page.tsx | 297 + app/news/page.tsx | 7 + app/page.tsx | 48 + app/products/page.tsx | 7 + app/robots.ts | 12 + app/sitemap.ts | 41 + app/support/page.tsx | 7 + build-static.js | 98 + bun.lockb | Bin 0 -> 184880 bytes components.json | 20 + components/Footer.tsx | 153 + components/HomePageClient.tsx | 271 + components/HomePageSEO.tsx | 314 + components/LanguageSwitcher.tsx | 72 + components/Layout.tsx | 38 + components/Navigation.tsx | 212 + components/SEOPreview.tsx | 95 + components/StructuredData.tsx | 146 + components/TDKConfigManager.tsx | 230 + components/TDKManager.tsx | 88 + components/about/CertificationsSection.tsx | 90 + components/about/CompanyMilestones.tsx | 95 + components/about/CompanyOverview.tsx | 147 + components/about/TeamSection.tsx | 64 + data/locales/en/about.json | 129 + data/locales/en/common.json | 24 + data/locales/en/home.json | 63 + data/locales/en/news.json | 97 + data/locales/en/products.json | 128 + data/locales/en/support.json | 101 + data/locales/zh-CN/about.json | 128 + data/locales/zh-CN/common.json | 24 + data/locales/zh-CN/home.json | 63 + data/locales/zh-CN/news.json | 97 + data/locales/zh-CN/products.json | 114 + data/locales/zh-CN/support.json | 101 + data/locales/zh-TW/about.json | 129 + data/locales/zh-TW/common.json | 24 + data/locales/zh-TW/home.json | 63 + data/locales/zh-TW/news.json | 97 + data/locales/zh-TW/products.json | 121 + data/locales/zh-TW/support.json | 101 + docs/news/en/ai-transformation-2024.md | 64 + docs/news/en/cloud-security-best-practices.md | 88 + docs/news/en/company-expansion-2024.md | 64 + docs/news/zh-CN/ai-transformation-2024.md | 64 + .../zh-CN/cloud-security-best-practices.md | 88 + docs/news/zh-CN/company-expansion-2024.md | 64 + docs/news/zh-TW/ai-transformation-2024.md | 64 + .../zh-TW/cloud-security-best-practices.md | 88 + docs/news/zh-TW/company-expansion-2024.md | 64 + dsds | 1 - lib/i18n.ts | 99 + lib/markdown.ts | 125 + lib/seo.ts | 358 + lib/utils.ts | 6 + middleware.ts | 69 + next.config.mjs | 28 + package-lock.json | 7865 +++++++++++++++++ package.json | 42 + postcss.config.mjs | 8 + public/images/logo.png | Bin 0 -> 109498 bytes public/site.webmanifest | 16 + tailwind.config.ts | 67 + tsconfig.json | 40 + yarn.lock | 4152 +++++++++ 85 files changed, 20451 insertions(+), 1 deletion(-) create mode 100644 .eslintrc.json create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 README.md create mode 100644 STATIC_DEPLOYMENT.md create mode 100644 WINDOWS_BUILD_GUIDE.md create mode 100644 app/[locale]/about/page.tsx create mode 100644 app/[locale]/layout.tsx create mode 100644 app/[locale]/news/[id]/not-found.tsx create mode 100644 app/[locale]/news/[id]/page.tsx create mode 100644 app/[locale]/news/page.tsx create mode 100644 app/[locale]/page.tsx create mode 100644 app/[locale]/products/page.tsx create mode 100644 app/[locale]/support/page.tsx create mode 100644 app/[locale]/test-seo/page.tsx create mode 100644 app/about/page.tsx create mode 100644 app/favicon.ico create mode 100644 app/globals.css create mode 100644 app/layout.tsx create mode 100644 app/news/[id]/page.tsx create mode 100644 app/news/page.tsx create mode 100644 app/page.tsx create mode 100644 app/products/page.tsx create mode 100644 app/robots.ts create mode 100644 app/sitemap.ts create mode 100644 app/support/page.tsx create mode 100644 build-static.js create mode 100644 bun.lockb create mode 100644 components.json create mode 100644 components/Footer.tsx create mode 100644 components/HomePageClient.tsx create mode 100644 components/HomePageSEO.tsx create mode 100644 components/LanguageSwitcher.tsx create mode 100644 components/Layout.tsx create mode 100644 components/Navigation.tsx create mode 100644 components/SEOPreview.tsx create mode 100644 components/StructuredData.tsx create mode 100644 components/TDKConfigManager.tsx create mode 100644 components/TDKManager.tsx create mode 100644 components/about/CertificationsSection.tsx create mode 100644 components/about/CompanyMilestones.tsx create mode 100644 components/about/CompanyOverview.tsx create mode 100644 components/about/TeamSection.tsx create mode 100644 data/locales/en/about.json create mode 100644 data/locales/en/common.json create mode 100644 data/locales/en/home.json create mode 100644 data/locales/en/news.json create mode 100644 data/locales/en/products.json create mode 100644 data/locales/en/support.json create mode 100644 data/locales/zh-CN/about.json create mode 100644 data/locales/zh-CN/common.json create mode 100644 data/locales/zh-CN/home.json create mode 100644 data/locales/zh-CN/news.json create mode 100644 data/locales/zh-CN/products.json create mode 100644 data/locales/zh-CN/support.json create mode 100644 data/locales/zh-TW/about.json create mode 100644 data/locales/zh-TW/common.json create mode 100644 data/locales/zh-TW/home.json create mode 100644 data/locales/zh-TW/news.json create mode 100644 data/locales/zh-TW/products.json create mode 100644 data/locales/zh-TW/support.json create mode 100644 docs/news/en/ai-transformation-2024.md create mode 100644 docs/news/en/cloud-security-best-practices.md create mode 100644 docs/news/en/company-expansion-2024.md create mode 100644 docs/news/zh-CN/ai-transformation-2024.md create mode 100644 docs/news/zh-CN/cloud-security-best-practices.md create mode 100644 docs/news/zh-CN/company-expansion-2024.md create mode 100644 docs/news/zh-TW/ai-transformation-2024.md create mode 100644 docs/news/zh-TW/cloud-security-best-practices.md create mode 100644 docs/news/zh-TW/company-expansion-2024.md delete mode 100644 dsds create mode 100644 lib/i18n.ts create mode 100644 lib/markdown.ts create mode 100644 lib/seo.ts create mode 100644 lib/utils.ts create mode 100644 middleware.ts create mode 100644 next.config.mjs create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.mjs create mode 100644 public/images/logo.png create mode 100644 public/site.webmanifest create mode 100644 tailwind.config.ts create mode 100644 tsconfig.json create mode 100644 yarn.lock diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b45fcb9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# vscode +.vscode \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..36f0850 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "singleQuote": true, + "printWidth": 100, + "tabWidth": 4, + "useTabs": false, + "semi": true, + "jsxSingleQuote": false, + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "lf" +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a3329a8 --- /dev/null +++ b/README.md @@ -0,0 +1,273 @@ +# HaoAWS官方网站 + +

+ HaoAWS +

+ +一个现代化的多语言企业云服务官网,基于 Next.js 14 构建,支持完全静态导出和多语言国际化。 + +## ✨ 项目特性 + +- 🌍 **多语言支持** - 简体中文、繁体中文、英文 +- 📱 **响应式设计** - 完美适配桌面、平板、手机 +- ⚡ **静态导出** - 支持完全静态化部署,加载速度极快 +- 🔍 **SEO 优化** - 完整的 Meta 标签、结构化数据、多语言 sitemap +- 📰 **新闻系统** - 基于 Markdown 的内容管理系统 +- 🎨 **现代 UI** - 使用 Tailwind CSS 和 ShadCN 组件 +- 🚀 **性能优化** - 代码分割、图片优化、预加载 +- 📦 **部署友好** - 支持 Netlify、Vercel、GitHub Pages 等平台 + +## 🛠️ 技术栈 + +- **前端框架**: [Next.js 14](https://nextjs.org/) (App Router) +- **样式**: [Tailwind CSS](https://tailwindcss.com/) +- **组件**: [ShadCN/UI](https://ui.shadcn.com/) +- **国际化**: 自定义 i18n 解决方案 +- **内容管理**: Markdown + Gray Matter +- **类型安全**: TypeScript +- **代码规范**: ESLint + Prettier + +## 📁 项目结构 + +``` +kejiyun/ +├── app/ # Next.js App Router +│ ├── [locale]/ # 多语言路由 +│ │ ├── about/ # 关于我们页面 +│ │ ├── news/ # 新闻列表和详情 +│ │ ├── products/ # 产品页面 +│ │ ├── support/ # 支持页面 +│ │ └── layout.tsx # 多语言布局 +│ ├── news/[id]/ # 默认语言新闻详情 +│ ├── globals.css # 全局样式 +│ └── layout.tsx # 根布局 +├── components/ # 可复用组件 +│ ├── about/ # 关于页面组件 +│ ├── LanguageSwitcher.tsx # 语言切换器 +│ ├── Layout.tsx # 布局组件 +│ └── SEOPreview.tsx # SEO 预览 +├── data/ # 数据文件 +│ └── locales/ # 多语言翻译文件 +│ ├── en/ # 英文翻译 +│ ├── zh-CN/ # 简体中文翻译 +│ └── zh-TW/ # 繁体中文翻译 +├── docs/ # 文档和新闻内容 +│ └── news/ # 新闻 Markdown 文件 +├── lib/ # 工具函数 +│ ├── i18n.ts # 国际化配置 +│ ├── markdown.ts # Markdown 解析 +│ └── seo.ts # SEO 工具 +├── public/ # 静态资源 +└── middleware.ts # 路由中间件 +``` + +## 🚀 快速开始 + +### 安装依赖 + +```bash +npm install +# 或 +yarn install +``` + +### 开发模式 + +```bash +npm run dev +``` + +打开 [http://localhost:3000](http://localhost:3000) 查看网站。 + +### 构建生产版本 + +```bash +# 普通构建 +npm run build + +# 静态导出构建 +npm run build:static +``` + +### 本地预览静态版本 + +```bash +npm run serve +# 或 +npm run preview +``` + +## 🌍 多语言支持 + +### 支持的语言 + +- **简体中文** (`zh-CN`) - 默认语言,路径: `/` +- **繁体中文** (`zh-TW`) - 路径: `/zh-TW/` +- **英文** (`en`) - 路径: `/en/` + +### 路由示例 + +``` +/ # 简体中文首页 +/about/ # 简体中文关于页面 +/news/article-id/ # 简体中文新闻详情 + +/zh-TW/ # 繁体中文首页 +/zh-TW/about/ # 繁体中文关于页面 +/zh-TW/news/article-id/ # 繁体中文新闻详情 + +/en/ # 英文首页 +/en/about/ # 英文关于页面 +/en/news/article-id/ # 英文新闻详情 +``` + +### 添加新语言 + +1. 在 `lib/i18n.ts` 中添加语言配置 +2. 在 `data/locales/` 中创建对应的翻译文件 +3. 在 `docs/news/` 中添加对应语言的新闻文件 +4. 更新各页面的 `generateStaticParams` 函数 + +## 📰 新闻系统 + +### 新闻文件结构 + +```markdown +--- +title: "文章标题" +description: "文章描述" +date: "2024-01-01" +author: "作者" +tags: ["标签1", "标签2"] +--- + +# 文章内容 + +这里是 Markdown 格式的文章内容... +``` + +### 添加新新闻 + +1. 在 `docs/news/[语言]/` 目录下创建新的 `.md` 文件 +2. 更新 `app/[locale]/news/[id]/page.tsx` 中的 `generateStaticParams` 函数 +3. 重新构建网站 + +## 🔍 SEO 优化 + +### 已实现的 SEO 功能 + +- ✅ 多语言 Meta 标签 +- ✅ Open Graph 标签 +- ✅ Twitter Card 支持 +- ✅ 结构化数据 (JSON-LD) +- ✅ 自动生成 sitemap.xml +- ✅ robots.txt 配置 +- ✅ 语言链接标签 (hreflang) + +### SEO 配置 + +在 `lib/seo.ts` 中配置默认的 SEO 设置,每个页面可以覆盖这些设置。 + +## 📦 部署 + +### 静态部署(推荐) + +#### Netlify +```bash +# 构建命令 +npm run build:static + +# 发布目录 +out +``` + +#### Vercel +```bash +npm install -g vercel +vercel --prod +``` + +#### GitHub Pages +```bash +npm run build:static +# 将 out/ 目录内容推送到 gh-pages 分支 +``` + +### 服务器部署 + +```bash +npm run build +npm start +``` + +## 🎨 自定义和扩展 + +### 添加新页面 + +1. 在 `app/[locale]/` 目录下创建新页面 +2. 添加对应的翻译文件 +3. 更新导航菜单 +4. 添加 `generateStaticParams` 函数(如果需要静态导出) + +### 修改样式 + +项目使用 Tailwind CSS,可以在以下文件中修改样式: +- `app/globals.css` - 全局样式 +- `tailwind.config.ts` - Tailwind 配置 +- 各组件文件中的 className + +### 添加新组件 + +在 `components/` 目录下创建新组件,遵循现有的命名和结构规范。 + +## 🔧 开发指南 + +### 代码规范 + +```bash +# 代码格式化 +npm run format + +# 代码检查 +npm run lint +``` + +### Windows 开发 + +如果在 Windows 上遇到权限问题,请参考 [WINDOWS_BUILD_GUIDE.md](./WINDOWS_BUILD_GUIDE.md)。 + +## 📋 可用脚本 + +```bash +npm run dev # 开发模式 +npm run build # 生产构建 +npm run build:static # 静态导出构建 +npm run build:simple # 简化构建(解决权限问题) +npm run start # 启动生产服务器 +npm run lint # 代码检查 +npm run format # 代码格式化 +npm run serve # 本地预览静态文件 +npm run preview # 构建并预览 +``` + +## 🐛 故障排除 + +### 常见问题 + +1. **构建权限错误** - 参考 [WINDOWS_BUILD_GUIDE.md](./WINDOWS_BUILD_GUIDE.md) +2. **语言切换不工作** - 检查 Cookie 设置和客户端 JavaScript +3. **静态导出失败** - 确保所有动态路由都有 `generateStaticParams` + +### 获取帮助 + +- 查看 [静态部署指南](./STATIC_DEPLOYMENT.md) +- 查看 [Windows 构建指南](./WINDOWS_BUILD_GUIDE.md) +- 检查 Next.js 官方文档 + +## 📄 许可证 + +本项目采用 MIT 许可证。 + +--- + +**提示**: 这是一个完全静态化的网站,构建后可以部署到任何静态文件托管服务,无需 Node.js 服务器环境。 diff --git a/STATIC_DEPLOYMENT.md b/STATIC_DEPLOYMENT.md new file mode 100644 index 0000000..f9280ea --- /dev/null +++ b/STATIC_DEPLOYMENT.md @@ -0,0 +1,166 @@ +# 静态部署指南 + +## 🚀 静态打包命令 + +### 构建静态文件 +```bash +npm run build:static +``` + +### 本地预览静态网站 +```bash +npm run preview +``` + +### 仅构建(不启动服务器) +```bash +npm run build +``` + +### 仅启动静态文件服务器 +```bash +npm run serve +``` + +## 📁 输出目录 + +静态文件将生成在 `out/` 目录中,包含: + +``` +out/ +├── index.html # 首页 (简体中文) +├── zh-TW/ +│ ├── index.html # 繁体中文首页 +│ ├── about/index.html # 关于我们页面 +│ ├── products/index.html # 产品页面 +│ ├── news/ +│ │ ├── index.html # 新闻列表 +│ │ └── [id]/index.html # 新闻详情 +│ └── support/index.html # 支持页面 +├── en/ +│ ├── index.html # 英文首页 +│ └── ... # 其他英文页面 +├── news/ +│ ├── index.html # 默认语言新闻列表 +│ └── [id]/index.html # 默认语言新闻详情 +├── about/index.html # 默认语言关于页面 +├── products/index.html # 默认语言产品页面 +├── support/index.html # 默认语言支持页面 +└── _next/ # Next.js 静态资源 +``` + +## 🌐 部署到不同平台 + +### 1. Netlify +1. 将 `out/` 目录拖拽到 Netlify 控制台 +2. 或连接 Git 仓库,设置构建命令: + ``` + Build command: npm run build:static + Publish directory: out + ``` + +### 2. Vercel +```bash +npx vercel --prod +``` + +### 3. GitHub Pages +1. 将 `out/` 目录内容推送到 `gh-pages` 分支 +2. 在仓库设置中启用 GitHub Pages + +### 4. 服务器部署 +将 `out/` 目录上传到服务器的网站根目录 + +## ⚙️ 配置说明 + +### 基础路径配置 +如果需要部署到子目录,修改 `next.config.mjs`: + +```js +const nextConfig = { + output: 'export', + basePath: '/your-subdirectory', // 取消注释并设置路径 + // ... +}; +``` + +### 图片优化 +静态导出中图片优化已设置为: +```js +images: { + unoptimized: true, // 静态导出必需 +} +``` + +## 🔄 多语言路由说明 + +静态导出后的路由结构: + +- **简体中文 (默认)**:`/`, `/about`, `/news/xxx` +- **繁体中文**:`/zh-TW/`, `/zh-TW/about`, `/zh-TW/news/xxx` +- **英文**:`/en/`, `/en/about`, `/en/news/xxx` + +## ⚠️ 注意事项 + +### 静态导出限制 +1. **无服务器端渲染**:所有页面在构建时生成 +2. **无中间件**:语言切换依赖客户端逻辑 +3. **无动态API路由**:不能使用 `/api` 路由 +4. **无服务器端重定向**:依赖客户端跳转 + +### 语言切换机制 +在静态模式下: +- 中间件不会执行 +- 语言切换完全依赖客户端 JavaScript +- Cookie 设置在客户端进行 + +### 新增文章 +如需添加新文章,需要: +1. 在 `docs/news/` 中添加 Markdown 文件 +2. 更新页面中的 `generateStaticParams` 函数 +3. 重新构建 + +## 🧪 测试 + +### 本地测试 +```bash +# 构建并预览 +npm run preview + +# 访问 http://localhost:3000 +``` + +### 构建验证 +```bash +# 检查构建输出 +npm run build:static +ls -la out/ + +# 验证多语言页面 +ls -la out/zh-TW/ +ls -la out/en/ +``` + +## 🔧 故障排除 + +### 常见问题 + +1. **页面404**:检查 `generateStaticParams` 是否包含所有路由 +2. **样式丢失**:确认 CSS 文件正确引入 +3. **图片不显示**:检查图片路径和 `unoptimized: true` 设置 +4. **语言切换无效**:确认 JavaScript 已启用 + +### 调试步骤 +1. 检查 `out/` 目录结构 +2. 查看浏览器开发者工具控制台 +3. 验证静态文件访问路径 + +## 📈 性能优化 + +- ✅ 自动代码分割 +- ✅ 静态资源优化 +- ✅ CSS 压缩 +- ✅ JavaScript 压缩 +- ✅ 预加载关键资源 + +静态导出的网站加载速度极快,适合 CDN 分发! \ No newline at end of file diff --git a/WINDOWS_BUILD_GUIDE.md b/WINDOWS_BUILD_GUIDE.md new file mode 100644 index 0000000..b6ecdce --- /dev/null +++ b/WINDOWS_BUILD_GUIDE.md @@ -0,0 +1,185 @@ +# Windows 静态构建指南 + +## 🚨 常见权限问题解决方案 + +### 方法一:使用管理员权限 +1. 右键点击 PowerShell 或 CMD +2. 选择"以管理员身份运行" +3. 导航到项目目录:`cd "E:\成品的网站\kejiyun"` +4. 运行构建:`npm run build:static` + +### 方法二:修改杀毒软件设置 +1. 临时关闭实时保护(Windows Defender 或其他杀毒软件) +2. 将项目目录添加到杀毒软件白名单 +3. 重新尝试构建 + +### 方法三:使用简化构建命令 +```bash +# 方法 1 +npm run build:simple + +# 方法 2 +npx next build + +# 方法 3(如果上面的失败) +set NEXT_DISABLE_TELEMETRY=1 +set NODE_ENV=production +npx next build +``` + +## 🔧 手动构建步骤 + +### 1. 清理环境 +```powershell +# 停止所有Node进程 +Get-Process node -ErrorAction SilentlyContinue | Stop-Process -Force + +# 清理缓存目录 +Remove-Item -Path ".next", "out" -Recurse -Force -ErrorAction SilentlyContinue + +# 清理npm缓存 +npm cache clean --force +``` + +### 2. 检查文件权限 +```powershell +# 检查当前目录权限 +Get-Acl . | Format-List + +# 如果需要,修改目录权限 +icacls . /grant $env:USERNAME:F /t +``` + +### 3. 重新安装依赖 +```bash +# 删除node_modules +Remove-Item -Path "node_modules" -Recurse -Force -ErrorAction SilentlyContinue + +# 重新安装 +npm install +``` + +### 4. 构建 +```bash +npm run build:static +``` + +## 🚀 成功构建后的验证 + +构建成功后,您应该看到: + +``` +out/ +├── index.html # ✅ 简体中文首页 +├── about/ +│ └── index.html # ✅ 关于页面 +├── news/ +│ ├── index.html # ✅ 新闻列表 +│ ├── ai-transformation-2024/ +│ ├── cloud-security-best-practices/ +│ └── company-expansion-2024/ +├── products/ +├── support/ +├── zh-TW/ # ✅ 繁体中文版本 +├── en/ # ✅ 英文版本 +└── _next/ # ✅ 静态资源 +``` + +## 🌐 本地预览 + +```bash +# 启动静态服务器 +npm run serve + +# 或者使用Python(如果安装了) +python -m http.server 3000 --directory out + +# 或者使用其他静态服务器 +npx http-server out -p 3000 +``` + +## 🔍 故障排除 + +### 错误:EPERM: operation not permitted +**原因**:文件权限或进程锁定问题 + +**解决方案**: +1. 以管理员身份运行终端 +2. 确保没有其他进程正在使用项目文件 +3. 临时关闭杀毒软件 +4. 重启电脑后重试 + +### 错误:构建过程中卡住 +**解决方案**: +1. 按 `Ctrl+C` 停止进程 +2. 运行:`npm run build:simple` +3. 或使用 `npx next build --debug` + +### 错误:out目录为空或缺少文件 +**检查**: +1. 查看构建日志中的错误信息 +2. 确认 `generateStaticParams` 函数正确 +3. 检查是否有动态导入或服务器端功能 + +### 错误:语言切换不工作 +这是正常的,静态导出模式下: +- 中间件不会执行 +- 语言切换依赖客户端JavaScript +- 需要在浏览器中测试 + +## 📦 部署选项 + +### 1. Netlify(推荐) +```bash +# 方法1:拖拽部署 +# 将整个 out/ 目录拖拽到 https://app.netlify.com/drop + +# 方法2:命令行部署 +npm install -g netlify-cli +netlify deploy --prod --dir=out +``` + +### 2. Vercel +```bash +npm install -g vercel +vercel --prod +``` + +### 3. GitHub Pages +```bash +# 推送 out/ 内容到 gh-pages 分支 +git subtree push --prefix out origin gh-pages +``` + +### 4. 传统服务器 +- 上传 `out/` 目录到服务器的网站根目录 +- 确保服务器支持SPA路由(对于多语言URL) + +## ⚡ 性能优化 + +构建后的静态网站已经包含: +- ✅ 代码分割 +- ✅ 资源压缩 +- ✅ 图片优化 +- ✅ CSS/JS 最小化 +- ✅ 预加载关键资源 + +## 🔄 更新流程 + +当您修改内容后: +1. 更新相应的Markdown或JSON文件 +2. 如果添加新文章,更新 `generateStaticParams` +3. 重新运行 `npm run build:static` +4. 重新部署 `out/` 目录 + +## 📞 需要帮助? + +如果仍然遇到问题: +1. 确保使用管理员权限 +2. 检查Windows版本兼容性 +3. 尝试在不同的文件夹位置重新克隆项目 +4. 考虑使用WSL(Windows Subsystem for Linux) + +--- + +**记住**:静态导出的网站速度极快,完全不需要Node.js服务器,可以部署到任何静态网站托管服务! \ No newline at end of file diff --git a/app/[locale]/about/page.tsx b/app/[locale]/about/page.tsx new file mode 100644 index 0000000..4258b51 --- /dev/null +++ b/app/[locale]/about/page.tsx @@ -0,0 +1,90 @@ +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import Layout from '@/components/Layout'; +import CompanyOverview from '@/components/about/CompanyOverview'; +import CompanyMilestones from '@/components/about/CompanyMilestones'; +import TeamSection from '@/components/about/TeamSection'; +import CertificationsSection from '@/components/about/CertificationsSection'; + +interface AboutPageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: AboutPageProps) { + return generateSEOMetadata(params.locale, 'about'); +} + +export default async function AboutPage({ params }: AboutPageProps) { + const { locale } = params; + const [common, about] = await Promise.all([ + getTranslations(locale, 'common'), + getTranslations(locale, 'about'), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + + {/* Hero Section */} +
+
+

+ {about.title} +

+

+ {about.subtitle} +

+
+
+ + {/* Company Overview */} + + + {/* Company Milestones */} + + + {/* Team Section */} + + + {/* Certifications */} + +
+ ); +} diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx new file mode 100644 index 0000000..4debfc7 --- /dev/null +++ b/app/[locale]/layout.tsx @@ -0,0 +1,14 @@ +import { Locale } from '@/lib/i18n'; + +interface LocaleLayoutProps { + children: React.ReactNode; + params: { + locale: Locale; + }; +} + +export default function LocaleLayout({ children, params }: LocaleLayoutProps) { + // 在动态路由布局中,我们只需要返回children + // 语言属性通过generateMetadata在页面级别设置 + return <>{children}; +} diff --git a/app/[locale]/news/[id]/not-found.tsx b/app/[locale]/news/[id]/not-found.tsx new file mode 100644 index 0000000..1166bb0 --- /dev/null +++ b/app/[locale]/news/[id]/not-found.tsx @@ -0,0 +1,147 @@ +import Link from 'next/link'; +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import Layout from '@/components/Layout'; + +export default async function NewsNotFound() { + // Since we can't access params in not-found.tsx, we'll use the default locale + const locale: Locale = 'zh-CN'; + const common = await getTranslations(locale, 'common'); + const navigationPaths = getNavigationPaths(locale); + + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + +
+
+
+
+ + + +
+ +

+ 404 +

+ +

+ 文章未找到 +

+ +

+ 抱歉,您请求的新闻文章不存在,或者该文章的当前语言版本尚未提供。 +

+
+ +
+

+ 可能的原因: +

+ +
    +
  • + + • + + 文章链接已过期或不正确 +
  • +
  • + + • + + 该文章尚未翻译成当前语言 +
  • +
  • + + • + + 文章已被移除或归档 +
  • +
+ +
+ p.key === 'news')?.path || '/news' + } + className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors font-light" + data-oid="c45-.pd" + > + 返回新闻列表 + + + p.key === 'home')?.path || '/'} + className="bg-gray-100 text-gray-700 px-6 py-3 rounded-lg hover:bg-gray-200 transition-colors font-light" + data-oid="vzzstbf" + > + 返回首页 + +
+
+ +
+

+ 如果您认为这是一个错误,请联系我们的技术支持团队。 +

+
+
+
+
+ ); +} diff --git a/app/[locale]/news/[id]/page.tsx b/app/[locale]/news/[id]/page.tsx new file mode 100644 index 0000000..48ba963 --- /dev/null +++ b/app/[locale]/news/[id]/page.tsx @@ -0,0 +1,467 @@ +import { notFound } from 'next/navigation'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import { getNewsArticle, getAvailableLocalesForArticle, checkArticleExists } from '@/lib/markdown'; +import Layout from '@/components/Layout'; +import LanguageSwitcher from '@/components/LanguageSwitcher'; +import Link from 'next/link'; + +interface NewsDetailPageProps { + params: { + locale: Locale; + id: string; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + const articles = ['ai-transformation-2024', 'cloud-security-best-practices', 'company-expansion-2024']; + + const params = []; + for (const locale of locales) { + for (const id of articles) { + params.push({ locale, id }); + } + } + return params; +} + +export async function generateMetadata({ params }: NewsDetailPageProps) { + const { locale, id } = params; + + // Check if article exists + if (!checkArticleExists(id, locale)) { + return { + title: 'Article Not Found', + description: 'The requested article could not be found.', + }; + } + + const article = await getNewsArticle(id, locale); + if (!article) { + return { + title: 'Article Not Found', + description: 'The requested article could not be found.', + }; + } + + return { + title: article.title, + description: article.excerpt, + openGraph: { + title: article.title, + description: article.excerpt, + type: 'article', + publishedTime: article.date, + authors: [article.author], + tags: article.tags, + }, + }; +} + +export default async function NewsDetailPage({ params }: NewsDetailPageProps) { + const { locale, id } = params; + + // Check if article exists in the requested locale + if (!checkArticleExists(id, locale)) { + notFound(); + } + + const article = await getNewsArticle(id, locale); + if (!article) { + notFound(); + } + + const [common, availableLocales] = await Promise.all([ + getTranslations(locale, 'common'), + getAvailableLocalesForArticle(id), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + + {/* Breadcrumb */} +
+
+ +
+
+ + {/* Language Switcher */} +
+
+
+
+ + {locale === 'en' + ? 'Available in:' + : locale === 'zh-TW' + ? '可用語言:' + : '可用语言:'} + + +
+ + {/* Back to News */} + p.key === 'news')?.path || '/news'} + className="text-sm text-blue-600 hover:text-blue-800 flex items-center space-x-1" + data-oid="eatue3o" + > + + + + + {locale === 'en' + ? 'Back to News' + : locale === 'zh-TW' + ? '返回新聞' + : '返回新闻'} + + +
+
+
+ + {/* Article Header */} +
+
+
+
+ + {article.category} + + {article.featured && ( + + {locale === 'en' + ? 'Featured' + : locale === 'zh-TW' + ? '精選' + : '精选'} + + )} +
+ +

+ {article.title} +

+ +
+
+ + + + {article.date} +
+ +
+ + + + {article.author} +
+ +
+ + + + {article.readTime} +
+
+ +

+ {article.excerpt} +

+
+ + {/* Tags */} + {article.tags.length > 0 && ( +
+ {article.tags.map((tag) => ( + + #{tag} + + ))} +
+ )} +
+
+ + {/* Article Content */} +
+
+
+ ( +

+ {children} +

+ ), + + h2: ({ children }) => ( +

+ {children} +

+ ), + + h3: ({ children }) => ( +

+ {children} +

+ ), + + p: ({ children }) => ( +

+ {children} +

+ ), + + ul: ({ children }) => ( +
    + {children} +
+ ), + + ol: ({ children }) => ( +
    + {children} +
+ ), + + li: ({ children }) => ( +
  • + {children} +
  • + ), + + blockquote: ({ children }) => ( +
    + {children} +
    + ), + + code: ({ children }) => ( + + {children} + + ), + + pre: ({ children }) => ( +
    +                                        {children}
    +                                    
    + ), + }} + data-oid="gwxptyo" + > + {article.content} +
    +
    +
    +
    + + {/* Article Footer */} +
    +
    +
    +
    +

    + {locale === 'en' + ? 'Published on' + : locale === 'zh-TW' + ? '發布於' + : '发布于'}{' '} + {article.date} +

    +

    + {locale === 'en' ? 'By' : locale === 'zh-TW' ? '作者:' : '作者:'}{' '} + {article.author} +

    +
    + +
    + +
    +
    +
    +
    +
    + ); +} diff --git a/app/[locale]/news/page.tsx b/app/[locale]/news/page.tsx new file mode 100644 index 0000000..0701942 --- /dev/null +++ b/app/[locale]/news/page.tsx @@ -0,0 +1,380 @@ +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import Layout from '@/components/Layout'; +import Link from 'next/link'; + +interface NewsPageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: NewsPageProps) { + return generateSEOMetadata(params.locale, 'news'); +} + +export default async function NewsPage({ params }: NewsPageProps) { + const { locale } = params; + const [common, news] = await Promise.all([ + getTranslations(locale, 'common'), + getTranslations(locale, 'news'), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + // Separate featured and regular articles + const featuredArticles = news.articles.filter((article: any) => article.featured); + const regularArticles = news.articles.filter((article: any) => !article.featured); + + return ( + + {/* Hero Section */} +
    +
    +

    + {news.title} +

    +

    + {news.subtitle} +

    +
    +
    + + {/* Categories Filter */} +
    +
    +
    + + {news.categories.map((category: any) => ( + + ))} +
    +
    +
    + + {/* Featured Articles */} + {featuredArticles.length > 0 && ( +
    +
    +

    + {locale === 'en' + ? 'Featured Stories' + : locale === 'zh-TW' + ? '精選報導' + : '精选报道'} +

    +
    + {featuredArticles.map((article: any, index: number) => ( +
    + {/* Article Image */} +
    +
    +
    + + {article.category} + +
    +
    + + {locale === 'en' + ? 'Featured' + : locale === 'zh-TW' + ? '精選' + : '精选'} + +
    +
    + +
    +
    + {article.date} + + {article.readTime} +
    + +

    + {article.title} +

    + +

    + {article.excerpt} +

    + + {news.readMore} + + + + +
    +
    + ))} +
    +
    +
    + )} + + {/* Regular Articles */} +
    +
    +

    + {locale === 'en' + ? 'Latest News' + : locale === 'zh-TW' + ? '最新消息' + : '最新消息'} +

    +
    + {regularArticles.map((article: any, index: number) => ( +
    +
    +
    +
    +
    +
    +
    + + {article.category} + + + {article.date} + + + {article.readTime} + +
    + +

    + {article.title} +

    + +

    + {article.excerpt} +

    +
    + + {news.readMore} + + + + + + {locale === 'en' + ? 'By' + : locale === 'zh-TW' + ? '作者:' + : '作者:'}{' '} + {article.author} + +
    +
    +
    +
    + ))} +
    +
    +
    + + {/* Newsletter Subscription */} +
    +
    +

    + {locale === 'en' + ? 'Stay Updated' + : locale === 'zh-TW' + ? '保持更新' + : '保持更新'} +

    +

    + {locale === 'en' + ? 'Subscribe to our newsletter for the latest industry insights and company updates.' + : locale === 'zh-TW' + ? '訂閱我們的電子報,獲取最新的行業洞察和公司動態。' + : '订阅我们的电子报,获取最新的行业洞察和公司动态。'} +

    +
    + + + +
    +
    +
    +
    + ); +} diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx new file mode 100644 index 0000000..15ad012 --- /dev/null +++ b/app/[locale]/page.tsx @@ -0,0 +1,65 @@ +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import { getLatestNewsArticles } from '@/lib/markdown'; +import Layout from '@/components/Layout'; +import HomePageClient from '@/components/HomePageClient'; +import HomePageSEO from '@/components/HomePageSEO'; + +interface HomePageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: HomePageProps) { + return generateSEOMetadata(params.locale, 'home'); +} + +export default async function HomePage({ params }: HomePageProps) { + const { locale } = params; + const [common, home, latestNews] = await Promise.all([ + getTranslations(locale, 'common'), + getTranslations(locale, 'home'), + getLatestNewsArticles(locale, 3), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + <> + + + + + + ); +} diff --git a/app/[locale]/products/page.tsx b/app/[locale]/products/page.tsx new file mode 100644 index 0000000..91c44af --- /dev/null +++ b/app/[locale]/products/page.tsx @@ -0,0 +1,266 @@ +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import Layout from '@/components/Layout'; + +interface ProductsPageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: ProductsPageProps) { + return generateSEOMetadata(params.locale, 'products'); +} + +export default async function ProductsPage({ params }: ProductsPageProps) { + const { locale } = params; + const [common, products] = await Promise.all([ + getTranslations(locale, 'common'), + getTranslations(locale, 'products'), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + + {/* Hero Section */} +
    +
    +

    + {products.title} +

    +

    + {products.subtitle} +

    +
    +
    + + {/* Categories Section */} +
    +
    +
    +

    + {locale === 'en' + ? 'Service Categories' + : locale === 'zh-TW' + ? '服務類別' + : '服务类别'} +

    +
    +
    + {products.categories.map((category: any, index: number) => ( +
    +
    + {category.icon} +
    +

    + {category.name} +

    +

    + {category.description} +

    +
    + ))} +
    +
    +
    + + {/* Products Grid */} +
    +
    +
    +

    + {locale === 'en' + ? 'Our Solutions' + : locale === 'zh-TW' + ? '我們的解決方案' + : '我们的解决方案'} +

    +
    +
    + {products.items.map((product: any, index: number) => ( +
    + {/* Product Icon/Image */} +
    + + {product.icon || '💼'} + +
    + +
    +

    + {product.title} +

    +

    + {product.description} +

    + + {/* Features */} +
    +

    + {locale === 'en' + ? 'Key Features:' + : locale === 'zh-TW' + ? '主要功能:' + : '主要功能:'} +

    +
    + {product.features + .slice(0, 4) + .map((feature: string, featureIndex: number) => ( + + {feature} + + ))} + {product.features.length > 4 && ( + + +{product.features.length - 4}{' '} + {locale === 'en' + ? 'more' + : locale === 'zh-TW' + ? '更多' + : '更多'} + + )} +
    +
    + + {/* CTA Button */} + +
    +
    + ))} +
    +
    +
    + + {/* CTA Section */} +
    +
    +

    + {locale === 'en' + ? 'Ready to Transform Your Business?' + : locale === 'zh-TW' + ? '準備好轉型您的業務了嗎?' + : '准备好转型您的业务了吗?'} +

    +

    + {locale === 'en' + ? 'Contact our experts to discuss your specific needs and get a customized solution.' + : locale === 'zh-TW' + ? '聯絡我們的專家討論您的具體需求,獲得客製化解決方案。' + : '联系我们的专家讨论您的具体需求,获得定制化解决方案。'} +

    +
    + + +
    +
    +
    +
    + ); +} diff --git a/app/[locale]/support/page.tsx b/app/[locale]/support/page.tsx new file mode 100644 index 0000000..d28b658 --- /dev/null +++ b/app/[locale]/support/page.tsx @@ -0,0 +1,259 @@ +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import Layout from '@/components/Layout'; + +interface SupportPageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: SupportPageProps) { + return generateSEOMetadata(params.locale, 'support'); +} + +export default async function SupportPage({ params }: SupportPageProps) { + const { locale } = params; + const [common, support] = await Promise.all([ + getTranslations(locale, 'common'), + getTranslations(locale, 'support'), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + + {/* Hero Section */} +
    +
    +

    + {support.title} +

    +

    + {support.subtitle} +

    +
    +
    + + {/* Contact Methods */} +
    +
    +
    +

    + {support.sections.contact.title} +

    +

    + {support.sections.contact.subtitle} +

    +
    +
    + {support.sections.contact.methods.map((method: any, index: number) => ( +
    +
    + {method.icon} +
    +

    + {method.title} +

    +

    + {method.value} +

    +

    + {method.description} +

    +
    + ))} +
    +
    +
    + + {/* Help Resources */} +
    +
    +
    +

    + {support.sections.resources.title} +

    +

    + {support.sections.resources.subtitle} +

    +
    +
    + {support.sections.resources.items.map((resource: any, index: number) => ( +
    +
    + {resource.icon} +
    +

    + {resource.title} +

    +

    + {resource.description} +

    +
    + ))} +
    +
    +
    + + {/* FAQ Section */} +
    +
    +
    +

    + {support.sections.faq.title} +

    +

    + {support.sections.faq.subtitle} +

    +
    +
    + {support.sections.faq.items.map((item: any, index: number) => ( +
    +

    + + Q + + {item.question} +

    +
    +

    + {item.answer} +

    +
    +
    + ))} +
    +
    +
    + + {/* Contact CTA */} +
    +
    +

    + {locale === 'en' + ? 'Still Need Help?' + : locale === 'zh-TW' + ? '仍需要幫助?' + : '仍需要帮助?'} +

    +

    + {locale === 'en' + ? 'Our support team is ready to assist you with any questions or issues.' + : locale === 'zh-TW' + ? '我們的支援團隊隨時準備協助您解決任何問題。' + : '我们的支持团队随时准备协助您解决任何问题。'} +

    +
    + + +
    +
    +
    +
    + ); +} diff --git a/app/[locale]/test-seo/page.tsx b/app/[locale]/test-seo/page.tsx new file mode 100644 index 0000000..1741e5b --- /dev/null +++ b/app/[locale]/test-seo/page.tsx @@ -0,0 +1,34 @@ +import { Locale } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; + +interface TestSEOPageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: TestSEOPageProps) { + return generateSEOMetadata(params.locale, 'home'); +} + +export default function TestSEOPage({ params }: TestSEOPageProps) { + const { locale } = params; + + return ( +
    +

    SEO测试页面 - {locale}

    +

    检查页面的title、description和keywords是否正确显示

    +
    +

    当前语言: {locale}

    +

    请查看浏览器标题栏和页面源代码中的meta标签

    +
    +
    + ); +} diff --git a/app/about/page.tsx b/app/about/page.tsx new file mode 100644 index 0000000..0efb8f6 --- /dev/null +++ b/app/about/page.tsx @@ -0,0 +1,7 @@ +import { redirect } from 'next/navigation'; +import { defaultLocale } from '@/lib/i18n'; + +export default function AboutPage() { + // Redirect to the localized version + redirect(`/${defaultLocale}/about`); +} diff --git a/app/favicon.ico b/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..08f2470cb3cd949a6a40f833d9bb68411e422116 GIT binary patch literal 15406 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 { + return ( + + + + + + + + + + + + + + + {children} + + {/* Google Analytics */} +