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.
101 lines
3.4 KiB
101 lines
3.4 KiB
import {
|
|
LANG_ID_FALLBACK_SEPARATOR,
|
|
parseLangRef,
|
|
type LangParams,
|
|
type SupportedLocale
|
|
} from '$libs/langs';
|
|
import type { EngineLangs } from '$langs';
|
|
import { SIUM_DIAGNOSTIC_EVENTS } from './consts';
|
|
import { emitSiumDiagnostic, type SiumDiagnostics } from './diagnostics';
|
|
import { resolve as resolveFallbackTemplate } from './langs';
|
|
import type { Issue } from './core';
|
|
|
|
export interface SiumResolver {
|
|
readonly resolve: (message: string, params?: LangParams, locale?: SupportedLocale) => string;
|
|
readonly resolveIssue: (issue: Issue, locale?: SupportedLocale) => string;
|
|
readonly resolveIssues: (issues: ReadonlyArray<Issue>, locale?: SupportedLocale) => string[];
|
|
}
|
|
|
|
/**
|
|
* Duck-typed shape: when Sium is wired to `ActiveLangs` (which exposes a
|
|
* runtime `getLocale()`), the resolver follows the active locale lazily.
|
|
* `EngineLangs` does not expose this getter, so consumers wired to the
|
|
* pure engine still use the captured construction-time default.
|
|
*/
|
|
interface ActiveLocaleSource {
|
|
readonly getLocale: () => SupportedLocale;
|
|
}
|
|
|
|
interface SiumResolverOptions {
|
|
readonly langs?: EngineLangs;
|
|
/**
|
|
* Locale used when neither the caller passes one nor `langs.getLocale`
|
|
* exists. When `langs` is wired through `ActiveLangs`, the resolver
|
|
* reads `langs.getLocale()` on every call so a runtime
|
|
* `Lang.setLocale(...)` takes effect immediately.
|
|
*/
|
|
readonly defaultLocale: SupportedLocale;
|
|
readonly diagnostics: SiumDiagnostics;
|
|
}
|
|
|
|
function hasActiveLocaleSource(
|
|
langs: EngineLangs | undefined
|
|
): langs is EngineLangs & ActiveLocaleSource {
|
|
return (
|
|
langs !== undefined &&
|
|
typeof (langs as Partial<ActiveLocaleSource>).getLocale === 'function'
|
|
);
|
|
}
|
|
|
|
function composeLangPath(message: string): string | undefined {
|
|
const parsed = parseLangRef(message);
|
|
if (parsed === null) return undefined;
|
|
if (parsed.fallback === undefined) return parsed.path;
|
|
return `${parsed.path}${LANG_ID_FALLBACK_SEPARATOR}${parsed.fallback}`;
|
|
}
|
|
|
|
function fallbackMessage(message: string, params?: LangParams): string {
|
|
const parsed = parseLangRef(message);
|
|
return resolveFallbackTemplate(parsed?.fallback ?? parsed?.path ?? message, params);
|
|
}
|
|
|
|
export function createSiumResolver(options: SiumResolverOptions): SiumResolver {
|
|
const { langs, defaultLocale, diagnostics } = options;
|
|
|
|
function effectiveLocale(explicit: SupportedLocale | undefined): SupportedLocale {
|
|
if (explicit !== undefined) return explicit;
|
|
// Read langs' current locale lazily when available so a runtime
|
|
// `setLocale()` is honored. EngineLangs has no `getLocale()`; for
|
|
// that path we fall back to the construction-time default.
|
|
if (hasActiveLocaleSource(langs)) return langs.getLocale();
|
|
return defaultLocale;
|
|
}
|
|
|
|
const resolve: SiumResolver['resolve'] = (message, params, locale) => {
|
|
const langPath = composeLangPath(message);
|
|
if (langs === undefined || langPath === undefined) {
|
|
return fallbackMessage(message, params);
|
|
}
|
|
|
|
try {
|
|
return langs.t(langPath, params, effectiveLocale(locale));
|
|
} catch (error) {
|
|
emitSiumDiagnostic(diagnostics, SIUM_DIAGNOSTIC_EVENTS.RESOLVE_FALLBACK, {
|
|
message,
|
|
error
|
|
});
|
|
return fallbackMessage(message, params);
|
|
}
|
|
};
|
|
|
|
const resolveIssue: SiumResolver['resolveIssue'] = (entry, locale) => {
|
|
return resolve(entry.message, entry.params, locale);
|
|
};
|
|
|
|
const resolveIssues: SiumResolver['resolveIssues'] = (issues, locale) => {
|
|
return issues.map((entry) => resolveIssue(entry, locale));
|
|
};
|
|
|
|
return { resolve, resolveIssue, resolveIssues };
|
|
}
|