From 453577f1b29b437bd83a90903b9a4d6b83ce27fa Mon Sep 17 00:00:00 2001 From: mo Date: Sat, 21 Feb 2026 00:20:12 +0100 Subject: [PATCH] i18n waterMe --- .../src/features/water-me/water-me.service.ts | 7 +- adapters/fluxer/src/actions/shutdown.ts | 2 +- .../src/features/water-me/water-me.service.ts | 7 +- .../fluxer/src/listeners/ready.listener.ts | 2 +- core/package.json | 3 + core/src/config.ts | 42 ------------ .../src/features/water-me/water-me.service.ts | 60 +++++++++-------- core/src/lib/i18n/i18n.service.ts | 64 +++++++++++++++++++ core/src/lib/i18n/index.ts | 3 + core/src/locales/de/water-me.ts | 10 +++ core/src/locales/en/water-me.ts | 10 +++ core/src/locales/index.ts | 9 +++ core/tsconfig.json | 3 +- 13 files changed, 144 insertions(+), 78 deletions(-) delete mode 100644 core/src/config.ts create mode 100644 core/src/lib/i18n/i18n.service.ts create mode 100644 core/src/lib/i18n/index.ts create mode 100644 core/src/locales/de/water-me.ts create mode 100644 core/src/locales/en/water-me.ts create mode 100644 core/src/locales/index.ts diff --git a/adapters/discord/src/features/water-me/water-me.service.ts b/adapters/discord/src/features/water-me/water-me.service.ts index 047fedb..b2ad152 100644 --- a/adapters/discord/src/features/water-me/water-me.service.ts +++ b/adapters/discord/src/features/water-me/water-me.service.ts @@ -1,4 +1,9 @@ import { WaterMeService } from "@avocadi/bot-core/features/water-me/water-me.service"; +import { i18nService } from "@avocadi/bot-core/lib/i18n"; import { messagesService } from "entitites/messages/messages.service"; -export const waterMeService = new WaterMeService(messagesService); +export const waterMeService = new WaterMeService( + messagesService, + i18nService, + "de", +); diff --git a/adapters/fluxer/src/actions/shutdown.ts b/adapters/fluxer/src/actions/shutdown.ts index aa8f80b..693c5f0 100644 --- a/adapters/fluxer/src/actions/shutdown.ts +++ b/adapters/fluxer/src/actions/shutdown.ts @@ -3,5 +3,5 @@ import { logger } from "lib/common-logger"; export const handleShutdown = async () => { logger.info("bot is shutting down..."); - await logChannelService.sendLogMessage("ich geh schlafen..."); + await logChannelService.sendLogMessage("**S H U T T I N G D O W N**"); }; diff --git a/adapters/fluxer/src/features/water-me/water-me.service.ts b/adapters/fluxer/src/features/water-me/water-me.service.ts index 983efe3..f830754 100644 --- a/adapters/fluxer/src/features/water-me/water-me.service.ts +++ b/adapters/fluxer/src/features/water-me/water-me.service.ts @@ -1,4 +1,9 @@ import { WaterMeService } from "@avocadi/bot-core/features/water-me/water-me.service"; +import { i18nService } from "@avocadi/bot-core/lib/i18n"; import { messagesService } from "entities/messages/messages.service"; -export const waterMeService = new WaterMeService(messagesService); +export const waterMeService = new WaterMeService( + messagesService, + i18nService, + "en", +); diff --git a/adapters/fluxer/src/listeners/ready.listener.ts b/adapters/fluxer/src/listeners/ready.listener.ts index 284a5b5..8584976 100644 --- a/adapters/fluxer/src/listeners/ready.listener.ts +++ b/adapters/fluxer/src/listeners/ready.listener.ts @@ -5,7 +5,7 @@ import client from "lib/client"; import { logger } from "lib/common-logger"; client.on(Events.Ready, async () => { - await logChannelService.sendLogMessage("wieder online!!!"); + await logChannelService.sendLogMessage("**O N L I N E**"); logger.info("bot is online"); diff --git a/core/package.json b/core/package.json index 6f30296..edbc5bd 100644 --- a/core/package.json +++ b/core/package.json @@ -21,6 +21,7 @@ "discord.js": "^14.16.3", "dotenv": "^16.4.7", "drizzle-orm": "^0.38.3", + "i18next": "^25.8.11", "tslog": "^4.10.2", "zod": "catalog:" }, @@ -46,6 +47,8 @@ "./features/greeting/greeting.service": "./dist/features/greeting/greeting.service.js", "./features/water-me/water-me.service": "./dist/features/water-me/water-me.service.js", "./lib/common": "./dist/lib/common.js", + "./lib/i18n": "./dist/lib/i18n/index.js", + "./lib/i18n/i18n.service": "./dist/lib/i18n/i18n.service.js", "./lib/logger": "./dist/lib/logger.js", "./lib/utils": "./dist/lib/utils.js", "./lib/utils.test": "./dist/lib/utils.test.js", diff --git a/core/src/config.ts b/core/src/config.ts deleted file mode 100644 index 14a7736..0000000 --- a/core/src/config.ts +++ /dev/null @@ -1,42 +0,0 @@ -export default { - discord: { - version: 251218, - // avocadi - serverID: process.env.DISCORD_SERVER_ID || "", - // text channel - channelIdBump: process.env.DISCORD_CHANNEL_ID_BUMP || "", - channelIdBot: process.env.DISCORD_CHANNEL_ID_BOT || "", - channelIdFeedback: process.env.DISCORD_CHANNEL_ID_FEEDBACK || "", - channelIdLog: process.env.DISCORD_CHANNEL_ID_NOTIFICATION || "", - channelIdWelcome: process.env.DISCORD_CHANNEL_ID_WELCOME || "", - channelIdRules: process.env.DISCORD_CHANNEL_ID_RULE || "", - channelIdNews: process.env.DISCORD_CHANNEL_ID_NEWS || "", - channelIdIntroduction: process.env.DISCORD_CHANNEL_ID_INTRODUCTION || "", - channelIdOffTopic: process.env.DISCORD_CHANNEL_ID_OFF_TOPIC || "", - channelIdHelp: process.env.DISCORD_CHANNEL_ID_HELP || "", - channelIdPomodoro: process.env.DISCORD_CHANNEL_ID_POMODORO || "", - // voice channel - vchannelIdForTwo: process.env.DISCORD_VCHANNEL_ID_FOR_TWO || "", - vchannelIdForThree: process.env.DISCORD_VCHANNEL_ID_FOR_THREE || "", - vchannelIdForFour: process.env.DISCORD_VCHANNEL_ID_FOR_FOUR || "", - vchannelIdForGroup: process.env.DISCORD_VCHANNEL_ID_FOR_GROUP || "", - vchannelIdPomodoro25: process.env.DISCORD_VCHANNEL_ID_POMODORO_25_5 || "", - vchannelIdPomodoro50: process.env.DISCORD_VCHANNEL_ID_POMODORO_50_10 || "", - // roles - roleBot: process.env.BOT || "", - roleStudy: process.env.PEOPLE || "", - roleMod: process.env.MOD || "", - roleAdmin: process.env.ADMIN || "", - roleMention: process.env.MENTION || "", - - rolesMsg: process.env.ROLES_NOTIFICATION_MSG || "", - rolesMsgTest: process.env.ROLES_MSG_TEST || "", - // other - applicationId: process.env.DISCORD_APPLICATION_ID || "", - token: process.env.DISCORD_TOKEN || "", - - botId: process.env.BOT_ID || "", - myId: process.env.MY_ID || "", - enricoId: process.env.ENRICO_ID || "", - }, -}; diff --git a/core/src/features/water-me/water-me.service.ts b/core/src/features/water-me/water-me.service.ts index 87b4d2f..2fabdd3 100644 --- a/core/src/features/water-me/water-me.service.ts +++ b/core/src/features/water-me/water-me.service.ts @@ -1,6 +1,7 @@ import type { MessagesServiceInterface } from "entities/messages/messages.service"; +import type { ParseKeys } from "i18next"; +import type { I18nService } from "lib/i18n/i18n.service"; import { createLogger } from "lib/logger"; -import { getRandomInt } from "lib/utils"; export class WaterMeService { waterLevel: number; @@ -8,48 +9,45 @@ export class WaterMeService { private logger = createLogger("WaterMeService"); private thirsty = 3 as const; - private enough = 10 as const; + private full = 10 as const; messagesService: MessagesServiceInterface; + i18nService: I18nService; + lang: "en" | "de"; - constructor(messagesService: MessagesServiceInterface) { + constructor( + messagesService: MessagesServiceInterface, + i18nService: I18nService, + lang: "en" | "de", + ) { this.waterLevel = 0; this.messagesService = messagesService; + this.i18nService = i18nService; + this.lang = lang; } - getReply() { - const thirstyReplies = [ - "... wow das wars schon??? ich brauche noch mehr wasser :(", - "dankeeeee!!!! ich waer fast verdurstet :(((", - "*roelpssssss*", - ]; + async getReply() { + // const key = ( + // this.waterLevel <= this.thirsty + // ? "water-me.needMoreWater" + // : this.waterLevel <= this.full + // ? "water-me.enoughWater" + // : "water-me.tooMuchWater" + // ) as ParseKeys; - const fullReplies = [ - "langsam reicht es :o", - "poah, das hat gut getan", - "das ist krass :3", - ]; + // return await this.i18nService.t(key, this.lang); - const tooMuchReplies = [ - "ES REICHT!!!!", - "bitte hoer auf, ich platze gleich :(", - ]; - - if (this.waterLevel <= this.thirsty) { - return thirstyReplies[getRandomInt(0, thirstyReplies.length - 1)]; - } - if (this.waterLevel > this.thirsty && this.waterLevel <= this.enough) { - return fullReplies[getRandomInt(0, fullReplies.length - 1)]; - } - if (this.waterLevel > this.enough) { - return tooMuchReplies[getRandomInt(0, tooMuchReplies.length - 1)]; - } + if (this.waterLevel <= this.thirsty) + return this.i18nService.t("needMoreWater", this.lang, "waterMe"); + if (this.waterLevel <= this.full) + return this.i18nService.t("enoughWater", this.lang, "waterMe"); + return this.i18nService.t("tooMuchWater", this.lang, "waterMe"); } async notifyIfThirsty() { if (this.waterLevel <= this.thirsty) { await this.messagesService.sendToChannel( { id: "channelId" }, - { content: "ich brauche wasser :(" }, + { content: await this.i18nService.t("waterMe.thirsty", this.lang) }, ); } } @@ -66,11 +64,11 @@ export class WaterMeService { } async handleCommand(channelId: string) { - const result = this.waterMe(); + const result = this.waterMe(); // muss hier await weil promise rueckgabe? this.messagesService.sendToChannel( { id: channelId }, - { content: result.reply }, + { content: await result.reply }, ); } } diff --git a/core/src/lib/i18n/i18n.service.ts b/core/src/lib/i18n/i18n.service.ts new file mode 100644 index 0000000..effa1cc --- /dev/null +++ b/core/src/lib/i18n/i18n.service.ts @@ -0,0 +1,64 @@ +import i18next, { type ParseKeys } from "i18next"; +// import { createLogger } from "lib/logger"; +import waterMeDe from "locales/de/water-me"; +import waterMeEn from "locales/en/water-me"; + +export class I18nService { + // private logger = createLogger("I18nService"); + private initialized: Promise; + + constructor() { + this.initialized = this.init(); + // this.logger.debug(`language ${lang}`); + } + + randomVariantPlugin = { + type: "postProcessor" as const, + name: "randomVariant", + process(value: string) { + return value; + }, + }; + + async init() { + await i18next.init({ + lng: "en", + fallbackLng: "en", + resources: { + en: { waterMe: waterMeEn }, + de: { waterMe: waterMeDe }, + }, + }); + } + + randomItem(arr: T[]): T { + return arr[Math.floor(Math.random() * arr.length)]; + } + + async t( + key: ParseKeys, + locale: string, + ns: string, + interpolations?: Record, + ): Promise { + await this.initialized; // wait for init before translating + const fixedT = i18next.getFixedT( + locale.startsWith("de") ? "de" : "en", + ns ?? "waterMe", + ); + const value = fixedT(key, { returnObjects: true, ...interpolations }); + + if (Array.isArray(value)) { + // pick random variant and interpolate manually if needed + const picked = this.randomItem(value as string[]); + // i18next won't interpolate when returnObjects is true, so we do it here + return picked.replace(/\{\{(\w+)\}\}/g, (_, k) => + interpolations?.[k] !== undefined + ? String(interpolations[k]) + : `{{${k}}}`, + ); + } + + return value as unknown as string; + } +} diff --git a/core/src/lib/i18n/index.ts b/core/src/lib/i18n/index.ts new file mode 100644 index 0000000..e1f028b --- /dev/null +++ b/core/src/lib/i18n/index.ts @@ -0,0 +1,3 @@ +import { I18nService } from "lib/i18n/i18n.service"; + +export const i18nService = new I18nService(); diff --git a/core/src/locales/de/water-me.ts b/core/src/locales/de/water-me.ts new file mode 100644 index 0000000..bc8ceb0 --- /dev/null +++ b/core/src/locales/de/water-me.ts @@ -0,0 +1,10 @@ +export default { + needMoreWater: [ + "... wow, das wars??? ich brauche noch mehr wasser :(", + "danke!!!! ich bin schon fast verdurstet :(((", + "*roooeeeelps*", + ], + enoughWater: ["wow, das hat gut getan", "das ist krass :3"], + tooMuchWater: ["ES REICHT!!!!", "bitte hoer auf, ich platze gleich :("], + thirsty: "ich brauche wasser :c", +} as const; diff --git a/core/src/locales/en/water-me.ts b/core/src/locales/en/water-me.ts new file mode 100644 index 0000000..37f776d --- /dev/null +++ b/core/src/locales/en/water-me.ts @@ -0,0 +1,10 @@ +export default { + needMoreWater: [ + "... wow, thats all??? i need more water :(", + "thank you!!!! i was almost dying of thirst :(((", + "*buuuurrrrp*", + ], + enoughWater: ["wow, that felt good", "that's crazy :3"], + tooMuchWater: ["THATS ENOUGH!!!!", "please stop, i'm about to explode :("], + thirsty: "i need some water :c", +} as const; diff --git a/core/src/locales/index.ts b/core/src/locales/index.ts new file mode 100644 index 0000000..629e68a --- /dev/null +++ b/core/src/locales/index.ts @@ -0,0 +1,9 @@ +import de from "./de"; +import en from "./en"; + +const locales = { + de, + en, +} as const; + +export default locales; diff --git a/core/tsconfig.json b/core/tsconfig.json index 3753d85..352bca5 100644 --- a/core/tsconfig.json +++ b/core/tsconfig.json @@ -17,11 +17,12 @@ // Best practices "strict": true, "skipLibCheck": true, + "strictNullChecks": true, "noFallthroughCasesInSwitch": true, // Some stricter flags (disabled by default) "noUnusedLocals": false, "noUnusedParameters": false, "noPropertyAccessFromIndexSignature": false - } + }, }