Skip to content

Commit 7c4800a

Browse files
committed
feat: update
1 parent dacfdc1 commit 7c4800a

4 files changed

Lines changed: 132 additions & 14 deletions

File tree

docs/content/2.guide/1.features.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,26 @@ Make sure to replace `TYPE` with one of the options listed below and `YOUR_PACKA
158158

159159
You can further customize your badges by appending query parameters to the badge URL.
160160

161-
##### `colorA`
161+
##### `labelColor`
162162

163163
Overrides the default label color. You can pass a standard hex code (with or without the `#` prefix).
164164

165165
- **Default**: `#0a0a0a`
166-
- **Usage**: `?colorA=HEX_CODE`
166+
- **Usage**: `?labelColor=HEX_CODE`
167167

168-
##### `colorB`
168+
##### `label`
169+
170+
Overrides the default label text. You can pass any string to customize the label displayed on the badge.
171+
172+
- **Default**: Depends on the badge type (e.g., "version", "downloads/mo").
173+
- **Usage**: `?label=YOUR_LABEL`
174+
175+
##### `color`
169176

170177
Overrides the default strategy color. You can pass a standard hex code (with or without the `#` prefix).
171178

172179
- **Default**: Depends on the badge type (e.g., version is blue, downloads are orange).
173-
- **Usage**: `?colorB=HEX_CODE`
180+
- **Usage**: `?color=HEX_CODE`
174181

175182
| Example | URL |
176183
| :------------- | :------------------------------------- |

scripts/create-translation.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/* eslint-disable no-console */
2+
import process from 'node:process'
3+
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
4+
import { join } from 'node:path'
5+
import { fileURLToPath } from 'node:url'
6+
7+
const LOCALES_DIRECTORY = fileURLToPath(new URL('../i18n/locales', import.meta.url))
8+
const REFERENCE_FILE_NAME = 'en.json'
9+
10+
const COLORS = {
11+
reset: '\x1b[0m',
12+
red: '\x1b[31m',
13+
green: '\x1b[32m',
14+
yellow: '\x1b[33m',
15+
cyan: '\x1b[36m',
16+
} as const
17+
18+
type NestedObject = { [key: string]: unknown }
19+
20+
const loadJson = (filePath: string): NestedObject => {
21+
if (!existsSync(filePath)) {
22+
console.error(`${COLORS.red}Error: File not found at ${filePath}${COLORS.reset}`)
23+
process.exit(1)
24+
}
25+
return JSON.parse(readFileSync(filePath, 'utf-8')) as NestedObject
26+
}
27+
28+
const isNested = (val: unknown): val is NestedObject =>
29+
val !== null && typeof val === 'object' && !Array.isArray(val)
30+
31+
const createTranslationStructure = (reference: NestedObject, prefix = ''): NestedObject => {
32+
const result: NestedObject = {}
33+
34+
for (const key of Object.keys(reference)) {
35+
const refValue = reference[key]
36+
37+
if (isNested(refValue)) {
38+
result[key] = createTranslationStructure(refValue, prefix ? `${prefix}.${key}` : key)
39+
} else {
40+
result[key] = `EN TEXT TO REPLACE: ${refValue}`
41+
}
42+
}
43+
44+
return result
45+
}
46+
47+
const run = (): void => {
48+
const args = process.argv.slice(2)
49+
50+
if (args.length === 0) {
51+
console.log(`${COLORS.yellow}Usage: pnpm create-translation <locale-code>${COLORS.reset}`)
52+
console.log(`Example: pnpm create-translation ko-KR`)
53+
console.log(`\nAvailable locale codes format: xx-XX (e.g., fr-FR, zh-CN)`)
54+
process.exit(1)
55+
}
56+
57+
const localeCode = args[0]
58+
59+
if (!localeCode) {
60+
console.error(`${COLORS.red}Error: Locale code is required${COLORS.reset}`)
61+
process.exit(1)
62+
}
63+
64+
if (!/^[a-z]{2}(-[A-Z]{2})?$/.test(localeCode)) {
65+
console.error(
66+
`${COLORS.red}Error: Invalid locale code format. Expected format: xx or xx-XX (e.g., fr or fr-FR)${COLORS.reset}`,
67+
)
68+
process.exit(1)
69+
}
70+
71+
const targetFileName = `${localeCode}.json`
72+
const targetFilePath = join(LOCALES_DIRECTORY, targetFileName)
73+
74+
if (existsSync(targetFilePath)) {
75+
console.error(
76+
`${COLORS.red}Error: Translation file already exists: ${targetFileName}${COLORS.reset}`,
77+
)
78+
console.log(`Use compare-translations.ts to update existing translations.`)
79+
process.exit(1)
80+
}
81+
82+
const referenceFilePath = join(LOCALES_DIRECTORY, REFERENCE_FILE_NAME)
83+
const referenceContent = loadJson(referenceFilePath)
84+
85+
console.log(`${COLORS.cyan}=== Creating new translation file ===${COLORS.reset}`)
86+
console.log(`Reference: ${REFERENCE_FILE_NAME}`)
87+
console.log(`Target: ${targetFileName}`)
88+
89+
const newTranslation = createTranslationStructure(referenceContent)
90+
91+
writeFileSync(targetFilePath, JSON.stringify(newTranslation, null, 2) + '\n', 'utf-8')
92+
93+
console.log(`\n${COLORS.green}✓ Successfully created ${targetFileName}${COLORS.reset}`)
94+
console.log(`Location: ${targetFilePath}`)
95+
console.log(`\n${COLORS.yellow}Next steps:${COLORS.reset}`)
96+
console.log(`1. Translate the English placeholder text to ${localeCode}`)
97+
console.log(`2. Run: pnpm i18n:check ${localeCode} to verify the translation`)
98+
console.log(`3. Run: pnpm i18n:check:fix ${localeCode} to sync with latest changes`)
99+
}
100+
101+
run()

