工程 5 分钟阅读

基于 Astro 4.8 的博客系统完整实现 - 包含 AI 翻译与 E2E 测试的综合方案

基于 Astro Content Collections 构建的高性能博客系统实现记录。详细解析包含 Google Gemini API 的 AI 自动翻译、Playwright E2E 测试以及速率限制功能在内的全方位开发流程

Terisuke
Share this article:

概述

在最近的开发中,我们实现了一套基于 Astro 4.8.7 的综合博客系统。该系统不仅能展示 Markdown,还具备 AI 自动翻译、E2E 测试以及面向正式运营的各项功能,是一个现代化的技术博客平台。

在 WordPress 里跟小部件(Widget)搏斗的时代已经结束了。从现在起,翻译交给 AI,测试全自动化,工程师只要躺着睡觉就行了(开玩笑的)。

技术栈

核心技术

  • Astro 4.8.7:基于群岛架构(Islands Architecture)的高速 SSG
  • TypeScript:类型安全的开发环境
  • Tailwind CSS:实用优先(Utility-first)的样式解决方案
  • Alpine.js 3.14.0:轻量级前端 JavaScript

博客专用功能

  • Content Collections:Astro 的类型安全内容管理
  • Google Gemini API:AI 自动翻译引擎
  • Playwright:E2E 测试自动化
  • remark-link-card-plus:富链接卡片生成

实现的核心架构

1. Content Collections 设计

我们从传统的 Markdown 文件管理切换到了 Astro 的 Content Collections。再也不用烦恼“咦?这个 frontmatter 的写法对不对来着?”了。

// src/content/config.ts
const blogCollection = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    category: z.enum(['ai', 'engineering', 'founder', 'lab']),
    tags: z.array(z.string()).default([]),
    lang: z.enum(['ja', 'en']).default('ja'),
    featured: z.boolean().default(false),
  }),
});

其优势如下:

  • 在构建时进行类型校验,防止元数据错误
  • 通过 Hot reloading 实现实时更新
  • 自动进行 Schema 校验

2. AI 翻译系统设计

我们使用 Google Gemini API 构建了自动翻译系统。这比拜托人工翻译要快得多,而且它还绝不抱怨。

// scripts/translate-blog.js
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
const model = genAI.getGenerativeModel({ 
  model: "gemini-2.5-flash-lite-preview-06-17" 
});

async function translateBlogPost(japaneseContent) {
  const prompt = `Translate the following Japanese blog post to English. 
  Translate technical terms appropriately and make it readable English:

${japaneseContent}`;

  const result = await model.generateContent(prompt);
  return result.response.text().trim();
}

其特点如下:

  • 速率限制:15 次请求/分钟的保守限制
  • 错误处理:基于指数退避的重试机制
  • YAML 安全性:对 frontmatter 进行适当的转义处理

3. 速率限制系统

为了让 API 稳定运行,我们实现了速率限制功能。这也是为了防止被 Google 警告“喂,你请求发太频繁了!”。

// scripts/rate-limiter.js
class RateLimiter {
  constructor({ requestsPerMinute = 15, maxRetries = 3 }) {
    this.requestsPerMinute = requestsPerMinute;
    this.maxRetries = maxRetries;
    this.requestTimes = [];
  }

  async executeWithRetry(operation, description) {
    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        await this.checkRateLimit();
        return await operation();
      } catch (error) {
        if (attempt === this.maxRetries) throw error;
        await this.backoff(attempt);
      }
    }
  }
}

4. E2E 测试的实现

使用 Playwright 实现了针对博客功能的综合测试。“看着能跑就算完事!”可不行。

// e2e/blog.spec.ts
test.describe('Blog functionality', () => {
  test('should navigate to Japanese blog', async ({ page }) => {
    await page.goto('/blog/');
    await expect(page.locator('h1')).toContainText('技術ブログ');
  });

  test('should display OGP image for blog posts', async ({ page }) => {
    await page.goto('/blog/');
    const firstPost = page.locator('[data-testid="blog-post"]').first();
    await firstPost.click();
    
    const ogImage = page.locator('meta[property="og:image"]');
    await expect(ogImage).toHaveAttribute('content', /\/og\/.*\.svg/);
  });
});

性能优化

压缩与优化配置

// astro.config.mjs
integrations: [
  compress({
    CSS: true,
    HTML: {
      'remove-comments': true,
      'minify-js': true,
      'minify-css': true
    },
    JavaScript: true,
    SVG: true
  }),
  compressor({
    gzip: true,
    brotli: true
  })
]

