i18n
plugin for GramIO.
This plugin provide good way to add internationalization for your bots! It can be used without GramIO, but it will always keep it in mind.
[!IMPORTANT] Since
1.0.0
, we have two ways to write localization:I18n-in-TS
andFluent
This syntax allows you to write localization without leaving .ts
files and does not require code-generation for type-safety, as well as provides convenient integration with the Format API out of the box!
import { format, Bot } from "gramio";
import {
defineI18n,
type LanguageMap,
type ShouldFollowLanguage,
} from "@gramio/i18n";
const en = {
greeting: (name: string) => format`Hello, ${name}!`,
and: {
some: {
nested: "Hi!!!",
},
},
} satisfies LanguageMap;
const ru = {
greeting: (name: string) => format`Привет, ${name}!`,
and: {
some: {
nested: "Hi!!!",
},
},
} satisfies ShouldFollowLanguage<typeof en>;
// Strict will show error on missing keys
// satisfies ShouldFollowLanguageStrict<typeof en>;
const i18n = defineI18n({
primaryLanguage: "en",
languages: {
en,
ru,
},
});
i18n.t("en", "greeting", "World"); // Hello, World!
i18n.t("en", "and.some.nested"); // Hi!!!
const bot = new Bot(process.env.BOT_TOKEN as string)
.derive("message", (context) => {
// u can take language from database or whatever u want and bind it to context without loosing type-safety
return {
t: i18n.buildT(context.from?.languageCode ?? "en"),
};
})
.on("message", (context) => {
return context.send(
context.t("greeting", context.from?.firstName ?? "World")
);
});
import { pluralizeEnglish, pluralizeRussian } from "@gramio/i18n";
const count = 5;
console.log(`You have ${count} ${pluralizeEnglish(count, "apple", "apples")}.`); // You have 5 apples.
console.log(
`У вас ${count} ${pluralizeRussian(count, "яблоко", "яблока", "яблок")}.`
); // У вас 5 яблок.
ExtractLanguages
helps you extract languages types from i18n instance.
type EnLocalization = ExtractLanguages<typeof i18n>["en"];
type EnLocalizationKeys = keyof ExtractLanguages<typeof i18n>["en"];
type EnGreetingArgs = ExtractArgsParams<EnLocalization["greeting"]>;
Fluent syntax
This plugin provide internationalization for your bots with Fluent syntax.
You can setup type-safety for it.
# Simple things are simple.
hello-user = Hello, {$userName}!
# Complex things are possible.
shared-photos =
{$userName} {$photoCount ->
[one] added a new photo
*[other] added {$photoCount} new photos
} to {$userGender ->
[male] his stream
[female] her stream
*[other] their stream
}.
[!IMPORTANT] Fluent language support extensions for VSCode and WebStorm
// src/index.ts
import { Bot } from "gramio";
import { i18n } from "@gramio/i18n/fluent";
const bot = new Bot(process.env.TOKEN as string)
.extend(i18n())
.command("start", async (context) => {
return context.send(
context.t("shared-photos", {
userName: "Anna",
userGender: "female",
photoCount: 3,
})
);
})
.onError(console.error)
.onStart(console.log);
bot.start();
Key | Type | Default | Description |
---|---|---|---|
defaultLocale? | string | first loaded language | Default locale |
directory? | string | "locales" | The path to the folder with *.ftl files |
Using this method, you can get the text in your chosen language.
For example:
hello-user = Hello, {$userName}!
context.t("hello-user", { userName: "Anna" }); // Hello, Anna!
You can set user locale by setLocale
method.
[!WARNING] At the moment, there is no integration with sessions, and therefore, after the message, the language will again become the one that defaultLocale
bot.command("start", async (context) => {
context.i18n.setLocale("ru"); // if ru not found fallback to defaultLocale
// context.i18n.setLocale("ru", true); if ru not found throw error
return context.send(
context.t("shared-photos", {
userName: "Anna",
userGender: "female",
photoCount: 3,
})
);
});
Get current user locale.
bot.command("lang", async (context) => {
return context.send(context.i18n.locale);
});
Get loaded locales
bot.command("languages", async (context) => {
return context.send(context.i18n.locales.join(", "));
});
You can use this plugin with fluent2ts which code-generates typescript types from your .ftl
files.
See usage.
Npm:
npx fluent2ts
Bun:
bunx fluent2ts
Yarn:
yarn dlx fluent2ts
Pnpm:
pnpm exec fluent2ts
And so we have a generated locales.types.ts
file in src
folder that exports the TypedFluentBundle
interface.
We set this type as a generic for the i18n
plugin. And now we have type-safety!
import type { TypedFluentBundle } from "./locales.types";
import { Bot } from "gramio";
import { i18n } from "@gramio/i18n/fluent";
const bot = new Bot(process.env.TOKEN as string)
.extend(i18n<TypedFluentBundle>())
.command("start", async (context) => {
return context.send(
context.t("shared-photos", {
userName: "Anna",
userGender: "female",
photoCount: 3,
})
);
})
.onError(console.error)
.onStart(console.log);
bot.start();