@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
TypeScript
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 };