@robsun/create-keystone-app 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/create-keystone-app.js +0 -0
- package/dist/create-module.js +0 -0
- package/package.json +7 -6
- package/template/.claude/skills/keystone-dev/SKILL.md +38 -13
- package/template/.claude/skills/keystone-dev/references/CAPABILITIES.md +80 -2
- package/template/.claude/skills/keystone-dev/references/TEMPLATES.md +220 -0
- package/template/.codex/skills/keystone-dev/SKILL.md +38 -13
- package/template/.codex/skills/keystone-dev/references/CAPABILITIES.md +80 -2
- package/template/.codex/skills/keystone-dev/references/TEMPLATES.md +220 -0
- package/template/apps/server/go.mod +1 -1
- package/template/apps/server/go.sum +1 -0
- package/template/apps/server/internal/modules/example/api/handler/item_handler.go +35 -32
- package/template/apps/server/internal/modules/example/domain/service/errors.go +13 -0
- package/template/apps/server/internal/modules/example/i18n/i18n.go +16 -0
- package/template/apps/server/internal/modules/example/i18n/keys.go +23 -0
- package/template/apps/server/internal/modules/example/i18n/locales/en-US.json +18 -0
- package/template/apps/server/internal/modules/example/i18n/locales/zh-CN.json +18 -0
- package/template/apps/server/internal/modules/example/module.go +9 -3
- package/template/apps/web/package.json +3 -1
- package/template/apps/web/src/app.config.ts +8 -1
- package/template/apps/web/src/modules/example/index.ts +7 -1
- package/template/apps/web/src/modules/example/locales/en-US/example.json +32 -0
- package/template/apps/web/src/modules/example/locales/zh-CN/example.json +32 -0
- package/template/apps/web/src/modules/example/pages/ExampleItemsPage.tsx +47 -45
- package/template/apps/web/src/modules/example/routes.tsx +6 -2
- package/template/docs/CONVENTIONS.md +73 -1
- package/template/docs/I18N.md +319 -0
- package/template/package.json +1 -0
- package/template/scripts/generate-i18n-types.js +154 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* i18n Type Definition Generator
|
|
4
|
+
*
|
|
5
|
+
* Generates TypeScript type definitions from JSON locale files
|
|
6
|
+
* for type-safe translations in your application.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node scripts/generate-i18n-types.js
|
|
10
|
+
*
|
|
11
|
+
* Output:
|
|
12
|
+
* apps/web/src/types/i18n.d.ts
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'fs'
|
|
16
|
+
import path from 'path'
|
|
17
|
+
import { fileURLToPath } from 'url'
|
|
18
|
+
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
20
|
+
const __dirname = path.dirname(__filename)
|
|
21
|
+
const rootDir = path.resolve(__dirname, '..')
|
|
22
|
+
const webSrcDir = path.join(rootDir, 'apps/web/src')
|
|
23
|
+
|
|
24
|
+
// Configuration
|
|
25
|
+
const LOCALE_DIRS = [
|
|
26
|
+
// Platform locales from keystone-web-core (reference only, not generated)
|
|
27
|
+
// Module locales
|
|
28
|
+
path.join(webSrcDir, 'modules'),
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
const OUTPUT_FILE = path.join(webSrcDir, 'types/i18n.d.ts')
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Recursively flattens a nested object into dot-notation keys
|
|
35
|
+
*/
|
|
36
|
+
function flattenKeys(obj, prefix = '') {
|
|
37
|
+
const keys = []
|
|
38
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
39
|
+
const fullKey = prefix ? `${prefix}.${key}` : key
|
|
40
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
41
|
+
keys.push(...flattenKeys(value, fullKey))
|
|
42
|
+
} else {
|
|
43
|
+
keys.push(fullKey)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return keys
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Finds all locale JSON files in a directory
|
|
51
|
+
*/
|
|
52
|
+
function findLocaleFiles(dir) {
|
|
53
|
+
const files = []
|
|
54
|
+
if (!fs.existsSync(dir)) return files
|
|
55
|
+
|
|
56
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
const fullPath = path.join(dir, entry.name)
|
|
59
|
+
if (entry.isDirectory()) {
|
|
60
|
+
// Look for locales directory
|
|
61
|
+
if (entry.name === 'locales') {
|
|
62
|
+
const zhDir = path.join(fullPath, 'zh-CN')
|
|
63
|
+
if (fs.existsSync(zhDir)) {
|
|
64
|
+
const jsonFiles = fs.readdirSync(zhDir).filter(f => f.endsWith('.json'))
|
|
65
|
+
for (const jsonFile of jsonFiles) {
|
|
66
|
+
files.push({
|
|
67
|
+
namespace: path.basename(jsonFile, '.json'),
|
|
68
|
+
path: path.join(zhDir, jsonFile),
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
files.push(...findLocaleFiles(fullPath))
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return files
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Generates TypeScript type definitions
|
|
82
|
+
*/
|
|
83
|
+
function generateTypes() {
|
|
84
|
+
const namespaces = new Map()
|
|
85
|
+
|
|
86
|
+
// Collect all locale files
|
|
87
|
+
for (const localeDir of LOCALE_DIRS) {
|
|
88
|
+
const files = findLocaleFiles(localeDir)
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
try {
|
|
91
|
+
const content = fs.readFileSync(file.path, 'utf-8')
|
|
92
|
+
const json = JSON.parse(content)
|
|
93
|
+
const keys = flattenKeys(json)
|
|
94
|
+
namespaces.set(file.namespace, keys)
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.warn(`Warning: Could not parse ${file.path}:`, err.message)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (namespaces.size === 0) {
|
|
102
|
+
console.log('No locale files found. Skipping type generation.')
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Generate TypeScript content
|
|
107
|
+
let output = `// Auto-generated by scripts/generate-i18n-types.js
|
|
108
|
+
// Do not edit manually
|
|
109
|
+
|
|
110
|
+
import 'react-i18next'
|
|
111
|
+
|
|
112
|
+
declare module 'react-i18next' {
|
|
113
|
+
interface CustomTypeOptions {
|
|
114
|
+
defaultNS: 'common'
|
|
115
|
+
resources: {
|
|
116
|
+
`
|
|
117
|
+
|
|
118
|
+
for (const [namespace, keys] of namespaces) {
|
|
119
|
+
output += ` ${namespace}: {\n`
|
|
120
|
+
for (const key of keys) {
|
|
121
|
+
// Create nested type from dot notation
|
|
122
|
+
output += ` '${key}': string\n`
|
|
123
|
+
}
|
|
124
|
+
output += ` }\n`
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
output += ` }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Type-safe translation keys
|
|
132
|
+
`
|
|
133
|
+
|
|
134
|
+
for (const [namespace, keys] of namespaces) {
|
|
135
|
+
const typeName = namespace.charAt(0).toUpperCase() + namespace.slice(1) + 'Keys'
|
|
136
|
+
output += `export type ${typeName} = \n`
|
|
137
|
+
output += keys.map(k => ` | '${k}'`).join('\n')
|
|
138
|
+
output += '\n\n'
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Ensure output directory exists
|
|
142
|
+
const outputDir = path.dirname(OUTPUT_FILE)
|
|
143
|
+
if (!fs.existsSync(outputDir)) {
|
|
144
|
+
fs.mkdirSync(outputDir, { recursive: true })
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Write output file
|
|
148
|
+
fs.writeFileSync(OUTPUT_FILE, output, 'utf-8')
|
|
149
|
+
console.log(`Generated i18n types: ${OUTPUT_FILE}`)
|
|
150
|
+
console.log(`Namespaces: ${[...namespaces.keys()].join(', ')}`)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Run generator
|
|
154
|
+
generateTypes()
|