server/api/registry/badge/[type]/[...pkg].get.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ const BUNDLEPHOBIA_API = 'https://bundlephobia.com/api/size'
1212
const NPMS_API = 'https://api.npms.io/v2/package'
1313

1414
const QUERY_SCHEMA = v.object({
15-
colorA: v.optional(v.string()),
15+
color: v.optional(v.string()),
1616
name: v.optional(v.string()),
17-
colorB: v.optional(v.string()),
17+
labelColor: v.optional(v.string()),
18+
label: v.optional(v.string()),
1819
})
1920

2021
const COLORS = {
@@ -263,9 +264,10 @@ export default defineCachedEventHandler(
263264
})
264265

265266
const queryParams = v.safeParse(QUERY_SCHEMA, query)
266-
const userColor = queryParams.success ? queryParams.output.colorB : undefined
267-
const userColorLeft = queryParams.success ? queryParams.output.colorA : undefined
267+
const userColor = queryParams.success ? queryParams.output.color : undefined
268+
const labelColor = queryParams.success ? queryParams.output.labelColor : undefined
268269
const showName = queryParams.success && queryParams.output.name === 'true'
270+
const userLabel = queryParams.success ? queryParams.output.label : undefined
269271

270272
const badgeTypeResult = v.safeParse(BadgeTypeSchema, typeParam)
271273
const strategyKey = badgeTypeResult.success ? badgeTypeResult.output : 'version'
@@ -276,13 +278,13 @@ export default defineCachedEventHandler(
276278
const pkgData = await fetchNpmPackage(packageName)
277279
const strategyResult = await strategy(pkgData, requestedVersion)
278280

279-
const finalLabel = showName ? packageName : strategyResult.label
281+
const finalLabel = userLabel ? userLabel : showName ? packageName : strategyResult.label
280282
const finalValue = strategyResult.value
281283

282284
const rawColor = userColor ?? strategyResult.color
283285
const finalColor = rawColor?.startsWith('#') ? rawColor : `#${rawColor}`
284286

285-
const rawLeftColor = userColorLeft ?? '#0a0a0a'
287+
const rawLeftColor = labelColor ?? '#0a0a0a'
286288
const finalLeftColor = rawLeftColor?.startsWith('#') ? rawLeftColor : `#${rawLeftColor}`
287289

288290
const leftWidth = measureTextWidth(finalLabel)

test/e2e/badge.spec.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,22 +102,30 @@ test.describe('badge API', () => {
102102
})
103103
})
104104

105-
test('custom colorA parameter is applied to SVG', async ({ page, baseURL }) => {
105+
test('custom labelColor parameter is applied to SVG', async ({ page, baseURL }) => {
106106
const customColor = '00ff00'
107-
const url = toLocalUrl(baseURL, `/api/registry/badge/version/nuxt?colorA=${customColor}`)
107+
const url = toLocalUrl(baseURL, `/api/registry/badge/version/nuxt?labelColor=${customColor}`)
108108
const { body } = await fetchBadge(page, url)
109109

110110
expect(body).toContain(`fill="#${customColor}"`)
111111
})
112112

113-
test('custom colorB parameter is applied to SVG', async ({ page, baseURL }) => {
113+
test('custom color parameter is applied to SVG', async ({ page, baseURL }) => {
114114
const customColor = 'ff69b4'
115-
const url = toLocalUrl(baseURL, `/api/registry/badge/version/nuxt?colorB=${customColor}`)
115+
const url = toLocalUrl(baseURL, `/api/registry/badge/version/nuxt?color=${customColor}`)
116116
const { body } = await fetchBadge(page, url)
117117

118118
expect(body).toContain(`fill="#${customColor}"`)
119119
})
120120

121+
test('custom label parameter is applied to SVG', async ({ page, baseURL }) => {
122+
const customLabel = 'my-label'
123+
const url = toLocalUrl(baseURL, `/api/registry/badge/version/nuxt?label=${customLabel}`)
124+
const { body } = await fetchBadge(page, url)
125+
126+
expect(body).toContain(customLabel)
127+
})
128+
121129
test('invalid badge type defaults to version strategy', async ({ page, baseURL }) => {
122130
const url = toLocalUrl(baseURL, '/api/registry/badge/invalid-type/nuxt')
123131
const { body } = await fetchBadge(page, url)

0 commit comments

Comments
 (0)