Skip to content

Commit

Permalink
feat: add buildT helper and place @fluent/bundle to optional peer dep…
Browse files Browse the repository at this point in the history
…endency with /fluent export
  • Loading branch information
kravetsone committed Oct 8, 2024
1 parent ee01111 commit dd760ef
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 37 deletions.
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,20 @@ const i18n = defineI18n({
},
});

// TODO: plugin

i18n.t("en", "greeting", "World"); // Hello, World!

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")
);
});
```

## [Fluent](https://projectfluent.org/) syntax
Expand Down Expand Up @@ -81,7 +92,7 @@ shared-photos =
```ts
// src/index.ts
import { Bot } from "gramio";
import { i18n } from "@gramio/i18n";
import { i18n } from "@gramio/i18n/fluent";

const bot = new Bot(process.env.TOKEN as string)
.extend(i18n())
Expand Down Expand Up @@ -200,7 +211,7 @@ We set this type as a **generic** for the `i18n` plugin. And now we have **type-
```ts
import type { TypedFluentBundle } from "./locales.types";
import { Bot } from "gramio";
import { i18n } from "@gramio/i18n";
import { i18n } from "@gramio/i18n/fluent";

const bot = new Bot(process.env.TOKEN as string)
.extend(i18n<TypedFluentBundle>())
Expand Down
14 changes: 9 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gramio/i18n",
"version": "0.0.6",
"version": "1.0.0",
"description": "i18n plugin for GramIO with type-safety",
"main": "dist/index.cjs",
"module": "dist/index.js",
Expand Down Expand Up @@ -47,7 +47,8 @@
"localization",
"translation",
"telegram-bot-api",
"telegram-bot"
"telegram-bot",
"i18n-in-ts"
],
"devDependencies": {
"@biomejs/biome": "1.9.3",
Expand All @@ -58,10 +59,13 @@
"typescript": "^5.6.2"
},
"peerDependencies": {
"gramio": ">=0.0.39 <1.0.0"
"gramio": ">=0.0.39 <1.0.0",
"@fluent/bundle": "^0.18.0"
},
"files": ["dist"],
"dependencies": {
"@fluent/bundle": "^0.18.0"
"peerDependenciesMeta": {
"@fluent/bundle": {
"optional": true
}
}
}
78 changes: 55 additions & 23 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type {
} from "./types.js";

export * from "./types.js";
export * from "./fluent/index.js";

export interface I18nOptions<
Languages extends LanguagesMap,
Expand All @@ -20,33 +19,66 @@ export function defineI18n<
Languages extends LanguagesMap,
PrimaryLanguage extends keyof Languages,
>({ languages, primaryLanguage }: I18nOptions<Languages, PrimaryLanguage>) {
return {
t: <
Language extends SoftString<keyof Languages>,
Key extends keyof Languages[Language],
Item extends Languages[Language][Key],
// @ts-expect-error trust me bro
FallbackItem extends Languages[PrimaryLanguage][Key],
>(
language: Language,
key: Key,
...args: ExtractArgsParams<Item>
): ExtractItemValue<Item, FallbackItem> => {
// @ts-expect-error trust me bro
const fallbackItem = languages[primaryLanguage][key];
console.log(languages[primaryLanguage], primaryLanguage);
// TODO: вынести из скоупа
function t<
Language extends SoftString<keyof Languages>,
Key extends Language extends keyof Languages
? keyof Languages[Language]
: keyof Languages[PrimaryLanguage],
Item extends Language extends keyof Languages
? // @ts-expect-error
Languages[Language][Key]
: // @ts-expect-error
Languages[PrimaryLanguage][Key],
// @ts-expect-error trust me bro
FallbackItem extends Languages[PrimaryLanguage][Key],
>(
language: Language,
key: Key,
...args: Item extends unknown
? ExtractArgsParams<FallbackItem>
: ExtractArgsParams<Item>
): ExtractItemValue<Item, FallbackItem> {
// @ts-expect-error trust me bro
const fallbackItem = languages[primaryLanguage][key];
console.log(languages[primaryLanguage], primaryLanguage);

const item = languages[language]
? (languages[language][key] ?? fallbackItem)
: fallbackItem;
const item = languages[language]
? // @ts-expect-error
(languages[language][key] ?? fallbackItem)
: fallbackItem;

// @ts-expect-error trust me bro
if (typeof item === "function") return item(...args);
// @ts-expect-error trust me bro
if (typeof item === "function") return item(...args);

return item;
},
return item;
}

return {
t,
languages: Object.keys(languages) as (keyof Languages)[],
primaryLanguage,
buildT: <Language extends SoftString<keyof Languages>>(
language: Language,
) => {
return <
Key extends Language extends keyof Languages
? keyof Languages[Language]
: keyof Languages[PrimaryLanguage],
Item extends Language extends keyof Languages
? // @ts-expect-error trust me bro
Languages[Language][Key]
: // @ts-expect-error trust me bro
Languages[PrimaryLanguage][Key],
// @ts-expect-error trust me bro
FallbackItem extends Languages[PrimaryLanguage][Key],
>(
key: Key,
...args: Item extends unknown
? ExtractArgsParams<FallbackItem>
: ExtractArgsParams<Item>
): ExtractItemValue<Item, FallbackItem> => t(language, key, ...args);
},
// plugin: () => {},
};
}
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type ExtractArgsParams<Item extends LocaleItem> =
export type ExtractItemValue<
Item extends LocaleItem,
FallbackItem extends LocaleItem,
> = Item extends never | undefined
> = Item extends never | undefined | unknown
? FallbackItem
: Item extends LocaleArgs
? ReturnType<Item>
Expand Down
29 changes: 25 additions & 4 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from "bun:test";
import { format } from "gramio";
import { Bot, format } from "gramio";
import { defineI18n } from "../src";
import type { ShouldFollowLanguage } from "../src/types";

Expand Down Expand Up @@ -37,8 +37,29 @@ describe("I18n", () => {
});

expect(i18n.t("en", "greeting", "World").toString()).toBe("Hello, World!");
expect(i18n.t("rus" as string, "greeting", "World").toString()).toBe(
"Hello, World!",
);
expect(i18n.t("ens", "greeting", "World").toString()).toBe("Hello, World!");
});

it("Just pickup primary translation", () => {
const i18n = defineI18n({
primaryLanguage: "en",
languages: {
en,
ru,
},
});

const bot = new Bot("s")
.derive("message", (context) => {
return {
t: i18n.buildT((context.from?.languageCode as string) ?? "en"),
};
})
.on("message", (context) => {
context.t("greeting", "s");
});

expect(i18n.t("en", "greeting", "World").toString()).toBe("Hello, World!");
expect(i18n.t("en", "greeting", "World").toString()).toBe("Hello, World!");
});
});

0 comments on commit dd760ef

Please sign in to comment.