UNPKG

@hi18n/core

Version:

Message internationalization meets immutability and type-safety - core runtime

431 lines 14.3 kB
import { CompiledMessage } from "./msgfmt.js"; import type { ComponentPlaceholder, InferredMessageType } from "./msgfmt-parser-types.js"; import { ErrorHandler, ErrorLevel } from "./error-handling.js"; export type { ComponentPlaceholder } from "./msgfmt-parser-types.js"; export * from "./errors.js"; export * from "./error-handling.js"; declare const messageBrandSymbol: unique symbol; declare const translationIdBrandSymbol: unique symbol; /** * A subtype of `string` that represents translation messages. * * @param Args parameters required by this message * * @since 0.1.0 (`@hi18n/core`) */ export declare type Message<Args = {}> = string & { [messageBrandSymbol]: (args: Args) => void; }; /** * A base type for a vocabulary. * * A vocabulary here means a set of translation ids required for this book of translations. * * @since 0.1.0 (`@hi18n/core`) */ export declare type VocabularyBase = Record<string, Message<any>>; /** * Extracts parameters required by the translated message. * * @param M the message being instantiated. * @param C replacement for the component interpolation (like `<0></0>` or `<link></link>`). * * @since 0.1.0 (`@hi18n/core`) */ export declare type MessageArguments<M extends Message<any>, C> = InstantiateComponentTypes<InjectAdditionalParams<AbstractMessageArguments<M>>, C>; export declare type InjectAdditionalParams<Args> = true extends HasDate<Args[keyof Args]> ? Args & { timeZone: string; } : Args; export declare type HasDate<T> = T extends Date ? true : never; export declare type AbstractMessageArguments<M extends Message<any>> = M extends Message<infer Args> ? Args : never; export declare type InstantiateComponentTypes<Args, C> = { [K in keyof Args]: InstantiateComponentType<Args[K], C>; }; export declare type InstantiateComponentType<T, C> = T extends ComponentPlaceholder ? C : T; /** * A subtype of `string` that represents a dynamically-managed translation id. * * @param Vocabulary the vocabulary type of the Book it refers to * @param Args parameters required by this message * * @since 0.1.1 (`@hi18n/core`) */ export declare type TranslationId<Vocabulary extends VocabularyBase, Args = {}> = string & { [translationIdBrandSymbol]: (catalog: Vocabulary, args: Args) => void; }; /** * Extracts the translation ids that don't take parameters. * * @param Vocabulary the vocabulary, a set of translation ids we can use for this book of translations. * @param K a dummy parameter to do a union distribution * * @since 0.1.0 (`@hi18n/core`) */ export declare type SimpleMessageKeys<Vocabulary extends VocabularyBase, K extends string & keyof Vocabulary = string & keyof Vocabulary> = K extends unknown ? {} extends MessageArguments<Vocabulary[K], never> ? K : never : never; /** * Infers the appropriate type for the translated message. * * At runtime, it just returns the first argument. * * @param s the translated message * @returns the first argument * * @since 0.1.0 (`@hi18n/core`) * * @example * ```ts * export default new Book<Vocabulary>({ * "example/greeting": msg("Hello, {name}!"), * }); * ``` */ export declare function msg<S extends string>(s: S): InferredMessageType<S>; export declare namespace msg { var todo: <S extends string>(s: S) => InferredMessageType<S>; } /** * Marks a translation id as dynamically used with {@link CompoundTranslatorFunction.dynamic t.dynamic}. * * At runtime, it just returns the second argument. * * @param book the book the id is linked to. Just discarded at runtime. * @param id the translation id. * @returns the second argument * * @since 0.1.1 (`@hi18n/core`) * * @example * ```ts * const menus = [ * { * url: "https://example.com/home", * titleId: translationId(book, "example/navigation/home"), * }, * { * url: "https://example.com/map", * titleId: translationId(book, "example/navigation/map"), * }, * ]; * * const { t } = getTranslator(book, "en"); * t.dynamic(menus[i].titleId); * ``` */ export declare function translationId<Vocabulary extends VocabularyBase, K extends string & keyof Vocabulary>(book: Book<Vocabulary>, id: K): TranslationId<Vocabulary, AbstractMessageArguments<Vocabulary[K]>>; /** * A set of translated messages, containing translations for all supported locales. * * In other words, a book is a set of {@link Catalog}s for all languages. * * @since 0.1.0 (`@hi18n/core`) * * @example * ```ts * type Vocabulary = { * "example/greeting": Message<{ name: string }>; * }; * export const book = new Book<Vocabulary>({ * en: catalogEn, * ja: catalogJa, * }); * ``` * * @example You can use `import()` to lazy-load catalogs. * Note that you need to use extra setup to avoid the * "Catalog not loaded" error. * * ```ts * type Vocabulary = { * "example/greeting": Message<{ name: string }>; * }; * export const book = new Book<Vocabulary>({ * en: () => import("./en"), * ja: () => import("./ja"), * }); * ``` */ export declare class Book<Vocabulary extends VocabularyBase> { readonly catalogs: Record<string, Catalog<Vocabulary>>; readonly _loaders: Readonly<Record<string, Catalog<Vocabulary> | CatalogLoader<Vocabulary>>>; _handleError?: ErrorHandler | undefined; _implicitLocale?: string | undefined; constructor(catalogs: Readonly<Record<string, Catalog<Vocabulary> | CatalogLoader<Vocabulary>>>, options?: BookOptions); /** * Load a catalog for specific locale. * * Consider using {@link preloadCatalogs} instead. * * @param locale locale to load * * @since 0.1.9 (`@hi18n/core`) */ loadCatalog(locale: string): Promise<void>; handleError(e: Error, level: ErrorLevel): void; } /** * @since 0.1.7 (`@hi18n/core`) */ export declare type BookOptions = { /** * Custom error handler. {@link defaultErrorHandler} is used by default. * * @example * ```ts * export const book = new Book({ * en: catalogEn, * ja: catalogJa, * }, { * handleError(error, level) { * if (level === "error") { * // Report to Sentry or somewhere * } else { * console.warn(error); * } * } * }); * ``` * * @since 0.1.7 (`@hi18n/core`) */ handleError?: ErrorHandler | undefined; /** * Locale fallback to use when no valid locale is specified. * * @example * ```ts * export const book = new Book({ * en: catalogEn, * ja: catalogJa, * }, { implicitLocale: "en" }); * ``` * * @since 0.1.7 (`@hi18n/core`) */ implicitLocale?: string | undefined; }; /** * A function to asynchronously load a catalog. * * It is usually provided as `() => import("...")`. * * @since 0.1.9 (`@hi18n/core`) */ export declare type CatalogLoader<Vocabulary extends VocabularyBase> = () => Promise<{ default: Catalog<Vocabulary>; }>; /** * A set of translated messages for a specific locale. * * @since 0.1.0 (`@hi18n/core`) * * @example * ```ts * type Vocabulary = { * "example/greeting": Message<{ name: string }>; * }; * export default new Catalog<Vocabulary>("en", { * "example/greeting": msg("Hello, {name}!"), * }); * ``` */ export declare class Catalog<Vocabulary extends VocabularyBase> { locale?: string | undefined; readonly data: Readonly<Vocabulary>; private _looseLocale; private _compiled; /** * @since 0.1.6 (`@hi18n/core`) */ constructor(locale: string, data: Readonly<Vocabulary>); /** * @deprecated deprecated from 0.1.6. Please specify the locale. * @since 0.1.0 (`@hi18n/core`) */ constructor(data: Readonly<Vocabulary>); getCompiledMessage(id: string & keyof Vocabulary): CompiledMessage; } /** * An object returned from {@link getTranslator}. * * @since 0.1.0 (`@hi18n/core`) */ export declare type TranslatorObject<Vocabulary extends VocabularyBase> = { /** * Returns the translated message. * * @since 0.1.0 (`@hi18n/core`) * * @example * ```ts * const { t } = getTranslator(book, "en"); * t("example/greeting-simple"); // => "Hello!" * ``` */ t: CompoundTranslatorFunction<Vocabulary>; /** * Similar to {@link TranslatorObject.t} but allows component interpolation * (i.e. to interpret commands like `<0>foo</0>` or `<link>foo</link>`) * * Users usually don't need to call it manually. * See the `@hi18n/react` package for its application to React. * * @param id the id of the translation * @param interpolator functions to customize the interpolation behavior * @param options the parameters of the translation. * * @since 0.1.0 (`@hi18n/core`) */ translateWithComponents<T, C, K extends string & keyof Vocabulary>(id: K, interpolator: ComponentInterpolator<T, C>, options: MessageArguments<Vocabulary[K], C>): T | string; }; declare type CompoundTranslatorFunction<Vocabulary extends VocabularyBase> = TranslatorFunction<Vocabulary> & { /** * Returns the translated message for a dynamic id. * * @since 0.1.1 (`@hi18n/core`) * * @example * ```ts * const { t } = getTranslator(book, "en"); * t.dynamic(menus[i].titleId); // => "Map" * ``` */ dynamic: DynamicTranslatorFunction<Vocabulary>; /** * Declares a translation to be made. * * At runtime, it returns the first argument. * * @param id the id of the translation * @param options the parameters of the translation. * * @since 0.1.1 (`@hi18n/core`) * * @example * ```ts * const { t } = getTranslator(book, "en"); * t.todo("example/greeting-simple"); // => "[TODO: example/greeting-simple]" * ``` */ todo(id: string, options?: Record<string, unknown>): string; }; declare type TranslatorFunction<Vocabulary extends VocabularyBase> = { /** * Returns the translated message for a simple one. * * @param id the id of the translation * * @since 0.1.0 (`@hi18n/core`) * * @example * ```ts * const { t } = getTranslator(book, "en"); * t("example/greeting-simple"); // => "Hello!" * ``` */ (id: SimpleMessageKeys<Vocabulary>): string; /** * Returns the translated message. * * @param id the id of the translation * @param options the parameters of the translation. * * @since 0.1.0 (`@hi18n/core`) * * @example * ```ts * const { t } = getTranslator(book, "en"); * t("example/greeting", { name: "John" }); // => "Hello, John!" * ``` */ <K extends string & keyof Vocabulary>(id: K, options: MessageArguments<Vocabulary[K], never>): string; }; declare type DynamicTranslatorFunction<Vocabulary extends VocabularyBase> = { /** * Returns the translated message for a simple dynamic id. * * @param id the id of the translation * * @since 0.1.1 (`@hi18n/core`) * * @example * ```ts * const { t } = getTranslator(book, "en"); * t.dynamic(menus[i].titleId); // => "Map" * ``` */ (id: TranslationId<Vocabulary, {}>): string; /** * Returns the translated message for a dynamic id. * * @param id the id of the translation * @param options the parameters of the translation. * * @since 0.1.1 (`@hi18n/core`) * * @example * ```ts * const { t } = getTranslator(book, "en"); * t.dynamic(greetings[i].translationId, { name: "John" }); // => "Hello, John!" * ``` */ <Args>(id: TranslationId<Vocabulary, Args>, options: InstantiateComponentTypes<InjectAdditionalParams<Args>, never>): string; }; /** * Used in {@link TranslatorObject.translateWithComponents} to customize * the behavior of component interpolation. * * @since 0.1.0 (`@hi18n/core`) */ export declare type ComponentInterpolator<T, C> = { collect: (submessages: (T | string)[]) => T | string; wrap: (component: C, message: T | string | undefined) => T | string; }; /** * Retrieves the translation helpers from the book and the locales. * * @param book the "book" (i.e. the set of translations) containing the desired messages. * @param locales a locale or a list of locale in the order of preference (the latter being not supported yet) * @param options.throwPromise if true, it throws a Promise instance instead of an error. Used for React Suspense integration. * @returns A set of translation helpers * * @since 0.1.0 (`@hi18n/core`) * * @example * ```ts * const { t } = getTranslator(book, "en"); * t("example/greeting-simple"); // => "Hello!" * ``` */ export declare function getTranslator<Vocabulary extends VocabularyBase>(book: Book<Vocabulary>, locales: string | string[], options?: GetTranslatorOptions): TranslatorObject<Vocabulary>; /** * options for {@link getTranslator} * * @since 0.1.9 (`@hi18n/core`) */ export declare type GetTranslatorOptions = { /** if true, it throws a Promise instance instead of an error. Used for React Suspense integration. */ throwPromise?: boolean | undefined; }; /** * Starts loading and waits for catalogs so that {@link getTranslator} does not error * with "Catalog not loaded" error. * * It is a wrapper for {@link Book.loadCatalog}. * * @param book same as {@link getTranslator}'s `book` parameter. * @param locales same as {@link getTranslator}'s `locales` parameter. * * @since 0.1.9 (`@hi18n/core`) */ export declare function preloadCatalogs<Vocabulary extends VocabularyBase>(book: Book<Vocabulary>, locales: string | string[]): Promise<void>; /** * A convenience helper to get the default time zone. * If you need more sophisticated guess for old browsers, * consider using other libraries like `moment.tz.guess`. * * @returns the default time zone, if anything is found. Otherwise the string "UTC" * * @since 0.1.3 (`@hi18n/core`) */ export declare function getDefaultTimeZone(): string; //# sourceMappingURL=index.d.ts.map