import { Buffer } from 'node:buffer' import { flatten } from 'flat' import { createResolver } from '@nuxt/kit' import fs from 'fs-extra' import { countryLocaleVariants, currentLocales } from '../config/i18n' import type { LocaleEntry } from '../docs/types' import type { ElkTranslationStatus } from '~/types/translation-status' export const localeData: [code: string, 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) { for (const key in src) { if (typeof src[key] === 'object') { if (!dst[key]) dst[key] = {} merge(src[key], dst[key]) } else { dst[key] = src[key] } } } async function readI18nFile(file: string | string[]) { const resolver = createResolver(import.meta.url) if (Array.isArray(file)) { const files = await Promise.all(file.map(f => async () => { return JSON.parse(Buffer.from( await fs.readFile(resolver.resolve(`../locales/${f}`), 'utf-8'), ).toString()) })).then(f => f.map(f => f())) const data: Record = files[0] files.splice(0, 1) files.forEach(f => merge(f, data)) return data } else { return JSON.parse(Buffer.from( await fs.readFile(resolver.resolve(`../locales/${file}`), 'utf-8'), ).toString()) } } async function compare( baseEntries: Record, file: string | string[], data: LocaleEntry, ) { const baseEntriesKeys = Object.keys(baseEntries) const entries: Record = await readI18nFile(file) const flatEntriesKeys = Object.keys(flatten>(entries)) data.translated = flatEntriesKeys.filter(e => baseEntriesKeys.includes(e)) data.missing = baseEntriesKeys.filter(e => !flatEntriesKeys.includes(e)) data.outdated = flatEntriesKeys.filter(e => !baseEntriesKeys.includes(e)) data.total = flatEntriesKeys.length } async function prepareTranslationStatus() { const sourceLanguageLocale = localeData.find(l => l[0] === 'en-US')! const entries: Record = await readI18nFile(sourceLanguageLocale[1]) const flatEntries = flatten>(entries) const total = Object.keys(flatEntries).length const data: Record = { en: { translated: [], file: 'en.json', useFile: 'en.json', missing: [], outdated: [], title: 'English (source)', total, isSource: true, }, } 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('.'))] if (entry) { const countryFile = entry.find(e => e.code === code && e.country === true) if (countryFile) useFile = file[0] } data[code] = { title, useFile, file: Array.isArray(file) ? file[file.length - 1] : file, translated: [], missing: [], outdated: [], total: 0, } await compare(flatEntries, file, data[code]) })) const sorted: Record = { en: { ...data.en } } Object.keys(data).filter(k => k !== 'en').sort((a, b) => { return data[a].translated.length - data[b].translated.length }).forEach((k) => { sorted[k] = { ...data[k] } }) const resolver = createResolver(import.meta.url) await fs.writeFile( resolver.resolve('../docs/translation-status.json'), JSON.stringify(sorted, null, 2), { encoding: 'utf-8' }, ) const translationStatus: ElkTranslationStatus = { total, locales: { 'en-US': { total, percentage: '100', }, }, } Object.keys(data).filter(k => k !== 'en').forEach((e) => { const percentage = (total <= 0.0 || data[e].total === 0.0) ? '0' : data[e].total === total ? '100' : ((data[e].translated.length / total) * 100).toFixed(1) translationStatus.locales[e] = { total: data[e].total, percentage, } }) await fs.writeFile( resolver.resolve('../elk-translation-status.json'), JSON.stringify(translationStatus, null, 2), { encoding: 'utf-8' }, ) } prepareTranslationStatus()