Skip to content

Commit

Permalink
fix: i18n locale config type safety (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
BobbieGoede authored Nov 25, 2024
1 parent 1202df4 commit 45437d5
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 21 deletions.
50 changes: 32 additions & 18 deletions config/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import type { DateTimeFormats, NumberFormats, PluralizationRule, PluralizationRules } from '@intlify/core-base'
import type { DateTimeFormats, Locale, NumberFormats, PluralizationRule, PluralizationRules } from '@intlify/core-base'
import type { LocaleObject } from '@nuxtjs/i18n'

interface LocaleObjectData extends LocaleObject {
interface LocaleObjectData<T> extends LocaleObject<T> {
numberFormats?: NumberFormats
dateTimeFormats?: DateTimeFormats
pluralRule?: PluralizationRule
}

export const countryLocaleVariants: Record<string, (LocaleObjectData & { country?: boolean }) []> = {
type CountryLocaleVariant = LocaleObjectData<Locale> & { country?: boolean }
type CountryLocaleVariantConfig = Record<string, CountryLocaleVariant[]>

export const countryLocaleVariants = {
ar: [
// ar.json contains ar-EG translations
// { code: 'ar-DZ', name: 'Arabic (Algeria)' },
Expand Down Expand Up @@ -71,17 +74,17 @@ export const countryLocaleVariants: Record<string, (LocaleObjectData & { country
{ country: true, code: 'pt-PT', name: 'Português (Portugal)' },
{ code: 'pt-BR', name: 'Português (Brasil)' },
],
}
} satisfies CountryLocaleVariantConfig

type LocaleVariantKeys = keyof typeof countryLocaleVariants

const locales: LocaleObjectData[] = [
const locales: (LocaleObjectData<LocaleVariantKeys> | LocaleObjectData<Locale>)[] = [
{
// @ts-expect-error en used as placeholder
code: 'en',
file: 'en.json',
name: 'English',
},
({
// @ts-expect-error ar used as placeholder
code: 'ar',
file: 'ar.json',
name: 'العربية',
Expand All @@ -90,7 +93,7 @@ const locales: LocaleObjectData[] = [
const name = new Intl.PluralRules('ar-EG').select(choice)
return { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }[name]
},
} satisfies LocaleObjectData),
}),
({
code: 'ckb',
file: 'ckb.json',
Expand All @@ -100,7 +103,7 @@ const locales: LocaleObjectData[] = [
const name = new Intl.PluralRules('ckb').select(choice)
return { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }[name]
},
} satisfies LocaleObjectData),
}),
({
code: 'fa-IR',
file: 'fa-IR.json',
Expand All @@ -110,9 +113,8 @@ const locales: LocaleObjectData[] = [
const name = new Intl.PluralRules('fa-IR').select(choice)
return { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }[name]
},
} satisfies LocaleObjectData),
}),
{
// @ts-expect-error ca used as placeholder
code: 'ca',
file: 'ca.json',
name: 'Català',
Expand Down Expand Up @@ -153,7 +155,6 @@ const locales: LocaleObjectData[] = [
name: 'Nederlands',
},
{
// @ts-expect-error es used as placeholder
code: 'es',
file: 'es.json',
name: 'Español',
Expand Down Expand Up @@ -207,7 +208,6 @@ const locales: LocaleObjectData[] = [
},
},
{
// @ts-expect-error pt used as placeholder
code: 'pt',
file: 'pt.json',
name: 'Português',
Expand Down Expand Up @@ -259,12 +259,25 @@ const locales: LocaleObjectData[] = [
},
]

export function isLocaleVariantKey(val: string): val is LocaleVariantKeys {
if (val in countryLocaleVariants === false)
return false

return true
}

function isLocaleVariant(val: LocaleObjectData<LocaleVariantKeys | Locale>): val is LocaleObjectData<LocaleVariantKeys> {
if (!isLocaleVariantKey(val.code))
return false

return true
}

function buildLocales() {
const useLocales = Object.values(locales).reduce((acc, data) => {
const locales = countryLocaleVariants[data.code]
if (locales) {
locales.forEach((l) => {
const entry: LocaleObjectData = {
if (isLocaleVariant(data)) {
countryLocaleVariants[data.code].forEach((l) => {
const entry: LocaleObject = {
...data,
code: l.code,
name: l.name,
Expand All @@ -277,8 +290,9 @@ function buildLocales() {
else {
acc.push(data)
}

return acc
}, <LocaleObjectData[]>[])
}, <LocaleObject[]>[])

return useLocales.sort((a, b) => a.code.localeCompare(b.code))
}
Expand Down
8 changes: 5 additions & 3 deletions scripts/prepare-translation-status.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { Locale } from '@intlify/core-base'
import type { LocaleEntry } from '../docs/types'
import { Buffer } from 'node:buffer'
import { readFile, writeFile } from 'node:fs/promises'
import { createResolver } from '@nuxt/kit'
import { flatten } from 'flat'
import type { NimbusTranslationStatus } from '~/types/translation-status'
import { countryLocaleVariants, currentLocales } from '../config/i18n'
import { countryLocaleVariants, currentLocales, isLocaleVariantKey } from '../config/i18n'

export const localeData: [code: string, file: string[], title: string][]
export const localeData: [code: Locale, file: string[], title: string][]
= currentLocales.map((l: any) => [l.code, l.files ? l.files : [l.file!], l.name ?? l.code])

function merge(src: Record<string, any>, dst: Record<string, any>) {
Expand Down Expand Up @@ -79,7 +80,8 @@ async function prepareTranslationStatus() {
await Promise.all(localeData.filter(l => l[0] !== 'en-US').map(async ([code, file, title]) => {
console.info(`Comparing ${code}...`, title)
let useFile = file[file.length - 1]
const entry = countryLocaleVariants[file[0].slice(0, file[0].indexOf('.'))]
const entryKey = file[0].slice(0, file[0].indexOf('.'))
const entry = isLocaleVariantKey(entryKey) ? countryLocaleVariants[entryKey] : undefined
if (entry) {
const countryFile = entry.find(e => e.code === code && e.country === true)
if (countryFile)
Expand Down

0 comments on commit 45437d5

Please sign in to comment.