@hi18n/core
Version:
Message internationalization meets immutability and type-safety - core runtime
431 lines • 14.3 kB
TypeScript
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