Skip to content

Commit c92258e

Browse files
committed
Merge branch 'main' of https://github.com/npmx-dev/npmx.dev into fix/scrollable-versions
2 parents 45afe1b + 3a4c729 commit c92258e

14 files changed

Lines changed: 3090 additions & 53 deletions

File tree

CONTRIBUTING.md

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -256,31 +256,46 @@ The [config/i18n.ts](./config/i18n.ts) configuration file will be used to regist
256256
- `locales` object will be used to link the supported locales (country and single one)
257257
- `buildLocales` function will build the target locales
258258

259-
To register a new locale:
259+
To add a new locale:
260260

261-
- for a single country, your JSON file should include the language and the country in the name (for example, `pl-PL.json`) and register the info at `locales` object
262-
- for multiple country variants, you need to add the default language JSON file (for example for Spanish, `es.json`) and one of the country variants (for example for Spanish for Spain, `es-ES.json`); register the language at `countryLocaleVariants` object adding the country variants with the JSON country file and register the language at `locales` object using the language JSON file (check how we register `es`, `es-ES` and `es-419` in [config/i18n.ts](./config/i18n.ts))
261+
1. Create a new JSON file in [`i18n/locales/`](./i18n/locales) with the locale code as the filename (e.g., `uk-UA.json`, `de-DE.json`)
262+
2. Copy [`en.json`](./i18n/locales/en.json) and translate the strings
263+
3. Add the locale to the `locales` array in [config/i18n.ts](./config/i18n.ts):
263264

264-
The country file should contain will contain only the translations that differ from the language JSON file, Vue I18n will merge the messages for us.
265+
```typescript
266+
{
267+
code: 'uk-UA', // Must match the filename (without .json)
268+
file: 'uk-UA.json',
269+
name: 'Українська', // Native name of the language
270+
},
271+
```
265272

266-
To add a new locale:
273+
4. Copy your translation file to `lunaria/files/` for translation tracking:
274+
275+
```bash
276+
cp i18n/locales/uk-UA.json lunaria/files/uk-UA.json
277+
```
267278

268-
1. Add a new file at [locales](./i18n/locales) folder with the language code as the filename.
269-
2. Copy [en](./i18n/locales/en.json) and translate the strings
270-
3. Add the language to the `locales` array in [config/i18n.ts](./config/i18n.ts), below `en` and `ar`:
271-
- If your language has multiple country variants, add the generic one for language only (only if there are a lot of common entries, you can always add it as a new one)
272-
- Add all country variants in [country variants object](./config/i18n.ts)
273-
- Add all country variants files with empty `messages` object: `{}`
274-
- Translate the strings in the generic language file
275-
- Later, when anyone wants to add the corresponding translations for the country variant, just override any entry in the corresponding file: you can see an example with `es` variants.
276-
- If the generic language already exists:
277-
- If the translation doesn't differ from the generic language, then add the corresponding translations in the corresponding file
278-
- If the translation differs from the generic language, then add the corresponding translations in the corresponding file and remove it from the country variants entry
279-
4. If the language is `right-to-left`, add `dir` option with `rtl` value, for example, for [ar](./config/i18n.ts)
280-
5. If the language requires special pluralization rules, add `pluralRule` callback option, for example, for [ar](./config/i18n.ts)
279+
> [!IMPORTANT]
280+
> This file must be committed. Lunaria uses git history to track translation progress, so the build will fail if this file is missing.
281+
282+
5. If the language is `right-to-left`, add `dir: 'rtl'` (see `ar-EG` in config for example)
283+
6. If the language requires special pluralization rules, add a `pluralRule` callback (see `ar-EG` or `ru-RU` in config for examples)
281284