图片优化

  • 以 AVIF 格式分发图片
  • WebP 回退(Fallback)
  • 响应式图片尺寸(480w, 800w)

课题与解决方案

1. YAML Frontmatter 的转义问题

课题在于,翻译后的标题或描述中若包含特殊字符,会导致 YAML 解析错误。

作为解决方案,通过以下函数进行转义:

function escapeYAMLString(str) {
  return str
    .replace(/\\/g, "\\\\") // バックスラッシュを最初にエスケープ
    .replace(/"/g, '\\"');   // ダブルクォートをエスケープ
}

2. API 速率限制应对

课题在于,由于 Gemini API 的限制会导致请求失败。

解决方案是指数退避与智能速率限制:

  • 1 秒基础延迟
  • 阶梯式延迟,最高至 60 秒
  • 通过请求时间追踪进行事前限制

3. E2E 测试的稳定性

课题在于,动态内容会导致测试变得不稳定。

解决方案是利用 data-testid 属性来切实定位元素。

运营流程

新文章发布流程

  1. 撰写日文文章:在 /src/content/blog/ja/ 中创建 Markdown 文件
  2. AI 翻译:npm run translate src/content/blog/ja/article-name.md
  3. 运行测试:npm run test:e2e
  4. 构建:npm run build
  5. 部署:自动部署至 Firebase Hosting

质量保证

  • 类型检查:利用 TypeScript 进行编译时检查
  • Schema 校验:利用 Zod 进行内容校验
  • E2E 测试:利用 Playwright 进行功能测试

心得体会

1. Content Collections 的威力

与传统基于文件的方式相比,类型安全性和开发体验有了大幅提升。尤其是修改 Schema 时的影响范围变得一目了然,使安全重构成为可能。

现在有 TypeScript 提醒“类型不对哦!”,再也不用因运行时错误而心惊肉跳了。太棒了。

2. AI 翻译的实用性

Google Gemini API 在技术文章上的翻译质量非常高,人工微调降到了最低限度。不过,专业术语的一致性仍需要持续改进。

当“npm”被翻译成“エヌピーエム(注:音译读音)”的时候真是让人哭笑不得。嘛,倒也不能算错就是了。

3. E2E 测试的重要性

我们切身感受到,即便是静态网站生成器,对于路由和元数据生成这类复杂功能而言,E2E 测试也是不可或缺的。

真想揍一顿过去那个觉得“既然是静态网站就不需要测试吧”的自己。

总结

通过实现这套博客系统,我们确认了在现代 JAMstack 架构中以下几点至关重要:

  1. 类型安全:通过 Content Collections 防患错误于设计之时(再也不用对着运行时错误流泪)
  2. 自动化:通过 AI 翻译与 E2E 测试实现运营高效化(逐步接管人类的工作)
  3. 性能:通过积极的优化实现高速化(极速之巅)
  4. 可维护性:通过清晰的架构实现可持续开发(造福未来的自己)

作为一个技术博客平台,我们自动化了从文章撰写到发布的整个流程,打造出了能让开发者专注于内容创作的环境。

换句话说,这下彻底找不到偷懒不更新博客的借口了。太好了……对吧?

想在贵公司推进本文的内容?

AI 受托开发的咨询,由 AI 接待「Cloudia」受理。

详细了解AI 基础设施与受托开发

Support this article

If this article was helpful, please support us with a tip. You can choose any amount.

Secure payment via Stripe
最后更新: 2026年10月23日

相关文章

阅读文章
工程

从Git历史中能看到什么、看不到什么。用Grift核算了4个OSS

本文分开写:按明示的规则,从git历史中能数出什么,数不出什么。对4个真实OSS按仓库做了核算。这不保证人的能力,也不保证记录是完整的。

#Grift #OSS +3
阅读文章
工程

如何在实现中坚守“不评分、不排名”。Grift CLI在下一版本中确定的5项条件

Grift CLI承诺不给出分数或排名。在2026年10月的核对中发现,虽然在注释中写明了并非排名,但表格仍是按提交次数从多到少排列的。面向发包方、SES销售以及工程师,我们将以通俗易懂的方式阐述对0.8版本设定的5项验收条件。

#Grift #OSS +3
阅读文章
工程

AI开发与定制开发的估算陷入个人经验化的原因——将估算依据转化为公司资产的方法

本文将解析定制开发的估算因人而异的原因,并讲解如何利用GitHub实际业绩、市场行情和工时明细,打造具有充分说服力的估算依据。同时介绍Grift的应用思路。

#見積もり #受託開発 +2