Skip to content

Commit 3f2dd7a

Browse files
committed
Add native Lunaria key merging
1 parent c52d259 commit 3f2dd7a

34 files changed

Lines changed: 132 additions & 26711 deletions

CONTRIBUTING.md

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -483,17 +483,8 @@ To add a new locale:
483483
},
484484
```
485485

486-
4. Copy your translation file to `lunaria/files/` for translation tracking:
487-
488-
```bash
489-
cp i18n/locales/uk-UA.json lunaria/files/uk-UA.json
490-
```
491-
492-
> **Important:**
493-
> This file must be committed. Lunaria uses git history to track translation progress, so the build will fail if this file is missing.
494-
495-
5. If the language is `right-to-left`, add `dir: 'rtl'` (see `ar-EG` in config for example)
496-
6. If the language requires special pluralization rules, add a `pluralRule` callback (see `ar-EG` or `ru-RU` in config for examples)
486+
4. If the language is `right-to-left`, add `dir: 'rtl'` (see `ar-EG` in config for example)
487+
5. If the language requires special pluralization rules, add a `pluralRule` callback (see `ar-EG` or `ru-RU` in config for examples)
497488

498489
Check [Pluralization rule callback](https://vue-i18n.intlify.dev/guide/essentials/pluralization#custom-pluralization) and [Plural Rules](https://cldr.unicode.org/index/cldr-spec/plural-rules#TOC-Determining-Plural-Categories) for more info.
499490

config/i18n.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,6 @@ const locales: (LocaleObjectData | (Omit<LocaleObjectData, 'code'> & { code: str
350350
},
351351
]
352352

353-
const lunariaJSONFiles: Record<string, string> = {}
354-
355353
function buildLocales() {
356354
const useLocales = Object.values(locales).reduce((acc, data) => {
357355
const locales = countryLocaleVariants[data.code]
@@ -363,12 +361,10 @@ function buildLocales() {
363361
name: l.name,
364362
files: [data.file as string, `${l.code}.json`],
365363
}
366-
lunariaJSONFiles[l.code] = l.country ? (data.file as string) : `${l.code}.json`
367364
delete entry.file
368365
acc.push(entry)
369366
})
370367
} else {
371-
lunariaJSONFiles[data.code] = data.file as string
372368
acc.push(data as LocaleObjectData)
373369
}
374370
return acc
@@ -379,8 +375,6 @@ function buildLocales() {
379375

380376
export const currentLocales = buildLocales()
381377

382-
export { lunariaJSONFiles }
383-
384378
export const datetimeFormats = Object.values(currentLocales).reduce((acc, data) => {
385379
const dateTimeFormats = data.dateTimeFormats
386380
if (dateTimeFormats) {

lunaria.config.ts

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,78 @@
11
import { defineConfig } from '@lunariajs/core/config'
2-
import { locales, sourceLocale } from './lunaria/prepare-json-files.ts'
2+
import type { Locale, Merge } from '@lunariajs/core'
3+
import { currentLocales, countryLocaleVariants } from './config/i18n.ts'
4+
5+
// The source locale is `en` (en.json contains all reference translation keys).
6+
// Country variants like `en-US` inherit from `en` via the merge config.
7+
const sourceLocale: Locale = { label: 'English', lang: 'en' }
8+
9+
// Build the list of Lunaria locales from currentLocales.
10+
// currentLocales has expanded codes (en-US, en-GB, ar-EG, es-ES, es-419, etc.)
11+
// but NOT the base codes (ar, es) that the variants inherit from.
12+
// We need to add those base codes as Lunaria locales too, so they can be
13+
// referenced in the merge config and tracked independently.
14+
const localeSet = new Set<string>()
15+
const locales: Locale[] = []
16+
17+
for (const l of currentLocales) {
18+
if (l.code === sourceLocale.lang || !l.name) continue
19+
if (!localeSet.has(l.code)) {
20+
localeSet.add(l.code)
21+
locales.push({ label: l.name, lang: l.code })
22+
}
23+
}
24+
25+
// Add base language codes (ar, es, etc.) that aren't already in the list.
26+
// These are the keys of countryLocaleVariants that aren't the source locale.
27+
for (const baseLang of Object.keys(countryLocaleVariants)) {
28+
if (baseLang === sourceLocale.lang) continue
29+
if (!localeSet.has(baseLang)) {
30+
// Use the first variant's name or the base code as label
31+
const variants = countryLocaleVariants[baseLang]!
32+
const label = variants[0]?.name ?? baseLang
33+
localeSet.add(baseLang)
34+
locales.push({ label, lang: baseLang })
35+
}
36+
}
37+
38+
if (locales.length === 0) {
39+
throw new Error('No locales found besides source locale')
40+
}
41+
42+
// Build merge config from countryLocaleVariants:
43+
// Each variant locale merges keys from its base locale, so keys present in
44+
// the base file count as covered for the variant.
45+
// e.g. { 'en-US': ['en'], 'en-GB': ['en'], 'ar-EG': ['ar'], 'es-ES': ['es'], 'es-419': ['es'] }
46+
const merge: Merge = {}
47+
for (const [baseLang, variants] of Object.entries(countryLocaleVariants)) {
48+
for (const variant of variants) {
49+
// Each variant merges from its base language and (if not the source) implicitly
50+
// from the source via normal Lunaria tracking.
51+
const existing = merge[variant.code]
52+
if (existing) {
53+
existing.push(baseLang)
54+
} else {
55+
merge[variant.code] = [baseLang]
56+
}
57+
}
58+
}
359

460
export default defineConfig({
561
repository: {
662
name: 'npmx-dev/npmx.dev',
763
},
864
sourceLocale,
9-
locales,
65+
locales: locales as [Locale, ...Locale[]],
1066
files: [
1167
{
12-
include: ['lunaria/files/en-US.json'],
13-
pattern: 'lunaria/files/@lang.json',
68+
include: ['i18n/locales/en.json'],
69+
pattern: 'i18n/locales/@lang.json',
1470
type: 'dictionary',
71+
merge,
72+
optionalKeys: {
73+
$schema: true,
74+
vacations: true,
75+
},
1576
},
1677
],
1778
tracking: {

0 commit comments

Comments
 (0)