282285
Check [Pluralization rule callback](https://vue-i18n.intlify.dev/guide/essentials/pluralization.html#custom-pluralization) for more info.
283286

287+
#### Country variants (advanced)
288+
289+
Most languages only need a single locale file. Country variants are only needed when you want to support regional differences (e.g., `es-ES` for Spain vs `es-419` for Latin America).
290+
291+
If you need country variants:
292+
293+
1. Create a base language file (e.g., `es.json`) with all translations
294+
2. Create country variant files (e.g., `es-ES.json`, `es-419.json`) with only the differing translations
295+
3. Register the base language in `locales` and add variants to `countryLocaleVariants`
296+
297+
See how `es`, `es-ES`, and `es-419` are configured in [config/i18n.ts](./config/i18n.ts) for a complete example.
298+
284299
### Adding translations
285300

286301
1. Add your translation key to `i18n/locales/en.json` first (American English is the source of truth)

app/components/PackageDeprecatedTree.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const isExpanded = shallowRef(false)
1515
const showAll = shallowRef(false)
1616
1717
const hasDeprecated = computed(
18-
() => analysisData.value && analysisData.value.deprecatedPackages.length > 0,
18+
() => analysisData.value?.deprecatedPackages && analysisData.value.deprecatedPackages.length > 0,
1919
)
2020
2121
// Banner color - purple for deprecated

app/pages/settings.vue

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ const { locale, locales, setLocale } = useI18n()
55
const colorMode = useColorMode()
66
const { currentLocaleStatus, isSourceLocale } = useI18nStatus()
77
8-
const availableLocales = computed(() =>
9-
locales.value.map(l => (typeof l === 'string' ? { code: l, name: l } : l)),
10-
)
11-
128
// Escape to go back (but not when focused on form elements)
139
onKeyStroke('Escape', e => {
1410
const target = e.target as HTMLElement
@@ -211,7 +207,7 @@ defineOgImageComponent('Default', {
211207
class="w-full sm:w-auto min-w-48 bg-bg border border-border rounded-md px-3 py-2 text-sm text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 cursor-pointer"
212208
@change="setLocale(($event.target as HTMLSelectElement).value as typeof locale)"
213209
>
214-
<option v-for="loc in availableLocales" :key="loc.code" :value="loc.code">
210+
<option v-for="loc in locales" :key="loc.code" :value="loc.code" :lang="loc.code">
215211
{{ loc.name }}
216212
</option>
217213
</select>

config/i18n.ts

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -78,22 +78,27 @@ export const countryLocaleVariants: Record<string, (LocaleObjectData & { country
7878
],*/
7979
}
8080

81-
const locales: (Omit<LocaleObjectData, 'code'> & { code: string })[] = [
81+
const locales: (LocaleObjectData | (Omit<LocaleObjectData, 'code'> & { code: string }))[] = [
8282
{
8383
code: 'en',
8484
file: 'en.json',
8585
name: 'English',
8686
},
8787
{
88-
code: 'ar-EG',
88+
code: 'ar',
8989
file: 'ar.json',
9090
name: 'العربية',
9191
dir: 'rtl',
9292
pluralRule: (choice: number) => {
9393
const name = new Intl.PluralRules('ar-EG').select(choice)
9494
return { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }[name]
9595
},
96-
} satisfies LocaleObjectData,
96+
},
97+
{
98+
code: 'az-AZ',
99+
file: 'az.json',
100+
name: 'Azərbaycanca',
101+
},
97102
/*{
98103
code: 'ckb',
99104
file: 'ckb.json',
@@ -103,7 +108,7 @@ const locales: (Omit<LocaleObjectData, 'code'> & { code: string })[] = [
103108
const name = new Intl.PluralRules('ckb').select(choice)
104109
return { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }[name]
105110
},
106-
} satisfies LocaleObjectData,
111+
},
107112
{
108113
code: 'fa-IR',
109114
file: 'fa-IR.json',
@@ -113,7 +118,7 @@ const locales: (Omit<LocaleObjectData, 'code'> & { code: string })[] = [
113118
const name = new Intl.PluralRules('fa-IR').select(choice)
114119
return { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }[name]
115120
},
116-
} satisfies LocaleObjectData,
121+
},
117122
{
118123
code: 'ca',
119124
file: 'ca.json',
@@ -177,7 +182,18 @@ const locales: (Omit<LocaleObjectData, 'code'> & { code: string })[] = [
177182
const name = new Intl.PluralRules('ru-RU').select(choice)
178183
return { zero: 2, one: 0, two: 1, few: 1, many: 2, other: 3 }[name]
179184
},
180-
} satisfies LocaleObjectData,
185+
},
186+
{
187+
code: 'uk-UA',
188+
file: 'uk-UA.json',
189+
name: 'Українська',
190+
pluralRule: (choice: number) => {
191+
if (choice === 0) return 0
192+
193+
const name = new Intl.PluralRules('uk-UA').select(choice)
194+
return { zero: 0, one: 1, two: 0, few: 2, many: 3, other: 4 }[name]
195+
},
196+
},
181197
/*{
182198
code: 'ru-RU',
183199
file: 'ru-RU.json',
@@ -187,18 +203,6 @@ const locales: (Omit<LocaleObjectData, 'code'> & { code: string })[] = [
187203
return { zero: 2 /!* not used *!/, one: 0, two: 1 /!* not used *!/, few: 1, many: 2, other: 3 }[name]
188204
},
189205
},
190-
{
191-
code: 'uk-UA',
192-
file: 'uk-UA.json',
193-
name: 'Українська',
194-
pluralRule: (choice: number) => {
195-
if (choice === 0)
196-
return 0
197-
198-
const name = new Intl.PluralRules('uk-UA').select(choice)
199-
return { zero: 0, one: 1, two: 0 /!* not used *!/, few: 2, many: 3, other: 4 }[name]
200-
},
201-
},
202206
{
203207
code: 'cs-CZ',
204208
file: 'cs-CZ.json',
@@ -278,6 +282,8 @@ const locales: (Omit<LocaleObjectData, 'code'> & { code: string })[] = [
278282
},*/
279283
]
280284

285+
const lunariaJSONFiles: Record<string, string> = {}
286+
281287
function buildLocales() {
282288
const useLocales = Object.values(locales).reduce((acc, data) => {
283289
const locales = countryLocaleVariants[data.code]
@@ -289,10 +295,12 @@ function buildLocales() {
289295
name: l.name,
290296
files: [data.file as string, `${l.code}.json`],
291297
}
298+
lunariaJSONFiles[l.code] = l.country ? (data.file as string) : `${l.code}.json`
292299
delete entry.file
293300
acc.push(entry)
294301
})
295302
} else {
303+
lunariaJSONFiles[data.code] = data.file as string
296304
acc.push(data as LocaleObjectData)
297305
}
298306
return acc
@@ -303,6 +311,8 @@ function buildLocales() {
303311

304312
export const currentLocales = buildLocales()
305313

314+
export { lunariaJSONFiles }
315+
306316
export const datetimeFormats = Object.values(currentLocales).reduce((acc, data) => {
307317
const dateTimeFormats = data.dateTimeFormats
308318
if (dateTimeFormats) {

docs/content/index.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ seo:
44
description: A fast, modern browser for the npm registry
55
---
66

7-
## ::u-page-hero
7+
<!-- prettier-ignore-start -->
88

9+
::u-page-hero
10+
---
911
title: npmx.dev
1012
description: A fast, modern browser for the npm registry. Speed first, URL compatible, and simple.
1113
links:
12-
1314
- label: Get Started
1415
to: /getting-started/introduction
1516
color: neutral
@@ -22,11 +23,11 @@ links:
2223
size: xl
2324
icon: i-simple-icons-github
2425
variant: outline
25-
2626
---
27-
2827
::
2928

29+
<!-- prettier-ignore-end -->
30+
3031
::u-page-section{title="What you can do"}
3132
#features
3233
:::u-page-feature{icon="i-lucide-search" to="/guide/features" title="Search packages" description="Fast package search with instant results, infinite scroll, and keyboard navigation."}

0 commit comments

Comments
 (0)