@grammyjs/i18n
Version:
Internationalization plugin for grammY based on Fluent.
173 lines (172 loc) • 6.44 kB
JavaScript
;
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);
};
}