UNPKG

@grammyjs/i18n

Version:

Internationalization plugin for grammY based on Fluent.

173 lines (172 loc) 6.44 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.I18n = void 0; exports.hears = hears; const fluent_js_1 = require("./fluent.js"); const utils_js_1 = require("./utils.js"); class I18n { constructor(config) { Object.defineProperty(this, "config", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "fluent", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "locales", { enumerable: true, configurable: true, writable: true, value: new Array() }); this.config = { defaultLocale: "en", ...config }; this.fluent = new fluent_js_1.Fluent(this.config.fluentOptions); if (config.directory) { this.loadLocalesDirSync(config.directory); } } /** * Loads locales from the specified directory and registers them in the Fluent instance. * @param directory Path to the directory to look for the translation files. */ async loadLocalesDir(directory) { const localeFiles = await (0, utils_js_1.readLocalesDir)(directory); await Promise.all(localeFiles.map(async (file) => { await this.loadLocale(file.belongsTo, { source: file.translationSource, bundleOptions: this.config.fluentBundleOptions, }); })); } /** * Loads locales from any existing nested file or folder within the specified directory and registers them in the Fluent instance. * @param directory Path to the directory to look for the translation files. */ loadLocalesDirSync(directory) { for (const file of (0, utils_js_1.readLocalesDirSync)(directory)) { this.loadLocaleSync(file.belongsTo, { source: file.translationSource, bundleOptions: this.config.fluentBundleOptions, }); } } /** * Registers a locale in the Fluent instance based on the provided options. * @param locale Locale ID * @param options Options to specify the source and behavior of the translation */ async loadLocale(locale, options) { await this.fluent.addTranslation({ locales: locale, isDefault: locale === this.config.defaultLocale, bundleOptions: this.config.fluentBundleOptions, ...options, }); this.locales.push(locale); } /** * Synchronously registers a locale in the Fluent instance based on the provided options. * @param locale Locale ID * @param options Options to specify the source and behavior of the translation */ loadLocaleSync(locale, options) { this.fluent.addTranslationSync({ locales: locale, isDefault: locale === this.config.defaultLocale, bundleOptions: this.config.fluentBundleOptions, ...options, }); this.locales.push(locale); } /** * Gets a message by its key from the specified locale. * Alias of `translate`. */ t(locale, key, variables) { return this.translate(locale, key, variables); } /** Gets a message by its key from the specified locale. */ translate(locale, key, variables) { return this.fluent.translate(locale, key, variables); } /** Returns a middleware to .use on the `Bot` instance. */ middleware() { return middleware(this.fluent, this.config); } } exports.I18n = I18n; function middleware(fluent, { defaultLocale, localeNegotiator, useSession, globalTranslationContext, }) { return async function (ctx, next) { let translate; function useLocale(locale) { translate = fluent.withLocale(locale); } async function getNegotiatedLocale() { return await localeNegotiator?.(ctx) ?? // deno-lint-ignore no-explicit-any (await (useSession && ctx.session))?.__language_code ?? ctx.from?.language_code ?? defaultLocale; } async function setLocale(locale) { if (!useSession) { throw new Error("You are calling `ctx.i18n.setLocale()` without setting `useSession` to `true` \ in the configuration. It doesn't make sense because you cannot set a locale in \ the session that way. When you call `ctx.i18n.setLocale()`, the bot tries to \ store the user locale in the session storage. But since you don't have session \ enabled, it cannot store the locale information in the session storage. You \ should either enable sessions or use `ctx.i18n.useLocale()` instead."); } // deno-lint-ignore no-explicit-any (await ctx.session).__language_code = locale; await negotiateLocale(); } // Determining the locale to use for translations async function negotiateLocale() { const negotiatedLocale = await getNegotiatedLocale(); useLocale(negotiatedLocale); } Object.defineProperty(ctx, "i18n", { value: { fluent, renegotiateLocale: negotiateLocale, useLocale, getLocale: getNegotiatedLocale, setLocale, }, // Allow redefine property. This is necessary to be able to install the plugin // inside the conversation even if the plugin is already installed globally. writable: true, }); ctx.translate = (key, translationVariables) => { return translate(key, { ...globalTranslationContext?.(ctx), ...translationVariables, }); }; ctx.t = ctx.translate; await negotiateLocale(); await next(); }; } /** * A filter middleware for listening to the messages send by the in their language. * It is useful when you have to listen for custom keyboard texts. * * ```ts * bot.filter(hears("menu-btn"), (ctx) => ...) * ``` * * @param key Key of the message to listen for. */ function hears(key) { return function (ctx) { const expected = ctx.t(key); return ctx.hasText(expected); }; }