UNPKG

@resk/core

Version:

An innovative TypeScript framework that empowers developers to build applications with a fully decorator-based architecture for efficient resource management. By combining the power of decorators with a resource-oriented design, DecorRes enhances code cla

587 lines (584 loc) 25.2 kB
import { IObservable, IObservableCallback } from "../observable"; import { Dict, I18n as I18nJs, I18nOptions, Scope, TranslateOptions } from "i18n-js"; import { LocaleSpecification } from "moment"; import "reflect-metadata"; import { I18nEvent, II18nTranslation } from "../types/i18n"; import { IDict } from "../types/index"; /** * A decorator to attach metadata to properties or methods for translation. * @param key The translation key in the translations. * @returns A property and method decorator. * @example * ```ts * // Class with translations using the decorator class MyComponent { @Translate("greeting") public greeting: string; @Translate("nested.example") public nestedExample: string; @Translate("farewell") public sayGoodbye(): string { return ""; } } * ``` */ export declare function Translate(key: string): PropertyDecorator & MethodDecorator; /** * The I18n class extends the i18n-js library to provide internationalization (i18n) * functionality with observable capabilities. It manages translations, allows for * dynamic loading of language dictionaries, and supports event-driven architecture * through observable patterns. * * @extends I18nJs * @implements IObservable<I18nEvent> * * @example * // Example usage of the I18n class * const i18nInstance = I18n.getInstance(); * i18nInstance.registerTranslations({ * en: { * greeting: "Hello, {name}!", * farewell: "Goodbye!", * }, * }); * console.log(i18nInstance.translate("greeting", { name: "John" })); // Outputs: Hello, John! * @see https://www.npmjs.com/package/i18n-js?activeTab=readme for more information on i18n-js library. */ export declare class I18n extends I18nJs implements IObservable<I18nEvent> { /** * Custom instanceof check. When consumers import `I18n` from built packages or * across module boundaries, class identity can differ. Using Symbol.hasInstance * allows `instanceof I18n` to succeed if the object has the required i18n API * shape (duck typing). This preserves `instanceof` checks externally while * keeping the current exported API intact. */ static [Symbol.hasInstance](obj: any): obj is I18n; /** * Type guard to check if an object is an instance of I18n. * Uses duck typing to verify the object has the required i18n methods, * allowing for cross-realm compatibility when instanceof checks fail. * @param obj The object to check. * @returns True if the object is an I18n instance, false otherwise. */ static isI18nInstance(obj: any): obj is I18n; /** * Translates the given scope with the provided options. * If the scope is a string and the options include pluralization, the method will pluralize the translation. * Otherwise, it will call the parent `translate` method. * @param scope The translation scope. * @param options The translation options, including pluralization. * @returns The translated string or the type specified in the generic parameter. * @example * // Register translations for the "en" locale. * i18n.registerTranslations({ * en: { * greeting: { * one: "Hello, %{name}!", * other: "Hello, %{name}s!", * zero: "Hello, %{name}s!" * }, * farewell: "Goodbye!" * } * }); * * // Translate the "greeting" scope with pluralization. * i18n.translate("greeting", { count: 1 }); // "Hello, John!" * i18n.translate("greeting", { count: 2 }); // "Hello, Johns!" * i18n.translate("greeting", { count: 0 }); // "Hello, Johns!" * * // Translate the "farewell" scope. * i18n.translate("farewell"); // "Goodbye!" */ translate<T = string>(scope: Scope, options?: TranslateOptions): string | T; /*** * Translates the keys of the given target class. * @param target The target class. * @param options The translation options. * @returns The translated keys. */ translateTarget<T extends { new (...args: any[]): {}; } = any>(target: T, options?: TranslateOptions): Record<keyof T, string>; /** * Translates an object containing translation keys as values. * This method takes an object where each property value is expected to be a translation key, * and returns a new object with the same structure but with translated values. * * @template T - The type of the input object, extending Record<string, string> * @param object - The object containing translation keys as values to be translated * @param {TranslateOptions} options - additional options to pass to the i18n.translate function * @returns A new object with the same keys but translated values * * @example * ```typescript * // Register translations first * i18n.registerTranslations({ * en: { * 'user.name': 'Name', * 'user.email': 'Email Address', * 'user.phone': 'Phone Number', * 'actions.save': 'Save', * 'actions.cancel': 'Cancel' * }, * fr: { * 'user.name': 'Nom', * 'user.email': 'Adresse Email', * 'user.phone': 'Numéro de Téléphone', * 'actions.save': 'Enregistrer', * 'actions.cancel': 'Annuler' * } * }); * * // Define an object with translation keys * const formLabels = { * name: 'user.name', * email: 'user.email', * phone: 'user.phone' * }; * * // Translate the object * const translatedLabels = i18n.translateObject(formLabels); * console.log(translatedLabels); * // Output (for 'en' locale): { name: 'Name', email: 'Email Address', phone: 'Phone Number' } * // Output (for 'fr' locale): { name: 'Nom', email: 'Adresse Email', phone: 'Numéro de Téléphone' } * * // Can also be used with button configurations * const buttonConfig = { * saveButton: 'actions.save', * cancelButton: 'actions.cancel' * }; * const translatedButtons = i18n.translateObject(buttonConfig); * // Output (for 'en' locale): { saveButton: 'Save', cancelButton: 'Cancel' } * ``` * * @example * ```typescript * // Advanced usage with form validation messages * const validationMessages = { * required: 'validation.required', * email: 'validation.email.invalid', * minLength: 'validation.minLength', * maxLength: 'validation.maxLength' * }; * * // Assuming you have registered validation translations * const translatedValidation = i18n.translateObject(validationMessages); * // This allows you to easily get all validation messages in the current locale * ``` * * @note If the input object is not a valid object, an empty object of type T is returned. * @note Only string values that are non-null and non-empty are translated; other values are skipped. * @note This method is particularly useful for translating configuration objects, form labels, * button texts, or any structured data containing translation keys. * * @see {@link translateTarget} for translating class properties decorated with @Translate * @see {@link t} for translating individual keys with interpolation support * * @since 1.20.3 */ translateObject<T extends Record<string, string> = any>(object: T, options?: TranslateOptions): T; /*** * returns the translation keys for the target class * @param target the target class * @returns the translation keys for the target class */ static getTargetTanslationKeys<T extends { new (...args: any[]): {}; } = any>(target: T): Record<keyof T, string>; private _isLoading; /*** * locales that are superted by the i18n instance */ private _locales; /** * Namespace resolvers for loading translations. */ private namespaceResolvers; /** * Singleton instance of the I18n class. */ private static instance; /** * Creates an instance of the I18n class. * @param options Optional configuration options for the I18n instance. */ constructor(translations?: II18nTranslation, options?: Partial<I18nOptions>); readonly _observableFactory: { _____isObservable?: boolean; on: (event: I18nEvent, fn: IObservableCallback) => { remove: () => any; }; finally: (event: I18nEvent, fn: IObservableCallback) => /*elided*/ any; off: (event: I18nEvent, fn: IObservableCallback) => /*elided*/ any; trigger: (event: "*" | I18nEvent, ...args: any[]) => /*elided*/ any; offAll: () => /*elided*/ any; once: (event: I18nEvent, fn: IObservableCallback) => { remove: () => any; }; getEventCallBacks: () => Partial<Record<"*" | I18nEvent, IObservableCallback[]>>; }; readonly _____isObservable?: boolean | undefined; /** * Subscribes a callback function to a specific event. * @param event The event name to listen for. * @param fn The callback function to be invoked when the event is triggered. * @returns An object containing a remove method to unsubscribe from the event. */ on(event: I18nEvent, fn: IObservableCallback): { remove: () => any; }; /** * Registers a callback to be invoked finally when an event is triggered. * @param event The event name. * @param fn The callback function to be invoked. * @returns The observable instance. */ finally(event: I18nEvent, fn: IObservableCallback): { _____isObservable?: boolean; on: (event: I18nEvent, fn: IObservableCallback) => { remove: () => any; }; finally: (event: I18nEvent, fn: IObservableCallback) => /*elided*/ any; off: (event: I18nEvent, fn: IObservableCallback) => /*elided*/ any; trigger: (event: "*" | I18nEvent, ...args: any[]) => /*elided*/ any; offAll: () => /*elided*/ any; once: (event: I18nEvent, fn: IObservableCallback) => { remove: () => any; }; getEventCallBacks: () => Partial<Record<"*" | I18nEvent, IObservableCallback[]>>; }; /** * Unsubscribes a callback from a specific event. * @param event The event name. * @param fn The callback function to remove. * @returns The observable instance. */ off(event: I18nEvent, fn: IObservableCallback): { _____isObservable?: boolean; on: (event: I18nEvent, fn: IObservableCallback) => { remove: () => any; }; finally: (event: I18nEvent, fn: IObservableCallback) => /*elided*/ any; off: (event: I18nEvent, fn: IObservableCallback) => /*elided*/ any; trigger: (event: "*" | I18nEvent, ...args: any[]) => /*elided*/ any; offAll: () => /*elided*/ any; once: (event: I18nEvent, fn: IObservableCallback) => { remove: () => any; }; getEventCallBacks: () => Partial<Record<"*" | I18nEvent, IObservableCallback[]>>; }; /** * Triggers a specific event with optional arguments. * @param event The event name to trigger. * @param args Optional arguments to pass to the event callbacks. * @returns The observable instance. */ trigger(event: I18nEvent | "*", ...args: any[]): { _____isObservable?: boolean; on: (event: I18nEvent, fn: IObservableCallback) => { remove: () => any; }; finally: (event: I18nEvent, fn: IObservableCallback) => /*elided*/ any; off: (event: I18nEvent, fn: IObservableCallback) => /*elided*/ any; trigger: (event: "*" | I18nEvent, ...args: any[]) => /*elided*/ any; offAll: () => /*elided*/ any; once: (event: I18nEvent, fn: IObservableCallback) => { remove: () => any; }; getEventCallBacks: () => Partial<Record<"*" | I18nEvent, IObservableCallback[]>>; }; /** * Unsubscribes all event callbacks for this component. * @returns The observable instance. */ offAll(): IObservable<I18nEvent>; /** * Subscribes a callback function to be triggered once for a specific event. * @param event The event name. * @param fn The callback function to be invoked. * @returns An object containing a remove method to unsubscribe from the event. */ once(event: I18nEvent, fn: IObservableCallback): { remove: () => any; }; /** * Retrieves all registered event callbacks. * @returns An object mapping event names to their respective callback functions. */ getEventCallBacks(): Partial<Record<"*" | I18nEvent, IObservableCallback[]>>; /** * Retrieves the singleton instance of the I18n class. * @returns The singleton I18n instance. */ static getInstance(options?: I18nOptions): I18n; /*** * returns true if the instance is the default instance. * @returns true if the instance is the default instance. */ isDefaultInstance(): boolean; private static setLocaleToSession; static getLocaleFromSession(): any; /** * Checks if the provided translation key can be pluralized for the given locale. * @param scope The translation scope to check. * @param locale The locale to use for the check. If not provided, the current locale is used. * @returns `true` if the translation key can be pluralized, `false` otherwise. * @note This method is useful for determining if a translation key can be pluralized for a specific locale. * A translation key can be pluralized if it has pluralization rules defined in the translation dictionary. * The pluralization rules are defined in the `one`, `other`, and `zero` properties of the translation dictionary. * @example * //register a translation dictionary for the "en" locale. * i18n.registerTranslations({ * en: { * greeting: { * one: "Hello, {name}!", * other: "Hello, {name}s!", * zero: "Hello, {name}s!" * }, * farewell: "Goodbye!" * } * ); * }); * // Check if the translation key "greeting" can be pluralized for the current locale. * i18n.canPluralize("greeting"); * * // Check if the translation key "greeting" can be pluralized for the "en" locale. * i18n.canPluralize("greeting", "en"); * i18n.canPluralize("greeting", "fr"); // returns false * i18n.canPluralize("farewell", "en"); // returns false */ canPluralize(scope: Scope, locale?: string): boolean; /** * Resolves translation for nested keys. * @param scope {Scope} The translation scope. * @param locale The locale to use for translation. * @returns The translated string or undefined if not found. * @example * // Register translations for the "en" locale. * i18n.registerTranslations({ * en: { * greeting: { * one: "Hello, {name}!", * other: "Hello, {name}s!", * zero: "Hello, {name}s!" * }, * farewell: "Goodbye!" * } * }); * * // Resolve translation for the "greeting" key. * i18n.getNestedTranslation("greeting.one", "en"); * * // Resolve translation for the "greeting" key. * i18n.getNestedTranslation("greeting.other", "en"); * * // Resolve translation for the "greeting" key. * i18n.getNestedTranslation("en", "greeting.zero", 0); * * // Resolve translation for the "farewell" key. * i18n.getNestedTranslation("en", "farewell"); */ getNestedTranslation(scope: Scope, locale?: string): string | IDict | undefined; /** * Checks if the provided `TranslateOptions` object has a `count` property of type `number`. * This is used to determine if the translation should be pluralized based on the provided count. * @param options The `TranslateOptions` object to check. * @returns `true` if the `options` object has a `count` property of type `number`, `false` otherwise. */ isPluralizeOptions(options?: TranslateOptions): options is TranslateOptions; /** * static function to attach translations to the I18n default instance. @example : // --- Usage as a decorator --- I18n.RegisterTranslations({ de: { greeting: "Hallo, {name}!", farewell: "Auf Wiedersehen!", }, }) * @param translations The language translations. */ static RegisterTranslations(translations: II18nTranslation): II18nTranslation; /** * Factory method to create I18n instances dynamically. * @param options Optional configuration options for the I18n instance. * @returns A new I18n instance. */ static createInstance(translations?: II18nTranslation, options?: Partial<I18nOptions> & { interpolate?: (i18n: I18nJs, str: string, params: Record<string, any>) => string; }): I18n; /** * Gets the translations for the specified locale, or all translations if no locale is provided. * @param locale The locale to get translations for. If not provided, returns all translations. * @returns The translations for the specified locale, or all translations if no locale is provided. * @example * // Get all translations * const translations = i18n.getTranslations(); * console.log(translations); * * // Get translations for the "en" locale * const enTranslations = i18n.getTranslations("en"); * console.log(enTranslations); */ getTranslations(locale?: string): any; /*** * list of registered moment locales */ private static momentLocales; private hasRegisteredDefaultTranslations; /*** * register a moment locale * @param {string} locale * @param {LocaleSpecification} momentLocale * @see https://momentjs.com/docs/#/customization/ for more information on customizing moment locales * @see https://momentjs.com/docs/#/i18n/ for more information on moment locales * @returns */ static registerMomentLocale(locale: string, momentLocale: LocaleSpecification): Record<string, LocaleSpecification>; /*** * get a registered moment locale * @param {string} locale * @returns {LocaleSpecification} */ static getMomentLocale(locale: string): LocaleSpecification; /*** * set a moment locale. the locale is picked from the momentLocales list * @param {string} locale * @param {Moment} momentInstance, The moment instance to set the locale on * @returns {boolean} */ static setMomentLocale(locale: string): boolean; /** * Registers translations into the I18n manager. * @param translations The translations to register. * @returns The updated translations. */ registerTranslations(translations: II18nTranslation): II18nTranslation; /** * Stores the provided translations and triggers a "translations-changed" event with the current locale and translations. * @param translations The translations to store. */ store(translations: Dict): void; /** * Automatically resolves translations using reflect Metadata. * Translations created using the @Translate decorator will be resolved. * @param target The target class instance or object. * @example * // Class with translations using the decorator * class MyComponent { * @Translate("greeting") * public greeting: string; * * @Translate("nested.example") * public nestedExample: string; * * @Translate("farewell") * public sayGoodbye(): string { * return ""; * } * } * // Resolve translations and print them * const component = new MyComponent(); * I18n.getInstance().resolveTranslations(component); */ resolveTranslations<T extends Object>(target: T): void; /*** * returns the missing placeholder string for the given placeholder and message. * @param placeholder - The placeholder to be replaced. * @param message - The message to be displayed. * @param options - The options for the missing placeholder string. * @returns The missing placeholder string. */ getMissingPlaceholderString(placeholder: string, message?: string, options?: II18nTranslation): string; /** * Gets the current locale for the i18n instance. * @returns {string} The current locale. */ getLocale(): string; /** * Sets the list of supported locales for the i18n instance. * @param locales - An array of locale strings to set as the supported locales. * @returns The list of all locales supported by the i18n instance, including both the locales for which translations are available and the locales explicitly set as supported. */ setLocales(locales: string[]): string[]; /*** * returns true if the locale is supported by a i18n instance. * @param locale - The locale to check. * @returns true if the locale is supported, false otherwise. */ hasLocale(locale: string): boolean; /** * Gets the list of all locales supported by the i18n instance, including both the locales for which translations are available and the locales explicitly set as supported. * @returns {string[]} The list of all supported locales. */ getLocales(): string[]; /*** * returns true if the locale is supported by the i18n instance. * @param locale - The locale to check. * @returns true if the locale is supported, false otherwise. */ isLocaleSupported(locale: string): boolean; /*** * returns true if the instance is loading translations. * @returns true if the instance is loading translations, false otherwise. * @example * // Check if the instance is loading translations. * i18n.isLoading(); */ isLoading(): boolean; private _namespacesLoaded; setLocale(locale: string, forceUpdate?: boolean): Promise<string>; /** * Register a namespace resolver. * @param namespace The namespace to register. * @param resolver The resolver function to load the namespace. * @example * // Register a namespace resolver for the "common" namespace. * i18n.registerNamespaceResolver("common", async (locale) => { * const response = await fetch(`/i18n/${locale}/common.json`); * return await response.json(); * }); */ registerNamespaceResolver(namespace: string, resolver: (locale: string) => Promise<II18nTranslation>): void; /** * Static method to register a namespace resolver to the I18n default instance. * @param namespace, The namespace to register. * @param resolver, The resolver function to load the namespace. * @returns * @example * // Register a namespace resolver for the "common" namespace. * I18n.RegisterNamespaceResolver("common", async (locale) => { * const response = await fetch(`/i18n/${locale}/common.json`); * return await response.json(); * }); */ static RegisterNamespaceResolver(namespace: string, resolver: (locale: string) => Promise<any>): void; loadNamespace(namespace: string, locale?: string, updateTranslations?: boolean): Promise<II18nTranslation>; static LoadNamespace(namespace: string, locale?: string, updateTranslations?: boolean): Promise<II18nTranslation>; loadNamespaces(locale?: string, updateTranslations?: boolean): Promise<II18nTranslation>; /*** * Load all registered namespaces for the current locale on the I18n default instance. * @param locale optional locale to load the namespaces for * @param updateTranslations optional boolean to update the translations * @returns {Promise<II18nTranslation>} A promise that resolves to the combined translations for the current local */ static LoadNamespaces(locale?: string, updateTranslations?: boolean): Promise<II18nTranslation>; static flattenObject(obj: any): TranslateOptions; /** * Provides a default interpolation function for the I18n instance. * * If the input `value` is `undefined` or `null`, an empty string is returned. * If the input `value` is not a number, boolean, or string, it is converted to a string using `stringify`. * If the input `params` is not an object, the `value` is returned as-is. * If the input `params` is an object, the `value` is replaced with any matching placeholders in the format `%{key}` using the corresponding values from the `params` object. * * @param i18n The I18n instance. * @param value The input value to be interpolated. * @param params Optional object containing replacement values for placeholders in the `value`. * @returns The interpolated string. */ private static defaultInterpolator; } declare const i18n: I18n; export { i18n };