export type MessageDict = Record<string, string>;
export type LocaleMessages = Record<string, MessageDict>;
export type TranslateParams = Record<string, string | number>;
export type LocaleChangeHandler = (locale: string) => void;
export interface I18nConfig {
    /** Initial locale. Overridden by a persisted choice when `persist` is on. */
    locale?: string;
    /** Locale used when a key is missing in the active locale. Defaults to 'en'. */
    fallbackLocale?: string;
    /** Locale → key → string. */
    messages?: LocaleMessages;
    /** Persist the active locale to localStorage. Defaults to false. */
    persist?: boolean;
    /** localStorage key used when `persist` is on. Defaults to 'ran-locale'. */
    storageKey?: string;
    /** Seed the initial locale from `navigator.language` when nothing else applies. */
    detectNavigator?: boolean;
}
export declare class I18nCore {
    private _locale;
    private _fallback;
    private _messages;
    private _persist;
    private _storageKey;
    private _handlers;
    constructor(config?: I18nConfig);
    private _resolveInitialLocale;
    /** The active locale. */
    get locale(): string;
    getLocale(): string;
    /** Switch locale; persists (when enabled) and notifies subscribers. No-op if unchanged. */
    setLocale(locale: string): void;
    /** Merge a dictionary into a locale (creating it if needed). */
    addMessages(locale: string, dict: MessageDict): void;
    getMessages(locale?: string): MessageDict;
    /** Locales that have a dictionary registered. */
    get availableLocales(): string[];
    /**
     * Translate a key against the active locale, falling back to the fallback
     * locale and finally the key itself. `{param}` placeholders are interpolated;
     * `{{` / `}}` are escapes for literal `{` / `}` (see {@link I18nCore._interpolate}).
     */
    t(key: string, params?: TranslateParams): string;
    /**
     * Substitute `{param}` placeholders and unescape literal braces.
     *
     * Grammar (a single left-to-right pass, so escapes and placeholders never
     * fight each other), matching the format-string convention used by Rust
     * `format!`, Python `str.format`, and .NET `String.Format`:
     * - `{{` → literal `{`
     * - `}}` → literal `}`
     * - `{name}` → `params.name` (stringified), or left untouched when the param
     *   is absent, so a stray placeholder is visible rather than silently blank.
     *
     * A lone `{`/`}` or a `{ spaced }` group is not a placeholder and is emitted
     * verbatim, so CSS/JSON/code fragments in a message pass through unharmed.
     * To wrap a value in literal braces, double the outer pair: `{{{name}}}`.
     */
    private _interpolate;
    /** Subscribe to locale changes. Returns an unsubscribe function. */
    onChange(handler: LocaleChangeHandler): () => void;
    private _notify;
    /** Remove all subscribers. */
    destroy(): void;
}
/** Create and register the global i18n singleton. */
export declare function createI18n(config?: I18nConfig): I18nCore;
/** Return the active global i18n instance, or null if none was created. */
export declare function useI18n(): I18nCore | null;
