Skip to content

Commit 2bcaf96

Browse files
ericyangpanclaude
andcommitted
docs(i18n): add i18n architecture rules and validation scripts
Add comprehensive i18n documentation and validation tools: - I18N-ARCHITECTURE-RULES.md: detailed architecture rules for translation files - scripts/validate/: url validation scripts for multi-language testing - scripts/README.md: updated with validate/ directory and compare-models.mjs Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ddfdd52 commit 2bcaf96

11 files changed

+1412
-2
lines changed

docs/I18N-ARCHITECTURE-RULES.md

Lines changed: 495 additions & 0 deletions
Large diffs are not rendered by default.

scripts/README.md

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# Scripts Documentation
22

3-
This directory contains utility scripts for managing and validating the AI Coding Stack project. Scripts are organized into three categories:
3+
This directory contains utility scripts for managing and validating the AI Coding Stack project. Scripts are organized into four categories:
44

55
- **`generate/`** - Generation scripts that create derived files
66
- **`refactor/`** - Refactoring scripts that reorganize or reformat data
77
- **`fetch/`** - Data fetching scripts that retrieve external data
8+
- **`validate/`** - URL validation scripts that check website accessibility
89

910
## Directory Structure
1011

@@ -21,7 +22,15 @@ scripts/
2122
│ └── export-vendors.mjs
2223
├── fetch/
2324
│ ├── index.mjs # Entry point for all fetch scripts
24-
│ └── fetch-github-stars.mjs
25+
│ ├── fetch-github-stars.mjs
26+
│ └── compare-models.mjs
27+
├── validate/
28+
│ ├── visit-all-urls.mjs
29+
│ ├── visit-urls-en-static-all-slugs.mjs
30+
│ ├── visit-urls-en-static-one-slug.mjs
31+
│ ├── visit-urls-all-locales-static-all-slugs.mjs
32+
│ ├── visit-urls-all-locales-static-one-slug.mjs
33+
│ └── lib/ # Shared validation utilities
2534
├── _shared/
2635
│ └── runner.mjs # Shared script runner for entry points
2736
└── temp/ # Temporary experimental scripts
@@ -45,6 +54,9 @@ npm run refactor
4554

4655
# Run all fetch scripts
4756
npm run fetch
57+
58+
# Run all validation scripts (manual execution)
59+
node scripts/validate/visit-all-urls.mjs
4860
```
4961

5062
### Running Individual Scripts
@@ -61,6 +73,11 @@ npm run refactor:sort-fields
6173

6274
# Fetch scripts
6375
npm run fetch:github-stars
76+
77+
# Validation scripts (run directly with node)
78+
node scripts/validate/visit-all-urls.mjs
79+
node scripts/validate/visit-urls-en-static-all-slugs.mjs
80+
node scripts/validate/visit-urls-all-locales-static-all-slugs.mjs
6481
```
6582

6683
Or directly using Node:
@@ -69,11 +86,17 @@ Or directly using Node:
6986
# Run generation/fetch scripts
7087
node scripts/generate/index.mjs metadata
7188
node scripts/fetch/index.mjs github-stars
89+
node scripts/fetch/index.mjs compare-models
7290

