mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:01:08 +02:00
✨ feat(zitare): add multilingual support and expanded quote metadata
- Add 6-language support: original, de, en, it, fr, es - Add quote metadata: source, year, tags, imageUrl, authorBio, verified - Add originalLanguage field to preserve original quote language (la, el, zh, sa, etc.) - Update all 50 quotes with full translations and metadata - Add new utility functions: getQuoteText, getQuotesByTag, getAllTags, getQuotesByAuthor, getVerifiedQuotes, getQuotesByYearRange, getQuotesByOriginalLanguage - Update matrix-zitare-bot to use new multilingual schema
This commit is contained in:
parent
d961508a81
commit
742aa0e046
6 changed files with 987 additions and 79 deletions
|
|
@ -1,5 +1,12 @@
|
|||
// Types
|
||||
export type { Quote } from './types';
|
||||
export type {
|
||||
Quote,
|
||||
TranslatedText,
|
||||
AuthorBio,
|
||||
SupportedLanguage,
|
||||
OriginalLanguage,
|
||||
} from './types';
|
||||
export { SUPPORTED_LANGUAGES, ORIGINAL_LANGUAGES } from './types';
|
||||
export type { Category } from './categories';
|
||||
|
||||
// Data
|
||||
|
|
@ -17,9 +24,16 @@ export {
|
|||
getQuoteByIndex,
|
||||
getAllCategories,
|
||||
getCategoryByName,
|
||||
getQuoteText,
|
||||
formatQuote,
|
||||
formatQuoteWithNumber,
|
||||
getTotalCount,
|
||||
getQuotesByTag,
|
||||
getAllTags,
|
||||
getQuotesByAuthor,
|
||||
getVerifiedQuotes,
|
||||
getQuotesByYearRange,
|
||||
getQuotesByOriginalLanguage,
|
||||
} from './utils';
|
||||
|
||||
export { getCategoryLabel, isValidCategory } from './categories';
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +1,107 @@
|
|||
import type { Category } from './categories';
|
||||
|
||||
/**
|
||||
* A quote with author and category
|
||||
* Supported languages for quote translations
|
||||
*/
|
||||
export const SUPPORTED_LANGUAGES = ['original', 'de', 'en', 'it', 'fr', 'es'] as const;
|
||||
export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
|
||||
|
||||
/**
|
||||
* Original language of a quote
|
||||
*/
|
||||
export const ORIGINAL_LANGUAGES = [
|
||||
'de', // German
|
||||
'en', // English
|
||||
'fr', // French
|
||||
'es', // Spanish
|
||||
'it', // Italian
|
||||
'la', // Latin
|
||||
'el', // Greek (ancient & modern)
|
||||
'zh', // Chinese
|
||||
'sa', // Sanskrit
|
||||
'ar', // Arabic
|
||||
'fa', // Persian
|
||||
'ja', // Japanese
|
||||
'ru', // Russian
|
||||
'pt', // Portuguese
|
||||
'nl', // Dutch
|
||||
'da', // Danish
|
||||
'hi', // Hindi
|
||||
'bn', // Bengali
|
||||
] as const;
|
||||
export type OriginalLanguage = (typeof ORIGINAL_LANGUAGES)[number];
|
||||
|
||||
/**
|
||||
* Translated text object
|
||||
*/
|
||||
export interface TranslatedText {
|
||||
/** Original language text */
|
||||
original: string;
|
||||
/** German translation */
|
||||
de: string;
|
||||
/** English translation */
|
||||
en: string;
|
||||
/** Italian translation */
|
||||
it: string;
|
||||
/** French translation */
|
||||
fr: string;
|
||||
/** Spanish translation */
|
||||
es: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Author biography in multiple languages
|
||||
*/
|
||||
export interface AuthorBio {
|
||||
de?: string;
|
||||
en?: string;
|
||||
it?: string;
|
||||
fr?: string;
|
||||
es?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A quote with author, translations, and metadata
|
||||
*/
|
||||
export interface Quote {
|
||||
/** Unique identifier (e.g., 'mot-1', 'weis-2') */
|
||||
id: string;
|
||||
/** The quote text in German */
|
||||
text: string;
|
||||
|
||||
/** Quote text in all supported languages */
|
||||
text: TranslatedText;
|
||||
|
||||
/** Author name */
|
||||
author: string;
|
||||
|
||||
/** Category for filtering */
|
||||
category: Category;
|
||||
|
||||
/** Original language of the quote */
|
||||
originalLanguage: OriginalLanguage;
|
||||
|
||||
/** Source: book, speech, interview, letter, etc. */
|
||||
source?: string;
|
||||
|
||||
/** Year the quote was made/published */
|
||||
year?: number;
|
||||
|
||||
/** Additional tags for search/filtering */
|
||||
tags?: string[];
|
||||
|
||||
/** URL to author image */
|
||||
imageUrl?: string;
|
||||
|
||||
/** Short author biography */
|
||||
authorBio?: AuthorBio;
|
||||
|
||||
/** Whether the quote source has been verified */
|
||||
verified?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper type for creating quotes with partial translations
|
||||
* (translations can be added incrementally)
|
||||
*/
|
||||
export type PartialQuote = Omit<Quote, 'text'> & {
|
||||
text: Partial<TranslatedText> & { original: string; de: string };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { QUOTES } from './quotes';
|
||||
import { CATEGORIES, CATEGORY_LABELS, type Category } from './categories';
|
||||
import type { Quote } from './types';
|
||||
import type { Quote, SupportedLanguage } from './types';
|
||||
|
||||
/**
|
||||
* Get a random quote
|
||||
|
|
@ -38,14 +38,14 @@ export function getRandomQuoteByCategory(category: Category): Quote | null {
|
|||
}
|
||||
|
||||
/**
|
||||
* Search quotes by text or author
|
||||
* Search quotes by text or author (searches in specified language, defaults to German)
|
||||
*/
|
||||
export function searchQuotes(searchText: string): Quote[] {
|
||||
export function searchQuotes(searchText: string, language: SupportedLanguage = 'de'): Quote[] {
|
||||
const lowerSearch = searchText.toLowerCase();
|
||||
return QUOTES.filter(
|
||||
(q) =>
|
||||
q.text.toLowerCase().includes(lowerSearch) || q.author.toLowerCase().includes(lowerSearch)
|
||||
);
|
||||
return QUOTES.filter((q) => {
|
||||
const text = language === 'original' ? q.text.original : q.text[language];
|
||||
return text.toLowerCase().includes(lowerSearch) || q.author.toLowerCase().includes(lowerSearch);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -98,20 +98,36 @@ export function getCategoryByName(name: string): Category | null {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get quote text in a specific language
|
||||
*/
|
||||
export function getQuoteText(quote: Quote, language: SupportedLanguage = 'de'): string {
|
||||
if (language === 'original') {
|
||||
return quote.text.original;
|
||||
}
|
||||
return quote.text[language];
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a quote for display
|
||||
*/
|
||||
export function formatQuote(quote: Quote): string {
|
||||
export function formatQuote(quote: Quote, language: SupportedLanguage = 'de'): string {
|
||||
const text = getQuoteText(quote, language);
|
||||
const categoryLabel = CATEGORY_LABELS[quote.category];
|
||||
return `"${quote.text}"\n\n— *${quote.author}*\n\n[${categoryLabel}]`;
|
||||
return `"${text}"\n\n— *${quote.author}*\n\n[${categoryLabel}]`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a quote with number
|
||||
*/
|
||||
export function formatQuoteWithNumber(quote: Quote, number: number): string {
|
||||
export function formatQuoteWithNumber(
|
||||
quote: Quote,
|
||||
number: number,
|
||||
language: SupportedLanguage = 'de'
|
||||
): string {
|
||||
const text = getQuoteText(quote, language);
|
||||
const categoryLabel = CATEGORY_LABELS[quote.category];
|
||||
return `**#${number}**\n"${quote.text}"\n\n— *${quote.author}* [${categoryLabel}]`;
|
||||
return `**#${number}**\n"${text}"\n\n— *${quote.author}* [${categoryLabel}]`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -121,6 +137,52 @@ export function getTotalCount(): number {
|
|||
return QUOTES.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get quotes by tag
|
||||
*/
|
||||
export function getQuotesByTag(tag: string): Quote[] {
|
||||
const lowerTag = tag.toLowerCase();
|
||||
return QUOTES.filter((q) => q.tags?.some((t) => t.toLowerCase() === lowerTag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all unique tags
|
||||
*/
|
||||
export function getAllTags(): string[] {
|
||||
const tags = new Set<string>();
|
||||
QUOTES.forEach((q) => q.tags?.forEach((t) => tags.add(t)));
|
||||
return Array.from(tags).sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get quotes by author
|
||||
*/
|
||||
export function getQuotesByAuthor(author: string): Quote[] {
|
||||
const lowerAuthor = author.toLowerCase();
|
||||
return QUOTES.filter((q) => q.author.toLowerCase().includes(lowerAuthor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get verified quotes only
|
||||
*/
|
||||
export function getVerifiedQuotes(): Quote[] {
|
||||
return QUOTES.filter((q) => q.verified === true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get quotes by year range
|
||||
*/
|
||||
export function getQuotesByYearRange(startYear: number, endYear: number): Quote[] {
|
||||
return QUOTES.filter((q) => q.year !== undefined && q.year >= startYear && q.year <= endYear);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get quotes by original language
|
||||
*/
|
||||
export function getQuotesByOriginalLanguage(language: string): Quote[] {
|
||||
return QUOTES.filter((q) => q.originalLanguage === language);
|
||||
}
|
||||
|
||||
// Helper function
|
||||
function hashString(str: string): number {
|
||||
let hash = 0;
|
||||
|
|
|
|||
|
|
@ -295,7 +295,8 @@ Sag "hilfe" fuer alle Befehle!`;
|
|||
const maxResults = Math.min(results.length, 5);
|
||||
for (let i = 0; i < maxResults; i++) {
|
||||
const quote = results[i];
|
||||
text += `**${i + 1}.** "${quote.text.substring(0, 80)}${quote.text.length > 80 ? '...' : ''}"\n-- *${quote.author}*\n\n`;
|
||||
const quoteText = this.quotesService.getQuoteText(quote);
|
||||
text += `**${i + 1}.** "${quoteText.substring(0, 80)}${quoteText.length > 80 ? '...' : ''}"\n-- *${quote.author}*\n\n`;
|
||||
}
|
||||
|
||||
if (results.length > 5) {
|
||||
|
|
@ -405,9 +406,10 @@ Sag "hilfe" fuer alle Befehle!`;
|
|||
try {
|
||||
await this.zitareService.addFavorite(lastQuoteId, token);
|
||||
const quote = this.quotesService.getQuoteById(lastQuoteId);
|
||||
const quoteText = quote ? this.quotesService.getQuoteText(quote) : '';
|
||||
await this.sendMessage(
|
||||
roomId,
|
||||
`Zu Favoriten hinzugefuegt!\n\n"${quote?.text.substring(0, 50)}..."`
|
||||
`Zu Favoriten hinzugefuegt!\n\n"${quoteText.substring(0, 50)}..."`
|
||||
);
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
|
|
@ -439,7 +441,8 @@ Sag "hilfe" fuer alle Befehle!`;
|
|||
const fav = favorites[i];
|
||||
const quote = this.quotesService.getQuoteById(fav.quoteId);
|
||||
if (quote) {
|
||||
text += `**${i + 1}.** "${quote.text.substring(0, 60)}${quote.text.length > 60 ? '...' : ''}"\n-- *${quote.author}*\n\n`;
|
||||
const quoteText = this.quotesService.getQuoteText(quote);
|
||||
text += `**${i + 1}.** "${quoteText.substring(0, 60)}${quoteText.length > 60 ? '...' : ''}"\n-- *${quote.author}*\n\n`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -557,9 +560,10 @@ Sag "hilfe" fuer alle Befehle!`;
|
|||
await this.zitareService.addQuoteToList(list.id, lastQuoteId, token);
|
||||
|
||||
const quote = this.quotesService.getQuoteById(lastQuoteId);
|
||||
const quoteText = quote ? this.quotesService.getQuoteText(quote) : '';
|
||||
await this.sendMessage(
|
||||
roomId,
|
||||
`Zitat zu "${list.name}" hinzugefuegt!\n\n"${quote?.text.substring(0, 50)}..."`
|
||||
`Zitat zu "${list.name}" hinzugefuegt!\n\n"${quoteText.substring(0, 50)}..."`
|
||||
);
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||
import {
|
||||
type Quote,
|
||||
type Category,
|
||||
type SupportedLanguage,
|
||||
QUOTES,
|
||||
CATEGORIES,
|
||||
CATEGORY_LABELS,
|
||||
|
|
@ -14,6 +15,7 @@ import {
|
|||
getQuoteByIndex,
|
||||
getAllCategories,
|
||||
getCategoryByName,
|
||||
getQuoteText,
|
||||
formatQuote,
|
||||
formatQuoteWithNumber,
|
||||
getTotalCount,
|
||||
|
|
@ -38,7 +40,8 @@ export class QuotesService {
|
|||
|
||||
const quote = getDailyQuote();
|
||||
this.dailyQuoteCache = { date: today, quote };
|
||||
this.logger.log(`Daily quote for ${today}: "${quote.text.substring(0, 30)}..."`);
|
||||
const text = getQuoteText(quote, 'de');
|
||||
this.logger.log(`Daily quote for ${today}: "${text.substring(0, 30)}..."`);
|
||||
|
||||
return quote;
|
||||
}
|
||||
|
|
@ -75,11 +78,15 @@ export class QuotesService {
|
|||
return getTotalCount();
|
||||
}
|
||||
|
||||
formatQuote(quote: Quote): string {
|
||||
return formatQuote(quote);
|
||||
getQuoteText(quote: Quote, language: SupportedLanguage = 'de'): string {
|
||||
return getQuoteText(quote, language);
|
||||
}
|
||||
|
||||
formatQuoteWithNumber(quote: Quote, number: number): string {
|
||||
return formatQuoteWithNumber(quote, number);
|
||||
formatQuote(quote: Quote, language: SupportedLanguage = 'de'): string {
|
||||
return formatQuote(quote, language);
|
||||
}
|
||||
|
||||
formatQuoteWithNumber(quote: Quote, number: number, language: SupportedLanguage = 'de'): string {
|
||||
return formatQuoteWithNumber(quote, number, language);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue