You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wiki/backend/models/locales.ts

133 lines
4.4 KiB

import { stat, readFile } from 'node:fs/promises'
import path from 'node:path'
import { locales as localesTable } from '../db/schema.ts'
import { eq, sql } from 'drizzle-orm'
/**
* Locales model
*/
class Locales {
async refreshFromDisk({ force = false }: { force?: boolean } = {}): Promise<false | void> {
try {
const localesMeta = (await import('../locales/metadata.js')).default
WIKI.logger.info(`Found ${localesMeta.languages.length} locales [ OK ]`)
const dbLocales = await WIKI.db
.select({
code: localesTable.code,
updatedAt: localesTable.updatedAt
})
.from(localesTable)
.orderBy(localesTable.code)
let localFilesSkipped = 0
for (const lang of localesMeta.languages) {
// -> Build filename
const langFilenameParts = [lang.language]
if (lang.region) {
langFilenameParts.push(lang.region)
}
if (lang.script) {
langFilenameParts.push(lang.script)
}
const langFilename = langFilenameParts.join('-')
// -> Get DB version
const dbLang = dbLocales.find((l: any) => l.code === langFilename)
// -> Get File version
const flPath = path.join(WIKI.SERVERPATH, `locales/${langFilename}.json`)
try {
const flStat = await stat(flPath)
const flUpdatedAt = flStat.mtime.toTemporalInstant()
// -> Load strings
if (
!dbLang ||
Temporal.Instant.compare(dbLang.updatedAt.toTemporalInstant(), flUpdatedAt) < 0 ||
force
) {
WIKI.logger.info(`Loading locale ${langFilename} into DB...`)
const flStrings = JSON.parse(await readFile(flPath, 'utf8'))
await WIKI.db
.insert(localesTable)
.values({
code: langFilename,
name: lang.name,
nativeName: lang.localizedName,
language: lang.language,
region: lang.region,
script: lang.script,
isRTL: lang.isRtl,
strings: flStrings
})
.onConflictDoUpdate({
target: localesTable.code,
set: { strings: flStrings, updatedAt: sql`now()` }
})
WIKI.logger.info(`Locale ${langFilename} loaded successfully. [ OK ]`)
} else {
WIKI.logger.info(
`Locale ${langFilename} is newer in the DB. Skipping disk version. [ OK ]`
)
}
} catch {
localFilesSkipped++
WIKI.logger.warn(
`Locale ${langFilename} not found on disk. Missing strings file. [ SKIPPED ]`
)
}
}
if (localFilesSkipped > 0) {
WIKI.logger.warn(
`${localFilesSkipped} locales were defined in the metadata file but not found on disk. [ SKIPPED ]`
)
}
} catch (err: any) {
WIKI.logger.warn('Failed to load locales from disk: [ FAILED ]')
WIKI.logger.warn(err)
return false
}
}
async getLocales({ cache = true }: { cache?: boolean } = {}): Promise<any[]> {
if (!WIKI.cache.has('locales') || !cache) {
const locales = await WIKI.db
.select({
code: localesTable.code,
isRTL: localesTable.isRTL,
language: localesTable.language,
name: localesTable.name,
nativeName: localesTable.nativeName,
createdAt: localesTable.createdAt,
updatedAt: localesTable.updatedAt,
completeness: localesTable.completeness
})
.from(localesTable)
.orderBy(localesTable.code)
WIKI.cache.set('locales', locales)
for (const locale of locales) {
WIKI.cache.set(`locale:${locale.code}`, locale)
}
}
return WIKI.cache.get('locales') as any[]
}
async getStrings(locale: string) {
const results = await WIKI.db
.select({ strings: localesTable.strings })
.from(localesTable)
.where(eq(localesTable.code, locale))
.limit(1)
return results.length === 1 ? results[0].strings : []
}
async reloadCache(): Promise<void> {
WIKI.logger.info('Reloading locales cache...')
const locales = await WIKI.models.locales.getLocales({ cache: false })
WIKI.logger.info(`Loaded ${locales.length} locales into cache [ OK ]`)
}
}
export const locales = new Locales()