7391
# Run specific refactor scripts
7492
node scripts/refactor/index.mjs sort-manifest-fields
7593
node scripts/refactor/index.mjs sort-locales-fields
7694
node scripts/refactor/index.mjs export-vendors
95+
96+
# Run validation scripts
97+
node scripts/validate/visit-all-urls.mjs
98+
node scripts/validate/visit-urls-en-static-all-slugs.mjs
99+
node scripts/validate/visit-urls-all-locales-static-all-slugs.mjs
77100
```
78101

79102
## Validation (Test-based)
@@ -242,6 +265,30 @@ node scripts/fetch/index.mjs github-stars
242265

243266
**Note:** Without a GitHub token, you may hit rate limits (60 requests/hour).
244267

268+
### compare-models.mjs
269+
270+
Compares model manifest data with API reference data to identify mismatches.
271+
272+
```bash
273+
node scripts/fetch/index.mjs compare-models
274+
# or
275+
node scripts/fetch/compare-models.mjs
276+
```
277+
278+
**What it does:**
279+
- Reads model manifests from `manifests/models/`
280+
- Compares with API reference data from `tmp/models-dev-api.json`
281+
- Uses mapping from `manifests/mapping.json` to match vendors and models
282+
- Reports matched models, mismatched fields, and unmatched models
283+
284+
**Output:**
285+
- Perfectly matched models (all fields match)
286+
- Matched models with field mismatches
287+
- Matched models with skipped fields (null in manifest)
288+
- Unmatched models (not found in API)
289+
290+
**Note:** This script requires `tmp/models-dev-api.json` to be present. It's used for data validation and synchronization.
291+
245292
## Additional Tools
246293

247294
### Benchmark Fetcher
@@ -259,6 +306,87 @@ node .claude/skills/benchmark-fetcher/scripts/fetch-benchmarks.mjs
259306

260307
For full documentation, see `.claude/skills/benchmark-fetcher/SKILL.md`
261308

309+
## URL Validation Scripts
310+
311+
The `validate/` directory contains scripts for checking website URL accessibility. These scripts visit URLs and verify they return successful HTTP responses.
312+
313+
### visit-all-urls.mjs
314+
315+
Visits all URLs on the website (all locales, all static pages, all slugs).
316+
317+
```bash
318+
node scripts/validate/visit-all-urls.mjs
319+
```
320+
321+
**What it does:**
322+
- Builds a comprehensive list of all website URLs
323+
- Visits each URL with HTTP HEAD requests
324+
- Reports success/failure status for each URL
325+
- Supports retries and timeout handling
326+
327+
**Environment variables:**
328+
- `BASE_URL` - Base URL to test (default: `http://localhost:3000`)
329+
330+
### visit-urls-en-static-all-slugs.mjs
331+
332+
Visits URLs with specific configuration: English locale only, all static pages, all slugs per route type.
333+
334+
```bash
335+
node scripts/validate/visit-urls-en-static-all-slugs.mjs
336+
```
337+
338+
**Configuration:**
339+
- Locales: English only
340+
- Static pages: All
341+
- Dynamic routes: All slugs for each route type
342+
343+
### visit-urls-en-static-one-slug.mjs
344+
345+
Visits URLs with specific configuration: English locale only, all static pages, one slug per route type.
346+
347+
```bash
348+
node scripts/validate/visit-urls-en-static-one-slug.mjs
349+
```
350+
351+
**Configuration:**
352+
- Locales: English only
353+
- Static pages: All
354+
- Dynamic routes: One slug per route type (for faster testing)
355+
356+
### visit-urls-all-locales-static-all-slugs.mjs
357+
358+
Visits URLs with specific configuration: All locales, all static pages, all slugs per route type.
359+
360+
```bash
361+
node scripts/validate/visit-urls-all-locales-static-all-slugs.mjs
362+
```
363+
364+
**Configuration:**
365+
- Locales: All configured locales
366+
- Static pages: All
367+
- Dynamic routes: All slugs for each route type
368+
369+
### visit-urls-all-locales-static-one-slug.mjs
370+
371+
Visits URLs with specific configuration: All locales, all static pages, one slug per route type.
372+
373+
```bash
374+
node scripts/validate/visit-urls-all-locales-static-one-slug.mjs
375+
```
376+
377+
**Configuration:**
378+
- Locales: All configured locales
379+
- Static pages: All
380+
- Dynamic routes: One slug per route type (for faster testing)
381+
382+
**Common features:**
383+
- Concurrent requests (default: 5 concurrent)
384+
- Request timeout (default: 10 seconds)
385+
- Automatic retries (default: 2 retries)
386+
- Summary statistics
387+
388+
**Note:** These scripts make HTTP requests and require the website to be running. They are useful for pre-deployment validation and CI/CD pipelines.
389+
262390
## Build Process
263391

264392
The build process runs validation tests and generation scripts automatically:
@@ -302,6 +430,9 @@ npm run refactor
302430

303431
# Run all fetch scripts (if needed)
304432
npm run fetch
433+
434+
# Run URL validation scripts (if needed)
435+
node scripts/validate/visit-all-urls.mjs
305436
```
306437

307438
Or run individual checks as needed:
@@ -313,6 +444,8 @@ npm run generate:metadata
313444
npm run refactor:sort-fields
314445
npm run refactor:sort-locales-fields
315446
npm run fetch:github-stars
447+
node scripts/fetch/index.mjs compare-models
448+
node scripts/validate/visit-all-urls.mjs
316449
```
317450

318451
## Manual Execution

