Complete Blog System Implementation in Astro 4.8 - A Comprehensive Approach Including AI Translation and E2E Testing
Record of implementing a high-performance blog system based on Astro Content Collections. This explanation covers the comprehensive development process, including AI automatic translation using the Google Gemini API, Playwright E2E testing, and rate limiting features.
Overview
In a recent development, we’ve implemented a comprehensive blog system based on Astro 4.8.7. This system goes beyond simple Markdown display, offering a modern technical blog platform with features like AI auto-translation, end-to-end testing, and functionalities geared towards full-scale operation.
Technical Stack
Core Technologies
- Astro 4.8.7: High-speed SSG with Island Architecture
- TypeScript: Type-safe development environment
- Tailwind CSS: Utility-first styling
- Alpine.js 3.14.0: Lightweight frontend JavaScript
Blog-Specific Features
- Content Collections: Astro’s type-safe content management
- Google Gemini API: AI auto-translation engine
- Playwright: E2E test automation
- remark-link-card-plus: Rich link card generation
Core Implementation Architecture
1. Content Collections Design
We adopted Astro’s Content Collections
over traditional Markdown file management:
// 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),
}),
});
Advantages:
- Prevents metadata errors through build-time type validation
- Real-time updates with Hot reloading
- Automatic schema validation
2. AI Translation System Design
We built an auto-translation system utilizing the 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();
}
Features:
- Rate Limiting: Conservative limit of 15 requests per minute
- Error Handling: Retry functionality with exponential backoff
- YAML Safety: Proper escaping for frontmatter
3. Rate Limiting System
Rate limiting features for stable API operations:
// 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 Test Implementation
Comprehensive testing of blog functionalities using 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/);
});
});
Performance Optimization
Compression and Optimization Settings
// 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
})
]
Image Optimization
- Image delivery in AVIF format
- WebP fallback
- Responsive image sizes (480w, 800w)
Challenges and Solutions
1. YAML Frontmatter Escaping Issue
Challenge: YAML parse errors when translated titles or descriptions contain special characters.
Solution:
function escapeYAMLString(str) {
return str
.replace(/\\/g, "\\\\") // Escape backslashes first
.replace(/"/g, '\\"'); // Escape double quotes
}
2. API Rate Limit Handling
Challenge: Request failures due to Gemini API limitations.
Solution: Exponential backoff and intelligent rate limiting
- 1-second base delay
- Gradual delays up to 60 seconds
- Proactive limiting by tracking request times
3. E2E Test Stability
Challenge: Test instability due to dynamic content.
Solution: Reliable element identification using data-testid
attributes.
Operational Flow
New Article Posting Process
- Create Japanese Article: Create a Markdown file in
/src/content/blog/ja/
- AI Translation:
npm run translate src/content/blog/ja/article-name.md
- Run Tests:
npm run test:e2e
- Build:
npm run build
- Deploy: Automatic deployment to Firebase Hosting
Quality Assurance
- Type Checking: Compile-time checks with TypeScript
- Schema Validation: Content validation with Zod
- E2E Tests: Functional testing with Playwright
Lessons Learned
1. The Power of Content Collections
Compared to traditional file-based management, type safety and developer experience have dramatically improved. In particular, the clear impact scope during schema changes enables safer refactoring.
2. Practicality of AI Translation
The translation quality from Google Gemini API is exceptionally high for technical articles, minimizing the need for manual fine-tuning. However, consistency in specialized terminology requires ongoing improvement.
3. Importance of E2E Testing
We’ve realized that for complex functionalities like routing and metadata generation, even in static site generators, E2E testing is indispensable.
Performance Results
- First Contentful Paint: 0.8 seconds
- Largest Contentful Paint: 1.2 seconds
- Cumulative Layout Shift: 0.05
- Total Bundle Size: 45KB (after gzip compression)
Conclusion
Through the implementation of this blog system, we’ve confirmed the importance of the following in a modern JAMstack architecture:
- Type Safety: Preventing design-time errors with Content Collections
- Automation: Improving operational efficiency with AI translation and E2E tests
- Performance: Speed improvements through proactive optimization
- Maintainability: Sustainable development through a clear architecture
As a technical blog platform, we’ve achieved complete automation from article writing to publication, creating an environment where developers can focus on essential content creation.
Support this article
Tip functionality is coming soon. Please stay tuned.
Related Posts
The Story of Creating an Amazing Homepage After Continuously Challenging Abe Hiroshi's Homepage
The trajectory and technical insights of a man who, self-proclaimed as "the owner of the fastest-downloading homepage," persistently challenged Abe Hiroshi''s homepage.