From 45437d594af3a0fd3ec70e8c78902d3e467b87a9 Mon Sep 17 00:00:00 2001 From: Bobbie Goede Date: Mon, 25 Nov 2024 08:44:28 +0100 Subject: [PATCH] fix: i18n locale config type safety (#22) --- config/i18n.ts | 50 +++++++++++++++++---------- scripts/prepare-translation-status.ts | 8 +++-- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/config/i18n.ts b/config/i18n.ts index e51da89e5..d132f17c3 100644 --- a/config/i18n.ts +++ b/config/i18n.ts @@ -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 extends LocaleObject { numberFormats?: NumberFormats dateTimeFormats?: DateTimeFormats pluralRule?: PluralizationRule } -export const countryLocaleVariants: Record = { +type CountryLocaleVariant = LocaleObjectData & { country?: boolean } +type CountryLocaleVariantConfig = Record + +export const countryLocaleVariants = { ar: [ // ar.json contains ar-EG translations // { code: 'ar-DZ', name: 'Arabic (Algeria)' }, @@ -71,17 +74,17 @@ export const countryLocaleVariants: Record | LocaleObjectData)[] = [ { - // @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: 'العربية', @@ -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', @@ -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', @@ -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à', @@ -153,7 +155,6 @@ const locales: LocaleObjectData[] = [ name: 'Nederlands', }, { - // @ts-expect-error es used as placeholder code: 'es', file: 'es.json', name: 'Español', @@ -207,7 +208,6 @@ const locales: LocaleObjectData[] = [ }, }, { - // @ts-expect-error pt used as placeholder code: 'pt', file: 'pt.json', name: 'Português', @@ -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): val is LocaleObjectData { + 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, @@ -277,8 +290,9 @@ function buildLocales() { else { acc.push(data) } + return acc - }, []) + }, []) return useLocales.sort((a, b) => a.code.localeCompare(b.code)) } diff --git a/scripts/prepare-translation-status.ts b/scripts/prepare-translation-status.ts index 2adda438e..e75fd52fd 100644 --- a/scripts/prepare-translation-status.ts +++ b/scripts/prepare-translation-status.ts @@ -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, dst: Record) { @@ -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)