{"id":26655636,"url":"https://github.com/gramiojs/i18n","last_synced_at":"2025-10-14T08:34:21.965Z","repository":{"id":237534902,"uuid":"794666005","full_name":"gramiojs/i18n","owner":"gramiojs","description":"This plugin provide good way to add internationalization for your bots!","archived":false,"fork":false,"pushed_at":"2025-07-12T22:20:44.000Z","size":182,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-14T02:49:12.876Z","etag":null,"topics":["fluent","i10n","i18n","internationalization","locales","telegram","telegram-bot","telegram-bot-api"],"latest_commit_sha":null,"homepage":"https://gramio.netlify.app/plugins/official/i18n.html","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gramiojs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-01T17:39:54.000Z","updated_at":"2025-07-12T21:51:46.000Z","dependencies_parsed_at":"2024-05-02T12:19:51.873Z","dependency_job_id":"9221d549-fcc2-4bfe-80d9-b334a834bdc2","html_url":"https://github.com/gramiojs/i18n","commit_stats":null,"previous_names":["gramiojs/i18n"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/gramiojs/i18n","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gramiojs%2Fi18n","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gramiojs%2Fi18n/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gramiojs%2Fi18n/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gramiojs%2Fi18n/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gramiojs","download_url":"https://codeload.github.com/gramiojs/i18n/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gramiojs%2Fi18n/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279015939,"owners_count":26085777,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fluent","i10n","i18n","internationalization","locales","telegram","telegram-bot","telegram-bot-api"],"created_at":"2025-03-25T06:36:47.091Z","updated_at":"2025-10-14T08:34:21.953Z","avatar_url":"https://github.com/gramiojs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @gramio/i18n\n\n[![npm](https://img.shields.io/npm/v/@gramio/i18n?logo=npm\u0026style=flat\u0026labelColor=000\u0026color=3b82f6)](https://www.npmjs.org/package/@gramio/i18n)\n[![npm downloads](https://img.shields.io/npm/dw/@gramio/i18n?logo=npm\u0026style=flat\u0026labelColor=000\u0026color=3b82f6)](https://www.npmjs.org/package/@gramio/i18n)\n[![JSR](https://jsr.io/badges/@gramio/i18n)](https://jsr.io/@gramio/i18n)\n[![JSR Score](https://jsr.io/badges/@gramio/i18n/score)](https://jsr.io/@gramio/i18n)\n\n`i18n` plugin for [GramIO](https://gramio.dev/).\n\nThis plugin provide good way to add internationalization for your bots! It can be used without GramIO, but it will always keep it in mind.\n\n\u003e [!IMPORTANT]\n\u003e Since `1.0.0`, we have two ways to write localization: [`I18n-in-TS`](#i18n-in-ts-syntax) and [`Fluent`](#fluent-syntax)\n\n### Installation\n\nFor [I18n-in-TS syntax](#i18n-in-ts-syntax)\n\n```bash\nnpm install @gramio/i18n\n```\n\nFor [Fluent syntax](#fluent-syntax)\n\n```bash\nnpm install @gramio/i18n @fluent/bundle\n```\n\n## I18n-in-TS syntax\n\nThis 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!\n\n```ts\nimport { format, Bot } from \"gramio\";\nimport {\n    defineI18n,\n    type LanguageMap,\n    type ShouldFollowLanguage,\n} from \"@gramio/i18n\";\n\nconst en = {\n    greeting: (name: string) =\u003e format`Hello, ${name}!`,\n    and: {\n        some: {\n            nested: \"Hi!!!\",\n        },\n    },\n} satisfies LanguageMap;\n\nconst ru = {\n    greeting: (name: string) =\u003e format`Привет, ${name}!`,\n    and: {\n        some: {\n            nested: \"Hi!!!\",\n        },\n    },\n} satisfies ShouldFollowLanguage\u003ctypeof en\u003e;\n\n// Strict will show error on missing keys\n// satisfies ShouldFollowLanguageStrict\u003ctypeof en\u003e;\n\nconst i18n = defineI18n({\n    primaryLanguage: \"en\",\n    languages: {\n        en,\n        ru,\n    },\n});\n\ni18n.t(\"en\", \"greeting\", \"World\"); // Hello, World!\ni18n.t(\"en\", \"and.some.nested\"); // Hi!!!\n\nconst bot = new Bot(process.env.BOT_TOKEN as string)\n    .derive(\"message\", (context) =\u003e {\n        // u can take language from database or whatever u want and bind it to context without loosing type-safety\n        return {\n            t: i18n.buildT(context.from?.languageCode ?? \"en\"),\n        };\n    })\n    .on(\"message\", (context) =\u003e {\n        return context.send(\n            context.t(\"greeting\", context.from?.firstName ?? \"World\")\n        );\n    });\n```\n\n### Plurals\n\n```ts\nimport { pluralizeEnglish, pluralizeRussian } from \"@gramio/i18n\";\n\nconst count = 5;\n\nconsole.log(`You have ${count} ${pluralizeEnglish(count, \"apple\", \"apples\")}.`); // You have 5 apples.\n\nconsole.log(\n    `У вас ${count} ${pluralizeRussian(count, \"яблоко\", \"яблока\", \"яблок\")}.`\n); // У вас 5 яблок.\n```\n\n### Get type-safe params for i18n\n\n```ts\n// Most great way to get type-safe params for i18n\nexport type GetI18nArgs\u003cKey extends GetI18nKeys\u003ctypeof i18n\u003e\u003e = GetI18nParams\u003c\n    typeof i18n,\n    Key\n\u003e;\n\nfunction test(...args: GetI18nArgs\u003c\"greeting\"\u003e) {\n    return args;\n}\n\ntype EnLocalization = ExtractLanguages\u003ctypeof i18n\u003e[\"en\"];\ntype EnLocalizationKeys = keyof ExtractLanguages\u003ctypeof i18n\u003e[\"en\"];\n\ntype EnGreetingArgs = ExtractArgsParams\u003cEnLocalization[\"greeting\"]\u003e;\n```\n\n## [Fluent](https://projectfluent.org/) syntax\n\nThis plugin provide internationalization for your bots with [Fluent](https://projectfluent.org/) syntax.\n\n![example](https://github.com/gramiojs/i18n/assets/57632712/47e04c22-f442-4a5a-b8b9-15b8512f7c4b)\n\nYou can [setup type-safety](#type-safety) for it.\n\n## Usage\n\n### Create `locales` folder with `en.ftl` file\n\n```fluent\n# Simple things are simple.\nhello-user = Hello, {$userName}!\n\n# Complex things are possible.\nshared-photos =\n    {$userName} {$photoCount -\u003e\n        [one] added a new photo\n       *[other] added {$photoCount} new photos\n    } to {$userGender -\u003e\n        [male] his stream\n        [female] her stream\n       *[other] their stream\n    }.\n```\n\n\u003e [!IMPORTANT]\n\u003e Fluent language support extensions for [VSCode](https://marketplace.visualstudio.com/items?itemName=macabeus.vscode-fluent) and [WebStorm](https://plugins.jetbrains.com/plugin/18416-fluent-language)\n\n### Use plugin\n\n```ts\n// src/index.ts\nimport { Bot } from \"gramio\";\nimport { i18n } from \"@gramio/i18n/fluent\";\n\nconst bot = new Bot(process.env.BOT_TOKEN as string)\n    .extend(i18n())\n    .command(\"start\", async (context) =\u003e {\n        return context.send(\n            context.t(\"shared-photos\", {\n                userName: \"Anna\",\n                userGender: \"female\",\n                photoCount: 3,\n            })\n        );\n    })\n    .onError(console.error)\n    .onStart(console.log);\n\nbot.start();\n```\n\n## Options\n\n| Key            | Type   | Default               | Description                               |\n| -------------- | ------ | --------------------- | ----------------------------------------- |\n| defaultLocale? | string | first loaded language | Default locale                            |\n| directory?     | string | \"locales\"             | The path to the folder with `*.ftl` files |\n\n##### Or provide an client\n\n```ts\n// ...\nimport { getFluentClient, i18n } from \"@gramio/i18n/fluent\";\n\nconst client = getFluentClient({\n    defaultLocale: \"en\",\n    directory: \"locales\",\n});\n\nconst bot = new Bot(process.env.BOT_TOKEN as string)\n    .extend(i18n(client))\n    .command(\"start\", async (context) =\u003e {\n        return context.send(context.t(\"hello-user\", { userName: \"Anna\" }));\n    });\n```\n\n### Methods\n\n#### t\n\nUsing this method, you can get the text in your chosen language.\n\nFor example:\n\n```ftl\nhello-user = Hello, {$userName}!\n```\n\n```ts\ncontext.t(\"hello-user\", { userName: \"Anna\" }); // Hello, Anna!\n```\n\n#### i18n.setLocale\n\nYou can set user locale by `setLocale` method.\n\n\u003e [!WARNING]\n\u003e At the moment, there is no integration with sessions, and therefore, after the message, the language will again become the one that defaultLocale\n\n```ts\nbot.command(\"start\", async (context) =\u003e {\n    context.i18n.setLocale(\"ru\"); // if ru not found fallback to defaultLocale\n    // context.i18n.setLocale(\"ru\", true); if ru not found throw error\n\n    return context.send(\n        context.t(\"shared-photos\", {\n            userName: \"Anna\",\n            userGender: \"female\",\n            photoCount: 3,\n        })\n    );\n});\n```\n\n#### i18n.locale\n\nGet current user locale.\n\n```ts\nbot.command(\"lang\", async (context) =\u003e {\n    return context.send(context.i18n.locale);\n});\n```\n\n#### i18n.locales\n\nGet loaded locales\n\n```ts\nbot.command(\"languages\", async (context) =\u003e {\n    return context.send(context.i18n.locales.join(\", \"));\n});\n```\n\n## Type-safety\n\nYou can use this plugin with [fluent2ts](https://github.com/kravetsone/fluent2ts) which code-generates typescript types from your `.ftl` files.\nSee [usage](https://github.com/kravetsone/fluent2ts?tab=readme-ov-file#usage).\n\nNpm:\n\n```bash [npm]\nnpx fluent2ts\n```\n\nBun:\n\n```bash [bun]\nbunx fluent2ts\n```\n\nYarn:\n\n```bash [yarn]\nyarn dlx fluent2ts\n```\n\nPnpm:\n\n```bash [pnpm]\npnpm exec fluent2ts\n```\n\nAnd so we have a generated `locales.types.ts` file in `src` folder that exports the `TypedFluentBundle` interface.\nWe set this type as a **generic** for the `i18n` plugin. And now we have **type-safety**!\n\n```ts\nimport type { TypedFluentBundle } from \"./locales.types\";\nimport { Bot } from \"gramio\";\nimport { i18n } from \"@gramio/i18n/fluent\";\n\nconst bot = new Bot(process.env.BOT_TOKEN as string)\n    // or i18n(getFluentClient\u003cTypedFluentBundle\u003e())\n    .extend(i18n\u003cTypedFluentBundle\u003e())\n    .command(\"start\", async (context) =\u003e {\n        return context.send(\n            context.t(\"shared-photos\", {\n                userName: \"Anna\",\n                userGender: \"female\",\n                photoCount: 3,\n            })\n        );\n    })\n    .onError(console.error)\n    .onStart(console.log);\n\nbot.start();\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgramiojs%2Fi18n","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgramiojs%2Fi18n","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgramiojs%2Fi18n/lists"}