scripts/validate/lib/config.mjs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Configuration constants
3+
*/
4+
5+
import path from 'node:path'
6+
import { fileURLToPath } from 'node:url'
7+
8+
const __filename = fileURLToPath(import.meta.url)
9+
const __dirname = path.dirname(__filename)
10+
export const ROOT_DIR = path.resolve(__dirname, '../../..')
11+
12+
// Locales configuration
13+
export const LOCALES = [
14+
'en',
15+
'de',
16+
'es',
17+
'fr',
18+
'id',
19+
'ja',
20+
'ko',
21+
'pt',
22+
'ru',
23+
'tr',
24+
'zh-Hans',
25+
'zh-Hant',
26+
]
27+
28+
// Base URL - can be overridden via BASE_URL environment variable
29+
export const BASE_URL = process.env.BASE_URL || 'http://localhost:3000'
30+
31+
// Delay between requests in milliseconds - can be overridden via REQUEST_DELAY environment variable
32+
// Default: 100ms (0.1 second) between requests to avoid overwhelming the server
33+
export const REQUEST_DELAY = Number.parseInt(process.env.REQUEST_DELAY || '100', 10)
34+
35+
/**
36+
* Get locale prefix for URL
37+
*/
38+
export function getLocalePrefix(locale) {
39+
return locale === 'en' ? '' : `/${locale}`
40+
}

scripts/validate/lib/routes.mjs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* Route and slug utilities
3+
*/
4+
5+
import fs from 'node:fs'
6+
import path from 'node:path'
7+
import { ROOT_DIR } from './config.mjs'
8+
9+
/**
10+
* Read JSON file
11+
*/
12+
export function readJsonFile(filePath) {
13+
const content = fs.readFileSync(filePath, 'utf8')
14+
return JSON.parse(content)
15+
}
16+
17+
/**
18+
* Get all static routes
19+
*/
20+
export function getStaticRoutes() {
21+
return [
22+
'/',
23+
'/ides',
24+
'/clis',
25+
'/extensions',
26+
'/models',
27+
'/model-providers',
28+
'/vendors',
29+
'/articles',
30+
'/ai-coding-stack',
31+
'/docs',
32+
'/curated-collections',
33+
'/manifesto',
34+
'/ai-coding-landscape',
35+
'/open-source-rank',
36+
'/search',
37+
'/clis/comparison',
38+
'/extensions/comparison',
39+
'/ides/comparison',
40+
'/models/comparison',
41+
]
42+
}
43+
44+
/**
45+
* Get all slugs from manifests directory
46+
*/
47+
export function getSlugsFromManifests(category) {
48+
const manifestsDir = path.join(ROOT_DIR, 'manifests', category)
49+
if (!fs.existsSync(manifestsDir)) {
50+
return []
51+
}
52+
53+
const files = fs.readdirSync(manifestsDir).filter(f => f.endsWith('.json'))
54+
const slugs = []
55+
56+
for (const file of files) {
57+
const filePath = path.join(manifestsDir, file)
58+
try {
59+
const data = readJsonFile(filePath)
60+
if (data && typeof data === 'object' && data.id) {
61+
slugs.push(data.id)
62+
}
63+
} catch (error) {
64+
console.warn(`Warning: Failed to read ${filePath}: ${error.message}`)
65+
}
66+
}
67+
68+
return slugs
69+
}
70+
71+
/**
72+
* Get article slugs from content directory
73+
*/
74+
export function getArticleSlugs() {
75+
try {
76+
// Read from content directory
77+
const articlesDir = path.join(ROOT_DIR, 'content/articles/en')
78+
if (!fs.existsSync(articlesDir)) {
79+
return []
80+
}
81+
82+
const files = fs.readdirSync(articlesDir).filter(f => f.endsWith('.mdx'))
83+
return files.map(file => file.replace('.mdx', ''))
84+
} catch (error) {
85+
console.warn(`Warning: Failed to get article slugs: ${error.message}`)
86+
return []
87+
}
88+
}
89+
90+
/**
91+
* Get doc slugs from content directory
92+
*/
93+
export function getDocSlugs() {
94+
try {
95+
const docsDir = path.join(ROOT_DIR, 'content/docs/en')
96+
if (!fs.existsSync(docsDir)) {
97+
return []
98+
}
99+
100+
const files = fs.readdirSync(docsDir).filter(f => f.endsWith('.mdx'))
101+
return files.map(file => file.replace('.mdx', ''))
102+
} catch (error) {
103+
console.warn(`Warning: Failed to get doc slugs: ${error.message}`)
104+
return []
105+
}
106+
}
107+
108+
/**
109+
* Get dynamic route configurations
110+
*/
111+
export function getDynamicRoutes() {
112+
return [
113+
{ path: '/ides', category: 'ides' },
114+
{ path: '/clis', category: 'clis' },
115+
{ path: '/extensions', category: 'extensions' },
116+
{ path: '/models', category: 'models' },
117+
{ path: '/model-providers', category: 'providers' },
118+
{ path: '/vendors', category: 'vendors' },
119+
]
120+
}

0 commit comments

Comments
 (0)