mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:01:08 +02:00
✨ feat(matrix-bots): add i18n system and direct message fallback
- Add I18nService with per-user language preferences (de/en) - Add !language/!sprache command to all 4 bots (todo, calendar, contacts, clock) - Add fallback behavior: messages without commands create tasks/events/contacts/timers - Improve clock bot duration parsing to accept bare numbers as minutes (e.g. "25" = 25min) - Add support for more duration formats: "25 minuten", "1 stunde", etc. Language preferences stored in SessionService, default configurable via BOT_DEFAULT_LANGUAGE env var.
This commit is contained in:
parent
5c688d713e
commit
c2c80efc50
17 changed files with 1626 additions and 18 deletions
|
|
@ -38,6 +38,10 @@
|
|||
"types": "./dist/credit/index.d.ts",
|
||||
"default": "./dist/credit/index.js"
|
||||
},
|
||||
"./i18n": {
|
||||
"types": "./dist/i18n/index.d.ts",
|
||||
"default": "./dist/i18n/index.js"
|
||||
},
|
||||
"./nutrition": {
|
||||
"types": "./dist/nutrition/index.d.ts",
|
||||
"default": "./dist/nutrition/index.js"
|
||||
|
|
|
|||
68
packages/bot-services/src/i18n/i18n.module.ts
Normal file
68
packages/bot-services/src/i18n/i18n.module.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import { Module, DynamicModule, Global } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { I18nService, I18N_OPTIONS } from './i18n.service';
|
||||
import { I18nOptions } from './types';
|
||||
import { SessionModule } from '../session/session.module';
|
||||
|
||||
/**
|
||||
* I18n Module for Matrix Bots
|
||||
*
|
||||
* Provides multi-language support with per-user language preferences.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Basic usage (uses SessionModule and ConfigModule)
|
||||
* @Module({
|
||||
* imports: [I18nModule.forRoot()],
|
||||
* })
|
||||
*
|
||||
* // With custom default language
|
||||
* @Module({
|
||||
* imports: [I18nModule.forRoot({ defaultLanguage: 'en' })],
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
@Global()
|
||||
@Module({})
|
||||
export class I18nModule {
|
||||
/**
|
||||
* Register the I18n module
|
||||
*/
|
||||
static forRoot(options?: I18nOptions): DynamicModule {
|
||||
return {
|
||||
module: I18nModule,
|
||||
imports: [ConfigModule, SessionModule.forRoot()],
|
||||
providers: [
|
||||
{
|
||||
provide: I18N_OPTIONS,
|
||||
useValue: options || {},
|
||||
},
|
||||
I18nService,
|
||||
],
|
||||
exports: [I18nService],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the I18n module with async configuration
|
||||
*/
|
||||
static forRootAsync(options: {
|
||||
imports?: any[];
|
||||
useFactory: (...args: any[]) => I18nOptions | Promise<I18nOptions>;
|
||||
inject?: any[];
|
||||
}): DynamicModule {
|
||||
return {
|
||||
module: I18nModule,
|
||||
imports: [...(options.imports || []), ConfigModule, SessionModule.forRoot()],
|
||||
providers: [
|
||||
{
|
||||
provide: I18N_OPTIONS,
|
||||
useFactory: options.useFactory,
|
||||
inject: options.inject || [],
|
||||
},
|
||||
I18nService,
|
||||
],
|
||||
exports: [I18nService],
|
||||
};
|
||||
}
|
||||
}
|
||||
242
packages/bot-services/src/i18n/i18n.service.ts
Normal file
242
packages/bot-services/src/i18n/i18n.service.ts
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
import { Injectable, Inject, Optional, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import {
|
||||
Language,
|
||||
BotTranslations,
|
||||
TodoTranslations,
|
||||
CalendarTranslations,
|
||||
ContactsTranslations,
|
||||
ClockTranslations,
|
||||
I18nOptions,
|
||||
} from './types';
|
||||
import { de } from './locales/de';
|
||||
import { en } from './locales/en';
|
||||
import { SessionService } from '../session/session.service';
|
||||
|
||||
/**
|
||||
* Injection token for I18n options
|
||||
*/
|
||||
export const I18N_OPTIONS = 'I18N_OPTIONS';
|
||||
|
||||
/**
|
||||
* Session data key for language preference
|
||||
*/
|
||||
const LANGUAGE_KEY = 'language';
|
||||
|
||||
/**
|
||||
* All available translations
|
||||
*/
|
||||
const translations: Record<Language, BotTranslations> = { de, en };
|
||||
|
||||
/**
|
||||
* Language display names
|
||||
*/
|
||||
export const LANGUAGE_NAMES: Record<Language, string> = {
|
||||
de: 'Deutsch',
|
||||
en: 'English',
|
||||
};
|
||||
|
||||
/**
|
||||
* I18n Service for Matrix Bots
|
||||
*
|
||||
* Provides multi-language support with:
|
||||
* - Per-user language preference (stored in session)
|
||||
* - Default language from environment variable
|
||||
* - Placeholder substitution in translations
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Get translator for a user
|
||||
* const t = await i18n.getTranslator(userId, 'todo');
|
||||
*
|
||||
* // Use translations
|
||||
* const msg = t('taskCreated', { title: 'Buy milk' });
|
||||
* // → "Aufgabe erstellt: **Buy milk**" (if user language is German)
|
||||
* ```
|
||||
*/
|
||||
@Injectable()
|
||||
export class I18nService {
|
||||
private readonly logger = new Logger(I18nService.name);
|
||||
private readonly defaultLanguage: Language;
|
||||
|
||||
constructor(
|
||||
@Optional() private sessionService?: SessionService,
|
||||
@Optional() private configService?: ConfigService,
|
||||
@Optional() @Inject(I18N_OPTIONS) private options?: I18nOptions
|
||||
) {
|
||||
// Priority: options > env > config > 'de'
|
||||
this.defaultLanguage =
|
||||
options?.defaultLanguage ||
|
||||
(process.env.BOT_DEFAULT_LANGUAGE as Language) ||
|
||||
this.configService?.get<Language>('bot.defaultLanguage') ||
|
||||
'de';
|
||||
|
||||
this.logger.log(`Default language: ${this.defaultLanguage}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the language for a user
|
||||
*/
|
||||
async getLanguage(userId: string): Promise<Language> {
|
||||
if (this.sessionService) {
|
||||
const lang = await this.sessionService.getSessionData<Language>(userId, LANGUAGE_KEY);
|
||||
if (lang && this.isValidLanguage(lang)) {
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
return this.defaultLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the language for a user
|
||||
*/
|
||||
async setLanguage(userId: string, language: Language): Promise<void> {
|
||||
if (!this.isValidLanguage(language)) {
|
||||
throw new Error(
|
||||
`Invalid language: ${language}. Available: ${this.getAvailableLanguages().join(', ')}`
|
||||
);
|
||||
}
|
||||
if (this.sessionService) {
|
||||
await this.sessionService.setSessionData(userId, LANGUAGE_KEY, language);
|
||||
this.logger.log(`Language set for ${userId}: ${language}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a language code is valid
|
||||
*/
|
||||
isValidLanguage(lang: string): lang is Language {
|
||||
return lang === 'de' || lang === 'en';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of available languages
|
||||
*/
|
||||
getAvailableLanguages(): Language[] {
|
||||
return ['de', 'en'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get language display name
|
||||
*/
|
||||
getLanguageName(lang: Language): string {
|
||||
return LANGUAGE_NAMES[lang];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all translations for a language
|
||||
*/
|
||||
getTranslations(language: Language): BotTranslations {
|
||||
return translations[language] || translations[this.defaultLanguage];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a translator function for todo bot
|
||||
*/
|
||||
async getTodoTranslator(
|
||||
userId: string
|
||||
): Promise<(key: keyof TodoTranslations, params?: Record<string, string | number>) => string> {
|
||||
const lang = await this.getLanguage(userId);
|
||||
const t = translations[lang].todo;
|
||||
return (key, params) => this.interpolate(t[key], params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a translator function for calendar bot
|
||||
*/
|
||||
async getCalendarTranslator(
|
||||
userId: string
|
||||
): Promise<
|
||||
(key: keyof CalendarTranslations, params?: Record<string, string | number>) => string
|
||||
> {
|
||||
const lang = await this.getLanguage(userId);
|
||||
const t = translations[lang].calendar;
|
||||
return (key, params) => this.interpolate(t[key], params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a translator function for contacts bot
|
||||
*/
|
||||
async getContactsTranslator(
|
||||
userId: string
|
||||
): Promise<
|
||||
(key: keyof ContactsTranslations, params?: Record<string, string | number>) => string
|
||||
> {
|
||||
const lang = await this.getLanguage(userId);
|
||||
const t = translations[lang].contacts;
|
||||
return (key, params) => this.interpolate(t[key], params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a translator function for clock bot
|
||||
*/
|
||||
async getClockTranslator(
|
||||
userId: string
|
||||
): Promise<(key: keyof ClockTranslations, params?: Record<string, string | number>) => string> {
|
||||
const lang = await this.getLanguage(userId);
|
||||
const t = translations[lang].clock;
|
||||
return (key, params) => this.interpolate(t[key], params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get translations directly for a bot type
|
||||
*/
|
||||
async getTodoTranslations(userId: string): Promise<TodoTranslations> {
|
||||
const lang = await this.getLanguage(userId);
|
||||
return translations[lang].todo;
|
||||
}
|
||||
|
||||
async getCalendarTranslations(userId: string): Promise<CalendarTranslations> {
|
||||
const lang = await this.getLanguage(userId);
|
||||
return translations[lang].calendar;
|
||||
}
|
||||
|
||||
async getContactsTranslations(userId: string): Promise<ContactsTranslations> {
|
||||
const lang = await this.getLanguage(userId);
|
||||
return translations[lang].contacts;
|
||||
}
|
||||
|
||||
async getClockTranslations(userId: string): Promise<ClockTranslations> {
|
||||
const lang = await this.getLanguage(userId);
|
||||
return translations[lang].clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolate placeholders in a string
|
||||
*
|
||||
* @example
|
||||
* interpolate('Hello {name}!', { name: 'World' })
|
||||
* // → 'Hello World!'
|
||||
*/
|
||||
interpolate(template: string, params?: Record<string, string | number>): string {
|
||||
if (!params) return template;
|
||||
return template.replace(/\{(\w+)\}/g, (_, key) => {
|
||||
return params[key]?.toString() ?? `{${key}}`;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a date according to user's language
|
||||
*/
|
||||
async formatDate(userId: string, date: Date | string): Promise<string> {
|
||||
const lang = await this.getLanguage(userId);
|
||||
const d = typeof date === 'string' ? new Date(date) : date;
|
||||
return d.toLocaleDateString(lang === 'de' ? 'de-DE' : 'en-US', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a time according to user's language
|
||||
*/
|
||||
async formatTime(userId: string, date: Date | string): Promise<string> {
|
||||
const lang = await this.getLanguage(userId);
|
||||
const d = typeof date === 'string' ? new Date(date) : date;
|
||||
return d.toLocaleTimeString(lang === 'de' ? 'de-DE' : 'en-US', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
}
|
||||
}
|
||||
5
packages/bot-services/src/i18n/index.ts
Normal file
5
packages/bot-services/src/i18n/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export * from './types';
|
||||
export * from './i18n.service';
|
||||
export * from './i18n.module';
|
||||
export { de } from './locales/de';
|
||||
export { en } from './locales/en';
|
||||
390
packages/bot-services/src/i18n/locales/de.ts
Normal file
390
packages/bot-services/src/i18n/locales/de.ts
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
import { type BotTranslations } from '../types';
|
||||
|
||||
export const de: BotTranslations = {
|
||||
common: {
|
||||
// General
|
||||
error: 'Fehler',
|
||||
errorOccurred: 'Ein Fehler ist aufgetreten. Bitte versuche es erneut.',
|
||||
notLoggedIn: 'Du bist nicht angemeldet.',
|
||||
loginRequired: 'Bitte melde dich zuerst an mit `!login email passwort`',
|
||||
loginSuccess: 'Erfolgreich angemeldet als **{email}**',
|
||||
loginFailed: 'Anmeldung fehlgeschlagen: {error}',
|
||||
logoutSuccess: 'Erfolgreich abgemeldet.',
|
||||
invalidCommand: 'Unbekannter Befehl: {command}',
|
||||
helpHint: 'Sag "hilfe" für alle Befehle.',
|
||||
|
||||
// Credits
|
||||
credits: 'Credits',
|
||||
creditsRemaining: '{amount} verbleibend',
|
||||
insufficientCredits: 'Nicht genügend Credits. Benötigt: {required}, Verfügbar: {available}',
|
||||
buyCredits: 'Credits kaufen: https://mana.how/credits',
|
||||
|
||||
// Sync
|
||||
synced: 'Synchronisiert',
|
||||
localStorage: 'Lokaler Speicher',
|
||||
|
||||
// Status
|
||||
status: 'Status',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
loggedInAs: 'Angemeldet als: {email}',
|
||||
notLoggedInStatus: 'Nicht angemeldet',
|
||||
|
||||
// Language
|
||||
languageChanged: 'Sprache geändert zu: **{language}**',
|
||||
currentLanguage: 'Aktuelle Sprache: **{language}**',
|
||||
availableLanguages: 'Verfügbare Sprachen: {languages}',
|
||||
|
||||
// Dates
|
||||
today: 'Heute',
|
||||
tomorrow: 'Morgen',
|
||||
dayAfterTomorrow: 'Übermorgen',
|
||||
|
||||
// Actions
|
||||
created: 'Erstellt',
|
||||
deleted: 'Gelöscht',
|
||||
updated: 'Aktualisiert',
|
||||
completed: 'Erledigt',
|
||||
},
|
||||
|
||||
todo: {
|
||||
// Inherit common
|
||||
error: 'Fehler',
|
||||
errorOccurred: 'Ein Fehler ist aufgetreten. Bitte versuche es erneut.',
|
||||
notLoggedIn: 'Du bist nicht angemeldet.',
|
||||
loginRequired: 'Bitte melde dich zuerst an mit `!login email passwort`',
|
||||
loginSuccess: 'Erfolgreich angemeldet als **{email}**',
|
||||
loginFailed: 'Anmeldung fehlgeschlagen: {error}',
|
||||
logoutSuccess: 'Erfolgreich abgemeldet.',
|
||||
invalidCommand: 'Unbekannter Befehl: {command}',
|
||||
helpHint: 'Sag "hilfe" für alle Befehle.',
|
||||
credits: 'Credits',
|
||||
creditsRemaining: '{amount} verbleibend',
|
||||
insufficientCredits: 'Nicht genügend Credits. Benötigt: {required}, Verfügbar: {available}',
|
||||
buyCredits: 'Credits kaufen: https://mana.how/credits',
|
||||
synced: 'Synchronisiert',
|
||||
localStorage: 'Lokaler Speicher',
|
||||
status: 'Status',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
loggedInAs: 'Angemeldet als: {email}',
|
||||
notLoggedInStatus: 'Nicht angemeldet',
|
||||
languageChanged: 'Sprache geändert zu: **{language}**',
|
||||
currentLanguage: 'Aktuelle Sprache: **{language}**',
|
||||
availableLanguages: 'Verfügbare Sprachen: {languages}',
|
||||
today: 'Heute',
|
||||
tomorrow: 'Morgen',
|
||||
dayAfterTomorrow: 'Übermorgen',
|
||||
created: 'Erstellt',
|
||||
deleted: 'Gelöscht',
|
||||
updated: 'Aktualisiert',
|
||||
completed: 'Erledigt',
|
||||
|
||||
// Tasks
|
||||
task: 'Aufgabe',
|
||||
tasks: 'Aufgaben',
|
||||
taskCreated: 'Aufgabe erstellt: **{title}**',
|
||||
taskCompleted: 'Erledigt: ~~{title}~~',
|
||||
taskDeleted: 'Gelöscht: {title}',
|
||||
noTasks: 'Keine offenen Aufgaben.',
|
||||
noTasksToday: 'Keine Aufgaben für heute.',
|
||||
inboxEmpty: 'Inbox ist leer.',
|
||||
allTasks: 'Alle offenen Aufgaben',
|
||||
todayTasks: 'Aufgaben für heute',
|
||||
inbox: 'Inbox (ohne Datum)',
|
||||
|
||||
// Projects
|
||||
project: 'Projekt',
|
||||
projects: 'Projekte',
|
||||
noProjects: 'Keine Projekte.',
|
||||
projectTasks: 'Projekt: {name}',
|
||||
|
||||
// Priorities
|
||||
priority: 'Priorität',
|
||||
date: 'Datum',
|
||||
|
||||
// Help
|
||||
helpTitle: 'Todo Bot - Hilfe',
|
||||
helpCommands: `**Befehle:**
|
||||
• \`!add [Aufgabe]\` - Neue Aufgabe erstellen
|
||||
• \`!list\` - Alle offenen Aufgaben
|
||||
• \`!today\` - Heutige Aufgaben
|
||||
• \`!inbox\` - Aufgaben ohne Datum
|
||||
• \`!done [Nr]\` - Aufgabe als erledigt markieren
|
||||
• \`!delete [Nr]\` - Aufgabe löschen
|
||||
• \`!projects\` - Alle Projekte
|
||||
• \`!project [Name]\` - Projektaufgaben anzeigen
|
||||
• \`!status\` - Bot-Status
|
||||
• \`!language [de/en]\` - Sprache ändern`,
|
||||
helpSyntax: `**Syntax:**
|
||||
\`!add Aufgabe !p1 @morgen #projekt\`
|
||||
• \`!p1-4\` - Priorität (1=höchste)
|
||||
• \`@heute/@morgen/@übermorgen\` - Datum
|
||||
• \`#projektname\` - Projekt`,
|
||||
helpExamples: `**Beispiele:**
|
||||
• \`Einkaufen gehen\`
|
||||
• \`Meeting vorbereiten !p1 @morgen\`
|
||||
• \`Bericht schreiben #arbeit\``,
|
||||
|
||||
// Actions
|
||||
markDone: 'Erledigen: `!done [Nr]`',
|
||||
delete: 'Löschen: `!delete [Nr]`',
|
||||
},
|
||||
|
||||
calendar: {
|
||||
// Inherit common
|
||||
error: 'Fehler',
|
||||
errorOccurred: 'Ein Fehler ist aufgetreten. Bitte versuche es erneut.',
|
||||
notLoggedIn: 'Du bist nicht angemeldet.',
|
||||
loginRequired: 'Bitte melde dich zuerst an mit `!login email passwort`',
|
||||
loginSuccess: 'Erfolgreich angemeldet als **{email}**',
|
||||
loginFailed: 'Anmeldung fehlgeschlagen: {error}',
|
||||
logoutSuccess: 'Erfolgreich abgemeldet.',
|
||||
invalidCommand: 'Unbekannter Befehl: {command}',
|
||||
helpHint: 'Sag "hilfe" für alle Befehle.',
|
||||
credits: 'Credits',
|
||||
creditsRemaining: '{amount} verbleibend',
|
||||
insufficientCredits: 'Nicht genügend Credits. Benötigt: {required}, Verfügbar: {available}',
|
||||
buyCredits: 'Credits kaufen: https://mana.how/credits',
|
||||
synced: 'Synchronisiert',
|
||||
localStorage: 'Lokaler Speicher',
|
||||
status: 'Status',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
loggedInAs: 'Angemeldet als: {email}',
|
||||
notLoggedInStatus: 'Nicht angemeldet',
|
||||
languageChanged: 'Sprache geändert zu: **{language}**',
|
||||
currentLanguage: 'Aktuelle Sprache: **{language}**',
|
||||
availableLanguages: 'Verfügbare Sprachen: {languages}',
|
||||
today: 'Heute',
|
||||
tomorrow: 'Morgen',
|
||||
dayAfterTomorrow: 'Übermorgen',
|
||||
created: 'Erstellt',
|
||||
deleted: 'Gelöscht',
|
||||
updated: 'Aktualisiert',
|
||||
completed: 'Erledigt',
|
||||
|
||||
// Events
|
||||
event: 'Termin',
|
||||
events: 'Termine',
|
||||
eventCreated: 'Termin erstellt: **{title}**',
|
||||
eventDeleted: 'Gelöscht: {title}',
|
||||
noEvents: 'Keine anstehenden Termine.',
|
||||
noEventsToday: 'Keine Termine für heute.',
|
||||
noEventsTomorrow: 'Keine Termine für morgen.',
|
||||
noEventsThisWeek: 'Keine Termine diese Woche.',
|
||||
upcomingEvents: 'Anstehende Termine',
|
||||
todayEvents: 'Termine heute',
|
||||
tomorrowEvents: 'Termine morgen',
|
||||
weekEvents: 'Termine diese Woche',
|
||||
|
||||
// Calendars
|
||||
calendar: 'Kalender',
|
||||
calendars: 'Kalender',
|
||||
yourCalendars: 'Deine Kalender',
|
||||
|
||||
// Time
|
||||
time: 'Zeit',
|
||||
allDay: 'ganztägig',
|
||||
location: 'Ort',
|
||||
|
||||
// Help
|
||||
helpTitle: 'Kalender Bot - Hilfe',
|
||||
helpCommands: `**Befehle:**
|
||||
• \`!add [Termin]\` - Neuen Termin erstellen
|
||||
• \`!today\` - Heutige Termine
|
||||
• \`!tomorrow\` - Morgige Termine
|
||||
• \`!week\` - Termine diese Woche
|
||||
• \`!events\` - Nächste 14 Tage
|
||||
• \`!details [Nr]\` - Termindetails
|
||||
• \`!delete [Nr]\` - Termin löschen
|
||||
• \`!calendars\` - Alle Kalender
|
||||
• \`!status\` - Bot-Status
|
||||
• \`!language [de/en]\` - Sprache ändern`,
|
||||
helpSyntax: `**Syntax:**
|
||||
\`Meeting morgen um 14:00\`
|
||||
\`Zahnarzt am 15.02. um 10:30\`
|
||||
\`Urlaub am 01.03. ganztägig\``,
|
||||
helpExamples: `**Beispiele:**
|
||||
• \`Team Meeting morgen um 10:00\`
|
||||
• \`Arzt am 20.02. um 15:30\`
|
||||
• \`Geburtstag am 15.03. ganztägig\``,
|
||||
|
||||
// Parsing errors
|
||||
couldNotParseDateTime: 'Konnte Datum/Uhrzeit nicht erkennen.',
|
||||
pleaseProvideTitle: 'Bitte gib einen Titel für den Termin an.',
|
||||
},
|
||||
|
||||
contacts: {
|
||||
// Inherit common
|
||||
error: 'Fehler',
|
||||
errorOccurred: 'Ein Fehler ist aufgetreten. Bitte versuche es erneut.',
|
||||
notLoggedIn: 'Du bist nicht angemeldet.',
|
||||
loginRequired: 'Bitte melde dich zuerst an mit `!login email passwort`',
|
||||
loginSuccess: 'Erfolgreich angemeldet als **{email}**',
|
||||
loginFailed: 'Anmeldung fehlgeschlagen: {error}',
|
||||
logoutSuccess: 'Erfolgreich abgemeldet.',
|
||||
invalidCommand: 'Unbekannter Befehl: {command}',
|
||||
helpHint: 'Sag "hilfe" für alle Befehle.',
|
||||
credits: 'Credits',
|
||||
creditsRemaining: '{amount} verbleibend',
|
||||
insufficientCredits: 'Nicht genügend Credits. Benötigt: {required}, Verfügbar: {available}',
|
||||
buyCredits: 'Credits kaufen: https://mana.how/credits',
|
||||
synced: 'Synchronisiert',
|
||||
localStorage: 'Lokaler Speicher',
|
||||
status: 'Status',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
loggedInAs: 'Angemeldet als: {email}',
|
||||
notLoggedInStatus: 'Nicht angemeldet',
|
||||
languageChanged: 'Sprache geändert zu: **{language}**',
|
||||
currentLanguage: 'Aktuelle Sprache: **{language}**',
|
||||
availableLanguages: 'Verfügbare Sprachen: {languages}',
|
||||
today: 'Heute',
|
||||
tomorrow: 'Morgen',
|
||||
dayAfterTomorrow: 'Übermorgen',
|
||||
created: 'Erstellt',
|
||||
deleted: 'Gelöscht',
|
||||
updated: 'Aktualisiert',
|
||||
completed: 'Erledigt',
|
||||
|
||||
// Contacts
|
||||
contact: 'Kontakt',
|
||||
contacts: 'Kontakte',
|
||||
contactCreated: 'Kontakt **{name}** erstellt!',
|
||||
contactDeleted: 'Kontakt **{name}** gelöscht.',
|
||||
contactUpdated: 'Kontakt **{name}** aktualisiert!',
|
||||
noContacts: 'Du hast noch keine Kontakte.',
|
||||
|
||||
// Favorites
|
||||
favorite: 'Favorit',
|
||||
favorites: 'Favoriten',
|
||||
noFavorites: 'Du hast noch keine Favoriten.',
|
||||
markedAsFavorite: '**{name}** als Favorit markiert ★',
|
||||
removedFromFavorites: '**{name}** aus Favoriten entfernt',
|
||||
|
||||
// Search
|
||||
search: 'Suche',
|
||||
searchResults: 'Suchergebnisse für "{query}"',
|
||||
noSearchResults: 'Keine Kontakte gefunden für: "{query}"',
|
||||
|
||||
// Fields
|
||||
email: 'E-Mail',
|
||||
phone: 'Telefon',
|
||||
mobile: 'Mobil',
|
||||
company: 'Firma',
|
||||
jobTitle: 'Beruf',
|
||||
address: 'Adresse',
|
||||
website: 'Website',
|
||||
birthday: 'Geburtstag',
|
||||
notes: 'Notizen',
|
||||
|
||||
// Help
|
||||
helpTitle: 'Contacts Bot - Hilfe',
|
||||
helpCommands: `**Befehle:**
|
||||
• \`!contacts\` - Alle Kontakte
|
||||
• \`!search [text]\` - Kontakte suchen
|
||||
• \`!favorites\` - Favoriten anzeigen
|
||||
• \`!contact [Nr]\` - Kontaktdetails
|
||||
• \`!add Vorname Nachname\` - Neuer Kontakt
|
||||
• \`!edit [Nr] [feld] [wert]\` - Bearbeiten
|
||||
• \`!delete [Nr]\` - Kontakt löschen
|
||||
• \`!fav [Nr]\` - Favorit umschalten
|
||||
• \`!status\` - Bot-Status
|
||||
• \`!language [de/en]\` - Sprache ändern`,
|
||||
helpFields: `**Felder:** email, phone, mobile, company, job, website, street, city, zip, country, notes, birthday`,
|
||||
helpExamples: `**Beispiele:**
|
||||
• \`Max Mustermann\`
|
||||
• \`!edit 1 email max@example.com\`
|
||||
• \`!edit 1 phone +49 123 456789\``,
|
||||
},
|
||||
|
||||
clock: {
|
||||
// Inherit common
|
||||
error: 'Fehler',
|
||||
errorOccurred: 'Ein Fehler ist aufgetreten. Bitte versuche es erneut.',
|
||||
notLoggedIn: 'Du bist nicht angemeldet.',
|
||||
loginRequired: 'Bitte melde dich zuerst an mit `!login email passwort`',
|
||||
loginSuccess: 'Erfolgreich angemeldet als **{email}**',
|
||||
loginFailed: 'Anmeldung fehlgeschlagen: {error}',
|
||||
logoutSuccess: 'Erfolgreich abgemeldet.',
|
||||
invalidCommand: 'Unbekannter Befehl: {command}',
|
||||
helpHint: 'Sag "hilfe" für alle Befehle.',
|
||||
credits: 'Credits',
|
||||
creditsRemaining: '{amount} verbleibend',
|
||||
insufficientCredits: 'Nicht genügend Credits. Benötigt: {required}, Verfügbar: {available}',
|
||||
buyCredits: 'Credits kaufen: https://mana.how/credits',
|
||||
synced: 'Synchronisiert',
|
||||
localStorage: 'Lokaler Speicher',
|
||||
status: 'Status',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
loggedInAs: 'Angemeldet als: {email}',
|
||||
notLoggedInStatus: 'Nicht angemeldet',
|
||||
languageChanged: 'Sprache geändert zu: **{language}**',
|
||||
currentLanguage: 'Aktuelle Sprache: **{language}**',
|
||||
availableLanguages: 'Verfügbare Sprachen: {languages}',
|
||||
today: 'Heute',
|
||||
tomorrow: 'Morgen',
|
||||
dayAfterTomorrow: 'Übermorgen',
|
||||
created: 'Erstellt',
|
||||
deleted: 'Gelöscht',
|
||||
updated: 'Aktualisiert',
|
||||
completed: 'Erledigt',
|
||||
|
||||
// Timer
|
||||
timer: 'Timer',
|
||||
timerStarted: 'Timer gestartet!',
|
||||
timerPaused: 'Timer pausiert',
|
||||
timerResumed: 'Timer fortgesetzt',
|
||||
timerReset: 'Timer zurückgesetzt.',
|
||||
timerFinished: 'Timer beendet!',
|
||||
noActiveTimer: 'Kein aktiver Timer.',
|
||||
noPausedTimer: 'Kein pausierter Timer.',
|
||||
noTimers: 'Keine Timer.',
|
||||
remaining: 'Verbleibend',
|
||||
duration: 'Dauer',
|
||||
label: 'Label',
|
||||
|
||||
// Alarm
|
||||
alarm: 'Alarm',
|
||||
alarmSet: 'Alarm gestellt!',
|
||||
alarmDeleted: 'Alarm gelöscht.',
|
||||
noAlarms: 'Keine Alarme.',
|
||||
yourAlarms: 'Deine Alarme',
|
||||
|
||||
// World Clock
|
||||
worldClock: 'Weltuhr',
|
||||
worldClocks: 'Weltuhren',
|
||||
worldClockAdded: 'Weltuhr hinzugefügt: {city}',
|
||||
noWorldClocks: 'Keine Weltuhren.',
|
||||
yourWorldClocks: 'Deine Weltuhren',
|
||||
|
||||
// Time
|
||||
currentTime: 'Aktuelle Zeit',
|
||||
|
||||
// Help
|
||||
helpTitle: 'Clock Bot - Hilfe',
|
||||
helpCommands: `**Befehle:**
|
||||
• \`!timer 25m\` - Timer starten
|
||||
• \`!stop\` - Timer pausieren
|
||||
• \`!resume\` - Timer fortsetzen
|
||||
• \`!reset\` - Timer zurücksetzen
|
||||
• \`!timers\` - Alle Timer
|
||||
• \`!alarm 07:30\` - Alarm stellen
|
||||
• \`!alarms\` - Alle Alarme
|
||||
• \`!time\` - Aktuelle Zeit
|
||||
• \`!worldclock Berlin\` - Weltuhr hinzufügen
|
||||
• \`!worldclocks\` - Alle Weltuhren
|
||||
• \`!status\` - Bot-Status
|
||||
• \`!language [de/en]\` - Sprache ändern`,
|
||||
helpExamples: `**Beispiele:**
|
||||
• \`25\` (25 Minuten Timer)
|
||||
• \`1h30m\` (1,5 Stunden Timer)
|
||||
• \`!alarm 7 Uhr 30\``,
|
||||
|
||||
// Parsing errors
|
||||
couldNotParseDuration: 'Konnte Zeit nicht verstehen.',
|
||||
couldNotParseTime: 'Konnte Uhrzeit nicht verstehen.',
|
||||
},
|
||||
};
|
||||
390
packages/bot-services/src/i18n/locales/en.ts
Normal file
390
packages/bot-services/src/i18n/locales/en.ts
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
import { type BotTranslations } from '../types';
|
||||
|
||||
export const en: BotTranslations = {
|
||||
common: {
|
||||
// General
|
||||
error: 'Error',
|
||||
errorOccurred: 'An error occurred. Please try again.',
|
||||
notLoggedIn: 'You are not logged in.',
|
||||
loginRequired: 'Please log in first with `!login email password`',
|
||||
loginSuccess: 'Successfully logged in as **{email}**',
|
||||
loginFailed: 'Login failed: {error}',
|
||||
logoutSuccess: 'Successfully logged out.',
|
||||
invalidCommand: 'Unknown command: {command}',
|
||||
helpHint: 'Say "help" for all commands.',
|
||||
|
||||
// Credits
|
||||
credits: 'Credits',
|
||||
creditsRemaining: '{amount} remaining',
|
||||
insufficientCredits: 'Insufficient credits. Required: {required}, Available: {available}',
|
||||
buyCredits: 'Buy credits: https://mana.how/credits',
|
||||
|
||||
// Sync
|
||||
synced: 'Synced',
|
||||
localStorage: 'Local storage',
|
||||
|
||||
// Status
|
||||
status: 'Status',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
loggedInAs: 'Logged in as: {email}',
|
||||
notLoggedInStatus: 'Not logged in',
|
||||
|
||||
// Language
|
||||
languageChanged: 'Language changed to: **{language}**',
|
||||
currentLanguage: 'Current language: **{language}**',
|
||||
availableLanguages: 'Available languages: {languages}',
|
||||
|
||||
// Dates
|
||||
today: 'Today',
|
||||
tomorrow: 'Tomorrow',
|
||||
dayAfterTomorrow: 'Day after tomorrow',
|
||||
|
||||
// Actions
|
||||
created: 'Created',
|
||||
deleted: 'Deleted',
|
||||
updated: 'Updated',
|
||||
completed: 'Completed',
|
||||
},
|
||||
|
||||
todo: {
|
||||
// Inherit common
|
||||
error: 'Error',
|
||||
errorOccurred: 'An error occurred. Please try again.',
|
||||
notLoggedIn: 'You are not logged in.',
|
||||
loginRequired: 'Please log in first with `!login email password`',
|
||||
loginSuccess: 'Successfully logged in as **{email}**',
|
||||
loginFailed: 'Login failed: {error}',
|
||||
logoutSuccess: 'Successfully logged out.',
|
||||
invalidCommand: 'Unknown command: {command}',
|
||||
helpHint: 'Say "help" for all commands.',
|
||||
credits: 'Credits',
|
||||
creditsRemaining: '{amount} remaining',
|
||||
insufficientCredits: 'Insufficient credits. Required: {required}, Available: {available}',
|
||||
buyCredits: 'Buy credits: https://mana.how/credits',
|
||||
synced: 'Synced',
|
||||
localStorage: 'Local storage',
|
||||
status: 'Status',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
loggedInAs: 'Logged in as: {email}',
|
||||
notLoggedInStatus: 'Not logged in',
|
||||
languageChanged: 'Language changed to: **{language}**',
|
||||
currentLanguage: 'Current language: **{language}**',
|
||||
availableLanguages: 'Available languages: {languages}',
|
||||
today: 'Today',
|
||||
tomorrow: 'Tomorrow',
|
||||
dayAfterTomorrow: 'Day after tomorrow',
|
||||
created: 'Created',
|
||||
deleted: 'Deleted',
|
||||
updated: 'Updated',
|
||||
completed: 'Completed',
|
||||
|
||||
// Tasks
|
||||
task: 'Task',
|
||||
tasks: 'Tasks',
|
||||
taskCreated: 'Task created: **{title}**',
|
||||
taskCompleted: 'Completed: ~~{title}~~',
|
||||
taskDeleted: 'Deleted: {title}',
|
||||
noTasks: 'No open tasks.',
|
||||
noTasksToday: 'No tasks for today.',
|
||||
inboxEmpty: 'Inbox is empty.',
|
||||
allTasks: 'All open tasks',
|
||||
todayTasks: 'Tasks for today',
|
||||
inbox: 'Inbox (no date)',
|
||||
|
||||
// Projects
|
||||
project: 'Project',
|
||||
projects: 'Projects',
|
||||
noProjects: 'No projects.',
|
||||
projectTasks: 'Project: {name}',
|
||||
|
||||
// Priorities
|
||||
priority: 'Priority',
|
||||
date: 'Date',
|
||||
|
||||
// Help
|
||||
helpTitle: 'Todo Bot - Help',
|
||||
helpCommands: `**Commands:**
|
||||
• \`!add [task]\` - Create new task
|
||||
• \`!list\` - All open tasks
|
||||
• \`!today\` - Today's tasks
|
||||
• \`!inbox\` - Tasks without date
|
||||
• \`!done [Nr]\` - Mark task as done
|
||||
• \`!delete [Nr]\` - Delete task
|
||||
• \`!projects\` - All projects
|
||||
• \`!project [name]\` - Show project tasks
|
||||
• \`!status\` - Bot status
|
||||
• \`!language [de/en]\` - Change language`,
|
||||
helpSyntax: `**Syntax:**
|
||||
\`!add Task !p1 @tomorrow #project\`
|
||||
• \`!p1-4\` - Priority (1=highest)
|
||||
• \`@today/@tomorrow\` - Due date
|
||||
• \`#projectname\` - Project`,
|
||||
helpExamples: `**Examples:**
|
||||
• \`Go shopping\`
|
||||
• \`Prepare meeting !p1 @tomorrow\`
|
||||
• \`Write report #work\``,
|
||||
|
||||
// Actions
|
||||
markDone: 'Complete: `!done [Nr]`',
|
||||
delete: 'Delete: `!delete [Nr]`',
|
||||
},
|
||||
|
||||
calendar: {
|
||||
// Inherit common
|
||||
error: 'Error',
|
||||
errorOccurred: 'An error occurred. Please try again.',
|
||||
notLoggedIn: 'You are not logged in.',
|
||||
loginRequired: 'Please log in first with `!login email password`',
|
||||
loginSuccess: 'Successfully logged in as **{email}**',
|
||||
loginFailed: 'Login failed: {error}',
|
||||
logoutSuccess: 'Successfully logged out.',
|
||||
invalidCommand: 'Unknown command: {command}',
|
||||
helpHint: 'Say "help" for all commands.',
|
||||
credits: 'Credits',
|
||||
creditsRemaining: '{amount} remaining',
|
||||
insufficientCredits: 'Insufficient credits. Required: {required}, Available: {available}',
|
||||
buyCredits: 'Buy credits: https://mana.how/credits',
|
||||
synced: 'Synced',
|
||||
localStorage: 'Local storage',
|
||||
status: 'Status',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
loggedInAs: 'Logged in as: {email}',
|
||||
notLoggedInStatus: 'Not logged in',
|
||||
languageChanged: 'Language changed to: **{language}**',
|
||||
currentLanguage: 'Current language: **{language}**',
|
||||
availableLanguages: 'Available languages: {languages}',
|
||||
today: 'Today',
|
||||
tomorrow: 'Tomorrow',
|
||||
dayAfterTomorrow: 'Day after tomorrow',
|
||||
created: 'Created',
|
||||
deleted: 'Deleted',
|
||||
updated: 'Updated',
|
||||
completed: 'Completed',
|
||||
|
||||
// Events
|
||||
event: 'Event',
|
||||
events: 'Events',
|
||||
eventCreated: 'Event created: **{title}**',
|
||||
eventDeleted: 'Deleted: {title}',
|
||||
noEvents: 'No upcoming events.',
|
||||
noEventsToday: 'No events for today.',
|
||||
noEventsTomorrow: 'No events for tomorrow.',
|
||||
noEventsThisWeek: 'No events this week.',
|
||||
upcomingEvents: 'Upcoming events',
|
||||
todayEvents: "Today's events",
|
||||
tomorrowEvents: "Tomorrow's events",
|
||||
weekEvents: "This week's events",
|
||||
|
||||
// Calendars
|
||||
calendar: 'Calendar',
|
||||
calendars: 'Calendars',
|
||||
yourCalendars: 'Your calendars',
|
||||
|
||||
// Time
|
||||
time: 'Time',
|
||||
allDay: 'all day',
|
||||
location: 'Location',
|
||||
|
||||
// Help
|
||||
helpTitle: 'Calendar Bot - Help',
|
||||
helpCommands: `**Commands:**
|
||||
• \`!add [event]\` - Create new event
|
||||
• \`!today\` - Today's events
|
||||
• \`!tomorrow\` - Tomorrow's events
|
||||
• \`!week\` - This week's events
|
||||
• \`!events\` - Next 14 days
|
||||
• \`!details [Nr]\` - Event details
|
||||
• \`!delete [Nr]\` - Delete event
|
||||
• \`!calendars\` - All calendars
|
||||
• \`!status\` - Bot status
|
||||
• \`!language [de/en]\` - Change language`,
|
||||
helpSyntax: `**Syntax:**
|
||||
\`Meeting tomorrow at 2pm\`
|
||||
\`Dentist on 02/15 at 10:30am\`
|
||||
\`Vacation on 03/01 all day\``,
|
||||
helpExamples: `**Examples:**
|
||||
• \`Team meeting tomorrow at 10am\`
|
||||
• \`Doctor on 02/20 at 3:30pm\`
|
||||
• \`Birthday on 03/15 all day\``,
|
||||
|
||||
// Parsing errors
|
||||
couldNotParseDateTime: 'Could not parse date/time.',
|
||||
pleaseProvideTitle: 'Please provide a title for the event.',
|
||||
},
|
||||
|
||||
contacts: {
|
||||
// Inherit common
|
||||
error: 'Error',
|
||||
errorOccurred: 'An error occurred. Please try again.',
|
||||
notLoggedIn: 'You are not logged in.',
|
||||
loginRequired: 'Please log in first with `!login email password`',
|
||||
loginSuccess: 'Successfully logged in as **{email}**',
|
||||
loginFailed: 'Login failed: {error}',
|
||||
logoutSuccess: 'Successfully logged out.',
|
||||
invalidCommand: 'Unknown command: {command}',
|
||||
helpHint: 'Say "help" for all commands.',
|
||||
credits: 'Credits',
|
||||
creditsRemaining: '{amount} remaining',
|
||||
insufficientCredits: 'Insufficient credits. Required: {required}, Available: {available}',
|
||||
buyCredits: 'Buy credits: https://mana.how/credits',
|
||||
synced: 'Synced',
|
||||
localStorage: 'Local storage',
|
||||
status: 'Status',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
loggedInAs: 'Logged in as: {email}',
|
||||
notLoggedInStatus: 'Not logged in',
|
||||
languageChanged: 'Language changed to: **{language}**',
|
||||
currentLanguage: 'Current language: **{language}**',
|
||||
availableLanguages: 'Available languages: {languages}',
|
||||
today: 'Today',
|
||||
tomorrow: 'Tomorrow',
|
||||
dayAfterTomorrow: 'Day after tomorrow',
|
||||
created: 'Created',
|
||||
deleted: 'Deleted',
|
||||
updated: 'Updated',
|
||||
completed: 'Completed',
|
||||
|
||||
// Contacts
|
||||
contact: 'Contact',
|
||||
contacts: 'Contacts',
|
||||
contactCreated: 'Contact **{name}** created!',
|
||||
contactDeleted: 'Contact **{name}** deleted.',
|
||||
contactUpdated: 'Contact **{name}** updated!',
|
||||
noContacts: 'You have no contacts yet.',
|
||||
|
||||
// Favorites
|
||||
favorite: 'Favorite',
|
||||
favorites: 'Favorites',
|
||||
noFavorites: 'You have no favorites yet.',
|
||||
markedAsFavorite: '**{name}** marked as favorite ★',
|
||||
removedFromFavorites: '**{name}** removed from favorites',
|
||||
|
||||
// Search
|
||||
search: 'Search',
|
||||
searchResults: 'Search results for "{query}"',
|
||||
noSearchResults: 'No contacts found for: "{query}"',
|
||||
|
||||
// Fields
|
||||
email: 'Email',
|
||||
phone: 'Phone',
|
||||
mobile: 'Mobile',
|
||||
company: 'Company',
|
||||
jobTitle: 'Job title',
|
||||
address: 'Address',
|
||||
website: 'Website',
|
||||
birthday: 'Birthday',
|
||||
notes: 'Notes',
|
||||
|
||||
// Help
|
||||
helpTitle: 'Contacts Bot - Help',
|
||||
helpCommands: `**Commands:**
|
||||
• \`!contacts\` - All contacts
|
||||
• \`!search [text]\` - Search contacts
|
||||
• \`!favorites\` - Show favorites
|
||||
• \`!contact [Nr]\` - Contact details
|
||||
• \`!add FirstName LastName\` - New contact
|
||||
• \`!edit [Nr] [field] [value]\` - Edit
|
||||
• \`!delete [Nr]\` - Delete contact
|
||||
• \`!fav [Nr]\` - Toggle favorite
|
||||
• \`!status\` - Bot status
|
||||
• \`!language [de/en]\` - Change language`,
|
||||
helpFields: `**Fields:** email, phone, mobile, company, job, website, street, city, zip, country, notes, birthday`,
|
||||
helpExamples: `**Examples:**
|
||||
• \`John Doe\`
|
||||
• \`!edit 1 email john@example.com\`
|
||||
• \`!edit 1 phone +1 123 456 7890\``,
|
||||
},
|
||||
|
||||
clock: {
|
||||
// Inherit common
|
||||
error: 'Error',
|
||||
errorOccurred: 'An error occurred. Please try again.',
|
||||
notLoggedIn: 'You are not logged in.',
|
||||
loginRequired: 'Please log in first with `!login email password`',
|
||||
loginSuccess: 'Successfully logged in as **{email}**',
|
||||
loginFailed: 'Login failed: {error}',
|
||||
logoutSuccess: 'Successfully logged out.',
|
||||
invalidCommand: 'Unknown command: {command}',
|
||||
helpHint: 'Say "help" for all commands.',
|
||||
credits: 'Credits',
|
||||
creditsRemaining: '{amount} remaining',
|
||||
insufficientCredits: 'Insufficient credits. Required: {required}, Available: {available}',
|
||||
buyCredits: 'Buy credits: https://mana.how/credits',
|
||||
synced: 'Synced',
|
||||
localStorage: 'Local storage',
|
||||
status: 'Status',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
loggedInAs: 'Logged in as: {email}',
|
||||
notLoggedInStatus: 'Not logged in',
|
||||
languageChanged: 'Language changed to: **{language}**',
|
||||
currentLanguage: 'Current language: **{language}**',
|
||||
availableLanguages: 'Available languages: {languages}',
|
||||
today: 'Today',
|
||||
tomorrow: 'Tomorrow',
|
||||
dayAfterTomorrow: 'Day after tomorrow',
|
||||
created: 'Created',
|
||||
deleted: 'Deleted',
|
||||
updated: 'Updated',
|
||||
completed: 'Completed',
|
||||
|
||||
// Timer
|
||||
timer: 'Timer',
|
||||
timerStarted: 'Timer started!',
|
||||
timerPaused: 'Timer paused',
|
||||
timerResumed: 'Timer resumed',
|
||||
timerReset: 'Timer reset.',
|
||||
timerFinished: 'Timer finished!',
|
||||
noActiveTimer: 'No active timer.',
|
||||
noPausedTimer: 'No paused timer.',
|
||||
noTimers: 'No timers.',
|
||||
remaining: 'Remaining',
|
||||
duration: 'Duration',
|
||||
label: 'Label',
|
||||
|
||||
// Alarm
|
||||
alarm: 'Alarm',
|
||||
alarmSet: 'Alarm set!',
|
||||
alarmDeleted: 'Alarm deleted.',
|
||||
noAlarms: 'No alarms.',
|
||||
yourAlarms: 'Your alarms',
|
||||
|
||||
// World Clock
|
||||
worldClock: 'World clock',
|
||||
worldClocks: 'World clocks',
|
||||
worldClockAdded: 'World clock added: {city}',
|
||||
noWorldClocks: 'No world clocks.',
|
||||
yourWorldClocks: 'Your world clocks',
|
||||
|
||||
// Time
|
||||
currentTime: 'Current time',
|
||||
|
||||
// Help
|
||||
helpTitle: 'Clock Bot - Help',
|
||||
helpCommands: `**Commands:**
|
||||
• \`!timer 25m\` - Start timer
|
||||
• \`!stop\` - Pause timer
|
||||
• \`!resume\` - Resume timer
|
||||
• \`!reset\` - Reset timer
|
||||
• \`!timers\` - All timers
|
||||
• \`!alarm 07:30\` - Set alarm
|
||||
• \`!alarms\` - All alarms
|
||||
• \`!time\` - Current time
|
||||
• \`!worldclock Berlin\` - Add world clock
|
||||
• \`!worldclocks\` - All world clocks
|
||||
• \`!status\` - Bot status
|
||||
• \`!language [de/en]\` - Change language`,
|
||||
helpExamples: `**Examples:**
|
||||
• \`25\` (25 minute timer)
|
||||
• \`1h30m\` (1.5 hour timer)
|
||||
• \`!alarm 7:30 am\``,
|
||||
|
||||
// Parsing errors
|
||||
couldNotParseDuration: 'Could not parse duration.',
|
||||
couldNotParseTime: 'Could not parse time.',
|
||||
},
|
||||
};
|
||||
235
packages/bot-services/src/i18n/types.ts
Normal file
235
packages/bot-services/src/i18n/types.ts
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
/**
|
||||
* Supported languages
|
||||
*/
|
||||
export type Language = 'de' | 'en';
|
||||
|
||||
/**
|
||||
* Common translations shared across all bots
|
||||
*/
|
||||
export interface CommonTranslations {
|
||||
// General
|
||||
error: string;
|
||||
errorOccurred: string;
|
||||
notLoggedIn: string;
|
||||
loginRequired: string;
|
||||
loginSuccess: string;
|
||||
loginFailed: string;
|
||||
logoutSuccess: string;
|
||||
invalidCommand: string;
|
||||
helpHint: string;
|
||||
|
||||
// Credits
|
||||
credits: string;
|
||||
creditsRemaining: string;
|
||||
insufficientCredits: string;
|
||||
buyCredits: string;
|
||||
|
||||
// Sync
|
||||
synced: string;
|
||||
localStorage: string;
|
||||
|
||||
// Status
|
||||
status: string;
|
||||
online: string;
|
||||
offline: string;
|
||||
loggedInAs: string;
|
||||
notLoggedInStatus: string;
|
||||
|
||||
// Language
|
||||
languageChanged: string;
|
||||
currentLanguage: string;
|
||||
availableLanguages: string;
|
||||
|
||||
// Dates
|
||||
today: string;
|
||||
tomorrow: string;
|
||||
dayAfterTomorrow: string;
|
||||
|
||||
// Actions
|
||||
created: string;
|
||||
deleted: string;
|
||||
updated: string;
|
||||
completed: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Todo bot translations
|
||||
*/
|
||||
export interface TodoTranslations extends CommonTranslations {
|
||||
// Tasks
|
||||
task: string;
|
||||
tasks: string;
|
||||
taskCreated: string;
|
||||
taskCompleted: string;
|
||||
taskDeleted: string;
|
||||
noTasks: string;
|
||||
noTasksToday: string;
|
||||
inboxEmpty: string;
|
||||
allTasks: string;
|
||||
todayTasks: string;
|
||||
inbox: string;
|
||||
|
||||
// Projects
|
||||
project: string;
|
||||
projects: string;
|
||||
noProjects: string;
|
||||
projectTasks: string;
|
||||
|
||||
// Priorities
|
||||
priority: string;
|
||||
date: string;
|
||||
|
||||
// Help
|
||||
helpTitle: string;
|
||||
helpCommands: string;
|
||||
helpSyntax: string;
|
||||
helpExamples: string;
|
||||
|
||||
// Actions
|
||||
markDone: string;
|
||||
delete: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calendar bot translations
|
||||
*/
|
||||
export interface CalendarTranslations extends CommonTranslations {
|
||||
// Events
|
||||
event: string;
|
||||
events: string;
|
||||
eventCreated: string;
|
||||
eventDeleted: string;
|
||||
noEvents: string;
|
||||
noEventsToday: string;
|
||||
noEventsTomorrow: string;
|
||||
noEventsThisWeek: string;
|
||||
upcomingEvents: string;
|
||||
todayEvents: string;
|
||||
tomorrowEvents: string;
|
||||
weekEvents: string;
|
||||
|
||||
// Calendars
|
||||
calendar: string;
|
||||
calendars: string;
|
||||
yourCalendars: string;
|
||||
|
||||
// Time
|
||||
time: string;
|
||||
allDay: string;
|
||||
location: string;
|
||||
|
||||
// Help
|
||||
helpTitle: string;
|
||||
helpCommands: string;
|
||||
helpSyntax: string;
|
||||
helpExamples: string;
|
||||
|
||||
// Parsing errors
|
||||
couldNotParseDateTime: string;
|
||||
pleaseProvideTitle: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contacts bot translations
|
||||
*/
|
||||
export interface ContactsTranslations extends CommonTranslations {
|
||||
// Contacts
|
||||
contact: string;
|
||||
contacts: string;
|
||||
contactCreated: string;
|
||||
contactDeleted: string;
|
||||
contactUpdated: string;
|
||||
noContacts: string;
|
||||
|
||||
// Favorites
|
||||
favorite: string;
|
||||
favorites: string;
|
||||
noFavorites: string;
|
||||
markedAsFavorite: string;
|
||||
removedFromFavorites: string;
|
||||
|
||||
// Search
|
||||
search: string;
|
||||
searchResults: string;
|
||||
noSearchResults: string;
|
||||
|
||||
// Fields
|
||||
email: string;
|
||||
phone: string;
|
||||
mobile: string;
|
||||
company: string;
|
||||
jobTitle: string;
|
||||
address: string;
|
||||
website: string;
|
||||
birthday: string;
|
||||
notes: string;
|
||||
|
||||
// Help
|
||||
helpTitle: string;
|
||||
helpCommands: string;
|
||||
helpFields: string;
|
||||
helpExamples: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clock bot translations
|
||||
*/
|
||||
export interface ClockTranslations extends CommonTranslations {
|
||||
// Timer
|
||||
timer: string;
|
||||
timerStarted: string;
|
||||
timerPaused: string;
|
||||
timerResumed: string;
|
||||
timerReset: string;
|
||||
timerFinished: string;
|
||||
noActiveTimer: string;
|
||||
noPausedTimer: string;
|
||||
noTimers: string;
|
||||
remaining: string;
|
||||
duration: string;
|
||||
label: string;
|
||||
|
||||
// Alarm
|
||||
alarm: string;
|
||||
alarmSet: string;
|
||||
alarmDeleted: string;
|
||||
noAlarms: string;
|
||||
yourAlarms: string;
|
||||
|
||||
// World Clock
|
||||
worldClock: string;
|
||||
worldClocks: string;
|
||||
worldClockAdded: string;
|
||||
noWorldClocks: string;
|
||||
yourWorldClocks: string;
|
||||
|
||||
// Time
|
||||
currentTime: string;
|
||||
|
||||
// Help
|
||||
helpTitle: string;
|
||||
helpCommands: string;
|
||||
helpExamples: string;
|
||||
|
||||
// Parsing errors
|
||||
couldNotParseDuration: string;
|
||||
couldNotParseTime: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* All bot translations combined
|
||||
*/
|
||||
export interface BotTranslations {
|
||||
common: CommonTranslations;
|
||||
todo: TodoTranslations;
|
||||
calendar: CalendarTranslations;
|
||||
contacts: ContactsTranslations;
|
||||
clock: ClockTranslations;
|
||||
}
|
||||
|
||||
/**
|
||||
* I18n service options
|
||||
*/
|
||||
export interface I18nOptions {
|
||||
defaultLanguage?: Language;
|
||||
}
|
||||
|
|
@ -136,6 +136,20 @@ export type {
|
|||
CreditStatusMessage,
|
||||
} from './credit/index.js';
|
||||
|
||||
// I18n (Multi-language support for Matrix bots)
|
||||
export { I18nModule, I18nService, I18N_OPTIONS, LANGUAGE_NAMES } from './i18n/index.js';
|
||||
export type {
|
||||
Language,
|
||||
I18nOptions,
|
||||
BotTranslations,
|
||||
CommonTranslations,
|
||||
TodoTranslations,
|
||||
CalendarTranslations,
|
||||
ContactsTranslations,
|
||||
ClockTranslations,
|
||||
} from './i18n/index.js';
|
||||
export { de as deTranslations, en as enTranslations } from './i18n/index.js';
|
||||
|
||||
// ===== Placeholder Services (to be implemented) =====
|
||||
|
||||
export { NutritionModule } from './nutrition/index.js';
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
SessionModule,
|
||||
CreditModule,
|
||||
CalendarApiService,
|
||||
I18nModule,
|
||||
} from '@manacore/bot-services';
|
||||
|
||||
// Factory provider for CalendarApiService
|
||||
|
|
@ -28,6 +29,7 @@ const calendarApiServiceProvider = {
|
|||
}),
|
||||
SessionModule.forRoot({ storageMode: 'redis' }),
|
||||
CreditModule.forRoot(),
|
||||
I18nModule.forRoot(),
|
||||
],
|
||||
providers: [MatrixService, calendarApiServiceProvider],
|
||||
exports: [MatrixService],
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ import {
|
|||
CreditService,
|
||||
CalendarApiService,
|
||||
CalendarEvent as ApiCalendarEvent,
|
||||
I18nService,
|
||||
Language,
|
||||
LANGUAGE_NAMES,
|
||||
} from '@manacore/bot-services';
|
||||
import { CalendarService, CalendarEvent } from '../calendar/calendar.service';
|
||||
import { HELP_TEXT, WELCOME_TEXT, BOT_INTRODUCTION } from '../config/configuration';
|
||||
|
|
@ -43,7 +46,8 @@ export class MatrixService extends BaseMatrixService {
|
|||
private calendarService: CalendarService,
|
||||
private calendarApiService: CalendarApiService,
|
||||
private sessionService: SessionService,
|
||||
private creditService: CreditService
|
||||
private creditService: CreditService,
|
||||
private i18nService: I18nService
|
||||
) {
|
||||
super(configService);
|
||||
}
|
||||
|
|
@ -132,6 +136,9 @@ export class MatrixService extends BaseMatrixService {
|
|||
await this.executeCommand(roomId, event, sender, keywordCommand, '');
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback: treat any message as an event
|
||||
await this.handleCreateEvent(roomId, event, sender, message);
|
||||
}
|
||||
|
||||
private async executeCommand(
|
||||
|
|
@ -207,6 +214,12 @@ export class MatrixService extends BaseMatrixService {
|
|||
await this.handleLogout(roomId, event, userId);
|
||||
break;
|
||||
|
||||
case 'language':
|
||||
case 'sprache':
|
||||
case 'lang':
|
||||
await this.handleLanguage(roomId, event, userId, args);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown command - ignore silently
|
||||
break;
|
||||
|
|
@ -695,4 +708,47 @@ export class MatrixService extends BaseMatrixService {
|
|||
this.logger.error(`Failed to send welcome message: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleLanguage(
|
||||
roomId: string,
|
||||
event: MatrixRoomEvent,
|
||||
userId: string,
|
||||
args: string
|
||||
) {
|
||||
const lang = args.trim().toLowerCase();
|
||||
|
||||
if (!lang) {
|
||||
const currentLang = await this.i18nService.getLanguage(userId);
|
||||
const langName = LANGUAGE_NAMES[currentLang];
|
||||
const available = this.i18nService
|
||||
.getAvailableLanguages()
|
||||
.map((l) => `${l} (${LANGUAGE_NAMES[l]})`)
|
||||
.join(', ');
|
||||
await this.sendReply(
|
||||
roomId,
|
||||
event,
|
||||
`**Sprache / Language:** ${langName}\n\n**Verfügbar / Available:** ${available}\n\nÄndern / Change: \`!language de\` oder / or \`!language en\``
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.i18nService.isValidLanguage(lang)) {
|
||||
const available = this.i18nService.getAvailableLanguages().join(', ');
|
||||
await this.sendReply(
|
||||
roomId,
|
||||
event,
|
||||
`Unbekannte Sprache / Unknown language: ${lang}\n\nVerfügbar / Available: ${available}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.i18nService.setLanguage(userId, lang as Language);
|
||||
const langName = LANGUAGE_NAMES[lang as Language];
|
||||
|
||||
if (lang === 'de') {
|
||||
await this.sendReply(roomId, event, `Sprache geändert zu: **${langName}**`);
|
||||
} else {
|
||||
await this.sendReply(roomId, event, `Language changed to: **${langName}**`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,21 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { MatrixService } from './matrix.service';
|
||||
import { ClockModule } from '../clock/clock.module';
|
||||
import { TranscriptionModule, SessionModule, CreditModule } from '@manacore/bot-services';
|
||||
import {
|
||||
TranscriptionModule,
|
||||
SessionModule,
|
||||
CreditModule,
|
||||
I18nModule,
|
||||
} from '@manacore/bot-services';
|
||||
|
||||
@Module({
|
||||
imports: [ClockModule, TranscriptionModule.forRoot(), SessionModule.forRoot(), CreditModule.forRoot()],
|
||||
imports: [
|
||||
ClockModule,
|
||||
TranscriptionModule.forRoot(),
|
||||
SessionModule.forRoot(),
|
||||
CreditModule.forRoot(),
|
||||
I18nModule.forRoot(),
|
||||
],
|
||||
providers: [MatrixService],
|
||||
exports: [MatrixService],
|
||||
})
|
||||
|
|
|
|||
|
|
@ -8,7 +8,14 @@ import {
|
|||
COMMON_KEYWORDS,
|
||||
} from '@manacore/matrix-bot-common';
|
||||
import { ClockService } from '../clock/clock.service';
|
||||
import { TranscriptionService, SessionService, CreditService } from '@manacore/bot-services';
|
||||
import {
|
||||
TranscriptionService,
|
||||
SessionService,
|
||||
CreditService,
|
||||
I18nService,
|
||||
Language,
|
||||
LANGUAGE_NAMES,
|
||||
} from '@manacore/bot-services';
|
||||
import { HELP_TEXT, WELCOME_TEXT } from '../config/configuration';
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -30,7 +37,8 @@ export class MatrixService extends BaseMatrixService {
|
|||
private clockService: ClockService,
|
||||
private transcriptionService: TranscriptionService,
|
||||
private sessionService: SessionService,
|
||||
private creditService: CreditService
|
||||
private creditService: CreditService,
|
||||
private i18nService: I18nService
|
||||
) {
|
||||
super(configService);
|
||||
}
|
||||
|
|
@ -218,6 +226,12 @@ export class MatrixService extends BaseMatrixService {
|
|||
await this.handleWorldClocksCommand(roomId, event, userId);
|
||||
break;
|
||||
|
||||
case 'language':
|
||||
case 'sprache':
|
||||
case 'lang':
|
||||
await this.handleLanguage(roomId, event, userId, args);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Silently ignore unknown commands
|
||||
break;
|
||||
|
|
@ -676,7 +690,12 @@ export class MatrixService extends BaseMatrixService {
|
|||
}
|
||||
}
|
||||
|
||||
// No match - don't respond to random messages
|
||||
// Fallback: try to parse any message as a timer duration
|
||||
const duration = this.clockService.parseDuration(text);
|
||||
if (duration) {
|
||||
await this.handleTimerCommand(roomId, event, userId, text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private async getToken(userId: string): Promise<string | null> {
|
||||
|
|
@ -691,4 +710,47 @@ export class MatrixService extends BaseMatrixService {
|
|||
// Entwicklungs-Fallback
|
||||
return this.demoToken || null;
|
||||
}
|
||||
|
||||
private async handleLanguage(
|
||||
roomId: string,
|
||||
event: MatrixRoomEvent,
|
||||
userId: string,
|
||||
args: string
|
||||
) {
|
||||
const lang = args.trim().toLowerCase();
|
||||
|
||||
if (!lang) {
|
||||
const currentLang = await this.i18nService.getLanguage(userId);
|
||||
const langName = LANGUAGE_NAMES[currentLang];
|
||||
const available = this.i18nService
|
||||
.getAvailableLanguages()
|
||||
.map((l) => `${l} (${LANGUAGE_NAMES[l]})`)
|
||||
.join(', ');
|
||||
await this.sendReply(
|
||||
roomId,
|
||||
event,
|
||||
`**Sprache / Language:** ${langName}\n\n**Verfügbar / Available:** ${available}\n\nÄndern / Change: \`!language de\` oder / or \`!language en\``
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.i18nService.isValidLanguage(lang)) {
|
||||
const available = this.i18nService.getAvailableLanguages().join(', ');
|
||||
await this.sendReply(
|
||||
roomId,
|
||||
event,
|
||||
`Unbekannte Sprache / Unknown language: ${lang}\n\nVerfügbar / Available: ${available}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.i18nService.setLanguage(userId, lang as Language);
|
||||
const langName = LANGUAGE_NAMES[lang as Language];
|
||||
|
||||
if (lang === 'de') {
|
||||
await this.sendReply(roomId, event, `Sprache geändert zu: **${langName}**`);
|
||||
} else {
|
||||
await this.sendReply(roomId, event, `Language changed to: **${langName}**`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,27 +186,27 @@ export class ClockService {
|
|||
parseDuration(input: string): number | null {
|
||||
let totalSeconds = 0;
|
||||
|
||||
// Match hours
|
||||
const hoursMatch = input.match(/(\d+)\s*h/i);
|
||||
// Match hours: 1h, 1 h, 1 stunde, 1 stunden, 1 hour, 1 hours
|
||||
const hoursMatch = input.match(/(\d+)\s*(?:h|stunde[n]?|hour[s]?)\b/i);
|
||||
if (hoursMatch) {
|
||||
totalSeconds += parseInt(hoursMatch[1], 10) * 3600;
|
||||
}
|
||||
|
||||
// Match minutes
|
||||
const minutesMatch = input.match(/(\d+)\s*m(?:in)?/i);
|
||||
// Match minutes: 25m, 25 m, 25min, 25 min, 25 minuten, 25 minute, 25 minutes
|
||||
const minutesMatch = input.match(/(\d+)\s*(?:m|min|minute[n]?|minutes?)\b/i);
|
||||
if (minutesMatch) {
|
||||
totalSeconds += parseInt(minutesMatch[1], 10) * 60;
|
||||
}
|
||||
|
||||
// Match seconds
|
||||
const secondsMatch = input.match(/(\d+)\s*s(?:ec)?/i);
|
||||
// Match seconds: 30s, 30 s, 30sec, 30 sec, 30 sekunden, 30 seconds
|
||||
const secondsMatch = input.match(/(\d+)\s*(?:s|sec|sekunde[n]?|seconds?)\b/i);
|
||||
if (secondsMatch) {
|
||||
totalSeconds += parseInt(secondsMatch[1], 10);
|
||||
}
|
||||
|
||||
// If just a number, assume minutes
|
||||
// If just a number (with optional whitespace), assume minutes
|
||||
if (totalSeconds === 0) {
|
||||
const justNumber = input.match(/^(\d+)$/);
|
||||
const justNumber = input.trim().match(/^(\d+)$/);
|
||||
if (justNumber) {
|
||||
totalSeconds = parseInt(justNumber[1], 10) * 60;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { MatrixService } from './matrix.service';
|
||||
import { ContactsModule } from '../contacts/contacts.module';
|
||||
import { SessionModule, TranscriptionModule, CreditModule } from '@manacore/bot-services';
|
||||
import {
|
||||
SessionModule,
|
||||
TranscriptionModule,
|
||||
CreditModule,
|
||||
I18nModule,
|
||||
} from '@manacore/bot-services';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -11,6 +16,7 @@ import { SessionModule, TranscriptionModule, CreditModule } from '@manacore/bot-
|
|||
sttUrl: process.env.STT_URL || 'http://localhost:3020',
|
||||
}),
|
||||
CreditModule.forRoot(),
|
||||
I18nModule.forRoot(),
|
||||
],
|
||||
providers: [MatrixService],
|
||||
exports: [MatrixService],
|
||||
|
|
|
|||
|
|
@ -9,7 +9,14 @@ import {
|
|||
UserListMapper,
|
||||
} from '@manacore/matrix-bot-common';
|
||||
import { ContactsService, Contact } from '../contacts/contacts.service';
|
||||
import { SessionService, TranscriptionService, CreditService } from '@manacore/bot-services';
|
||||
import {
|
||||
SessionService,
|
||||
TranscriptionService,
|
||||
CreditService,
|
||||
I18nService,
|
||||
Language,
|
||||
LANGUAGE_NAMES,
|
||||
} from '@manacore/bot-services';
|
||||
import { HELP_MESSAGE } from '../config/configuration';
|
||||
|
||||
const CONTACT_CREATE_CREDITS = 0.02;
|
||||
|
|
@ -32,7 +39,8 @@ export class MatrixService extends BaseMatrixService {
|
|||
private readonly transcriptionService: TranscriptionService,
|
||||
private contactsService: ContactsService,
|
||||
private sessionService: SessionService,
|
||||
private creditService: CreditService
|
||||
private creditService: CreditService,
|
||||
private i18nService: I18nService
|
||||
) {
|
||||
super(configService);
|
||||
}
|
||||
|
|
@ -102,6 +110,10 @@ Sag "hilfe" fur alle Befehle!`;
|
|||
await this.handleCommand(roomId, event, sender, `!${detectedCommand}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback: treat any message as a new contact
|
||||
const args = message.trim().split(/\s+/);
|
||||
await this.handleCreateContact(roomId, event, sender, args);
|
||||
}
|
||||
|
||||
private async handleCommand(
|
||||
|
|
@ -188,6 +200,12 @@ Sag "hilfe" fur alle Befehle!`;
|
|||
await this.pinHelpMessage(roomId, event);
|
||||
break;
|
||||
|
||||
case 'language':
|
||||
case 'sprache':
|
||||
case 'lang':
|
||||
await this.handleLanguage(roomId, event, sender, argString);
|
||||
break;
|
||||
|
||||
default:
|
||||
await this.sendReply(
|
||||
roomId,
|
||||
|
|
@ -766,4 +784,47 @@ Sag "hilfe" fur alle Befehle!`;
|
|||
await this.sendReply(roomId, event, 'Fehler beim Pinnen der Hilfe.');
|
||||
}
|
||||
}
|
||||
|
||||
private async handleLanguage(
|
||||
roomId: string,
|
||||
event: MatrixRoomEvent,
|
||||
userId: string,
|
||||
args: string
|
||||
) {
|
||||
const lang = args.trim().toLowerCase();
|
||||
|
||||
if (!lang) {
|
||||
const currentLang = await this.i18nService.getLanguage(userId);
|
||||
const langName = LANGUAGE_NAMES[currentLang];
|
||||
const available = this.i18nService
|
||||
.getAvailableLanguages()
|
||||
.map((l) => `${l} (${LANGUAGE_NAMES[l]})`)
|
||||
.join(', ');
|
||||
await this.sendReply(
|
||||
roomId,
|
||||
event,
|
||||
`**Sprache / Language:** ${langName}\n\n**Verfügbar / Available:** ${available}\n\nÄndern / Change: \`!language de\` oder / or \`!language en\``
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.i18nService.isValidLanguage(lang)) {
|
||||
const available = this.i18nService.getAvailableLanguages().join(', ');
|
||||
await this.sendReply(
|
||||
roomId,
|
||||
event,
|
||||
`Unbekannte Sprache / Unknown language: ${lang}\n\nVerfügbar / Available: ${available}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.i18nService.setLanguage(userId, lang as Language);
|
||||
const langName = LANGUAGE_NAMES[lang as Language];
|
||||
|
||||
if (lang === 'de') {
|
||||
await this.sendReply(roomId, event, `Sprache geändert zu: **${langName}**`);
|
||||
} else {
|
||||
await this.sendReply(roomId, event, `Language changed to: **${langName}**`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
SessionModule,
|
||||
CreditModule,
|
||||
TodoApiService,
|
||||
I18nModule,
|
||||
} from '@manacore/bot-services';
|
||||
|
||||
// Factory provider for TodoApiService
|
||||
|
|
@ -26,6 +27,7 @@ const todoApiServiceProvider = {
|
|||
TranscriptionModule.forRoot(),
|
||||
SessionModule.forRoot({ storageMode: 'redis' }),
|
||||
CreditModule.forRoot(),
|
||||
I18nModule.forRoot(),
|
||||
],
|
||||
providers: [MatrixService, todoApiServiceProvider],
|
||||
exports: [MatrixService],
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ import {
|
|||
CreditService,
|
||||
TodoApiService,
|
||||
Task as ApiTask,
|
||||
I18nService,
|
||||
Language,
|
||||
LANGUAGE_NAMES,
|
||||
} from '@manacore/bot-services';
|
||||
import { HELP_TEXT, WELCOME_TEXT, BOT_INTRODUCTION } from '../config/configuration';
|
||||
|
||||
|
|
@ -44,7 +47,8 @@ export class MatrixService extends BaseMatrixService {
|
|||
private todoApiService: TodoApiService,
|
||||
private transcriptionService: TranscriptionService,
|
||||
private sessionService: SessionService,
|
||||
private creditService: CreditService
|
||||
private creditService: CreditService,
|
||||
private i18nService: I18nService
|
||||
) {
|
||||
super(configService);
|
||||
}
|
||||
|
|
@ -108,7 +112,11 @@ export class MatrixService extends BaseMatrixService {
|
|||
if (body.startsWith('!')) {
|
||||
const [command, ...args] = body.slice(1).split(' ');
|
||||
await this.executeCommand(roomId, event, userId, command.toLowerCase(), args.join(' '));
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback: treat any message as a task
|
||||
await this.handleAddTask(roomId, event, userId, body);
|
||||
} catch (error) {
|
||||
this.logger.error(`Error handling message: ${error}`);
|
||||
await this.sendReply(roomId, event, 'Ein Fehler ist aufgetreten. Bitte versuche es erneut.');
|
||||
|
|
@ -299,12 +307,64 @@ export class MatrixService extends BaseMatrixService {
|
|||
await this.handlePinHelp(roomId, event);
|
||||
break;
|
||||
|
||||
case 'language':
|
||||
case 'sprache':
|
||||
case 'lang':
|
||||
await this.handleLanguage(roomId, event, userId, args);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown command - ignore silently or send help
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async handleLanguage(
|
||||
roomId: string,
|
||||
event: MatrixRoomEvent,
|
||||
userId: string,
|
||||
args: string
|
||||
) {
|
||||
const lang = args.trim().toLowerCase();
|
||||
|
||||
// Show current language if no argument
|
||||
if (!lang) {
|
||||
const currentLang = await this.i18nService.getLanguage(userId);
|
||||
const langName = LANGUAGE_NAMES[currentLang];
|
||||
const available = this.i18nService
|
||||
.getAvailableLanguages()
|
||||
.map((l) => `${l} (${LANGUAGE_NAMES[l]})`)
|
||||
.join(', ');
|
||||
await this.sendReply(
|
||||
roomId,
|
||||
event,
|
||||
`**Sprache / Language:** ${langName}\n\n**Verfügbar / Available:** ${available}\n\nÄndern / Change: \`!language de\` oder / or \`!language en\``
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate and set language
|
||||
if (!this.i18nService.isValidLanguage(lang)) {
|
||||
const available = this.i18nService.getAvailableLanguages().join(', ');
|
||||
await this.sendReply(
|
||||
roomId,
|
||||
event,
|
||||
`Unbekannte Sprache / Unknown language: ${lang}\n\nVerfügbar / Available: ${available}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.i18nService.setLanguage(userId, lang as Language);
|
||||
const langName = LANGUAGE_NAMES[lang as Language];
|
||||
|
||||
// Respond in the new language
|
||||
if (lang === 'de') {
|
||||
await this.sendReply(roomId, event, `Sprache geändert zu: **${langName}**`);
|
||||
} else {
|
||||
await this.sendReply(roomId, event, `Language changed to: **${langName}**`);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleAddTask(
|
||||
roomId: string,
|
||||
event: MatrixRoomEvent,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue