129 lines
3.8 KiB
TypeScript
129 lines
3.8 KiB
TypeScript
import type { I18nString } from '@pikku/react'
|
|
import arMessages from '../messages/ar.json'
|
|
import bsMessages from '../messages/bs.json'
|
|
import deMessages from '../messages/de.json'
|
|
import enMessages from '../messages/en.json'
|
|
import esMessages from '../messages/es.json'
|
|
import filMessages from '../messages/fil.json'
|
|
import hiMessages from '../messages/hi.json'
|
|
import sqMessages from '../messages/sq.json'
|
|
import trMessages from '../messages/tr.json'
|
|
import ukMessages from '../messages/uk.json'
|
|
import viMessages from '../messages/vi.json'
|
|
import { defaultLocale, getLocale, maskI18n, type Locale } from './config'
|
|
import { identOf } from './ident'
|
|
|
|
type MessageCatalog = Record<string, string>
|
|
type MessageArgs = Record<string, unknown>
|
|
type MessageFn = (args?: MessageArgs) => I18nString
|
|
|
|
const catalogs: Record<Locale, MessageCatalog> = {
|
|
ar: arMessages as MessageCatalog,
|
|
bs: bsMessages as MessageCatalog,
|
|
de: deMessages as MessageCatalog,
|
|
en: enMessages as MessageCatalog,
|
|
es: esMessages as MessageCatalog,
|
|
fil: filMessages as MessageCatalog,
|
|
hi: hiMessages as MessageCatalog,
|
|
sq: sqMessages as MessageCatalog,
|
|
tr: trMessages as MessageCatalog,
|
|
uk: ukMessages as MessageCatalog,
|
|
vi: viMessages as MessageCatalog,
|
|
}
|
|
|
|
const interpolate = (template: string, args?: MessageArgs) =>
|
|
template.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_match, key: string) => {
|
|
const value = args?.[key]
|
|
return value === undefined || value === null ? '' : String(value)
|
|
})
|
|
|
|
const getMessageValue = (key: string, locale = getLocale()) =>
|
|
catalogs[locale]?.[key] ?? catalogs[defaultLocale]?.[key]
|
|
|
|
const toI18n = (value: string) => maskI18n(value) as unknown as I18nString
|
|
|
|
const parseListValue = (value: string): string[] => {
|
|
try {
|
|
const parsed = JSON.parse(value) as unknown
|
|
if (Array.isArray(parsed)) {
|
|
return parsed.map((item) => String(item))
|
|
}
|
|
} catch {
|
|
// Fall through to a permissive parser for malformed copied JSON.
|
|
}
|
|
|
|
return value
|
|
.replace(/^\s*\[/, '')
|
|
.replace(/\]\s*$/, '')
|
|
.split(/","|"،\s*"|»,\s*«|",\s*"/)
|
|
.map((item) => item.replace(/^["«\s]+|["»\s]+$/g, '').trim())
|
|
.filter(Boolean)
|
|
}
|
|
|
|
const getIndexedList = (keyPrefix: string): string[] => {
|
|
const values: string[] = []
|
|
for (let index = 0; ; index += 1) {
|
|
const value = getMessageValue(`${keyPrefix}_${index}`)
|
|
if (!value) {
|
|
break
|
|
}
|
|
values.push(value)
|
|
}
|
|
return values
|
|
}
|
|
|
|
export const mKey = (key: string, args?: MessageArgs): I18nString => {
|
|
const value = getMessageValue(identOf(key))
|
|
if (!value) {
|
|
if (typeof console !== 'undefined') {
|
|
console.warn(`[i18n] missing message for key "${key}"`)
|
|
}
|
|
return toI18n(key)
|
|
}
|
|
|
|
return toI18n(interpolate(value, args))
|
|
}
|
|
|
|
// Plain-string resolution in an explicit locale (no active-locale store).
|
|
// For server-side, locale-parameterized text like SEO meta tags.
|
|
export const mTextIn = (
|
|
locale: Locale,
|
|
key: string,
|
|
args?: MessageArgs,
|
|
): string => {
|
|
const value = getMessageValue(identOf(key), locale)
|
|
return value ? interpolate(value, args) : key
|
|
}
|
|
|
|
export const mList = (keyPrefix: string, args?: MessageArgs): I18nString[] => {
|
|
const ident = identOf(keyPrefix)
|
|
const indexedValues = getIndexedList(ident)
|
|
const values =
|
|
indexedValues.length > 0
|
|
? indexedValues
|
|
: (() => {
|
|
const exactValue = getMessageValue(ident)
|
|
return exactValue ? parseListValue(exactValue) : []
|
|
})()
|
|
|
|
return values.map((value) => toI18n(interpolate(value, args)))
|
|
}
|
|
|
|
export const m = new Proxy(
|
|
{},
|
|
{
|
|
get: (_target, property) => {
|
|
if (typeof property !== 'string') {
|
|
return undefined
|
|
}
|
|
|
|
const fn: MessageFn = (args) => {
|
|
const value = getMessageValue(property)
|
|
return toI18n(interpolate(value ?? property, args))
|
|
}
|
|
|
|
return fn
|
|
},
|
|
},
|
|
) as Record<string, MessageFn>
|