sozsoft-platform/ui/src/services/localization.service.ts

122 lines
3.2 KiB
TypeScript
Raw Normal View History

2026-02-24 20:44:16 +00:00
// Type definitions
interface LocalizationResource {
texts: Record<string, string>
baseResources: string[]
}
interface LocalizationState {
resources: Record<string, LocalizationResource>
}
interface LocalizationKey {
key: string
defaultValue?: string
}
const getBaseResources = (state: LocalizationState, resourceNames: string[]): Record<string, string> => {
if (!resourceNames.length) {
return {}
}
// [{}, {}, {}]
const texts = resourceNames.map((resourceName: string) => {
const resource = state.resources[resourceName]
return {
...resource.texts,
...getBaseResources(state, resource.baseResources),
}
})
return Object.assign({}, ...texts)
}
export const setLocalization = (state: LocalizationState): Record<string, Record<string, string>> => {
const values: Record<string, Record<string, string>> = {}
Object.entries(state.resources).forEach(([key, value]) => {
values[key] = {
...value.texts,
...getBaseResources(state, value.baseResources),
}
})
return values
}
// 🔑 Interpolation helper
const interpolate = (text: string, params: Record<string, string | number>) => {
if (!params || typeof params !== 'object') return text
return Object.entries(params).reduce((acc, [key, value]) => {
2026-05-02 19:48:48 +00:00
if (/^\d+$/.test(key)) {
// Positional ABP-style: {0}, {1}, etc.
const pattern = new RegExp(`\\{${key}\\}`, 'g')
return acc.replace(pattern, String(value))
} else {
// Named: {{ key }} or {key}
let result = acc.replace(new RegExp(`{{\\s*${key}\\s*}}`, 'g'), String(value))
result = result.replace(new RegExp(`(?<!{){${key}}(?!})`, 'g'), String(value))
return result
2026-05-02 19:48:48 +00:00
}
2026-02-24 20:44:16 +00:00
}, text)
}
export const getLocalization = (
texts: Record<string, Record<string, string>>,
defaultResourceName: string | undefined,
key: string | LocalizationKey,
params?: Record<string, string | number>
): string => {
let keyString = ''
let defaultValue = ''
if (typeof key === 'string') {
keyString = key || ''
} else {
defaultValue = key.defaultValue || ''
keyString = key.key
}
const keys = keyString.split('::')
const warn = (message: string) => {
if (import.meta.env.DEV) console.warn(message)
}
if (keys.length < 2) {
warn('The localization source separator (::) not found.')
return defaultValue || keyString
}
if (!texts) return defaultValue || keys[1]
const sourceName = keys[0] || defaultResourceName
const sourceKey = keys[1]
if (sourceName === '_') {
return defaultValue || sourceKey
}
if (!sourceName) {
warn('Localization source name is not specified and the defaultResourceName was not defined!')
return defaultValue || sourceKey
}
const source = texts[sourceName]
if (!source) {
warn('Could not find localization source: ' + sourceName)
return defaultValue || sourceKey
}
let localization = source[sourceKey]
if (typeof localization === 'undefined') {
return defaultValue || sourceKey
}
if (typeof localization !== 'string') localization = ''
// ✅ Interpolation using the helper function
if (localization && params && typeof params === 'object') {
localization = interpolate(localization, params)
}
return localization || defaultValue || keyString
}