mirror of
https://github.com/callumalpass/tasknotes.git
synced 2026-07-22 12:50:26 +00:00
feat: add complete chrono-node language support (12 languages)
Implement comprehensive NLP support for all chrono-node supported languages: ## 🌍 Complete Language Coverage - **5 new languages**: Italian (it), Dutch (nl), Portuguese (pt), Swedish (sv), Ukrainian (uk) - **12 total languages**: EN, ES, FR, DE, RU, ZH, JA, IT, NL, PT, SV, UK - All languages match complete chrono-node locale support ## 📝 Language Configurations - Researched accurate native translations for all patterns - Cultural appropriateness for status and priority terms - Grammar considerations (gender, plurals, cases where applicable) - Comprehensive coverage of date triggers, recurrence, time estimates ## 🔍 Enhanced Pattern Matching - Added Ukrainian (Cyrillic) to non-ASCII boundary handling - Proper handling for all script types (Latin, Cyrillic, Chinese, Japanese) - Multiple variations and synonyms for better recognition ## ✅ Comprehensive Testing - **51 multi-language tests** covering all 12 languages - **19 language configuration tests** validating structure and registry - All tests passing with proper pattern validation - Fixed test cases to match actual language configurations ## 🏗️ Technical Improvements - Updated language registry with all new imports - Enhanced TypeScript interfaces and type safety - Maintained backward compatibility - Zero compilation errors This completes full international NLP support matching all available chrono-node locales for comprehensive global usability.
This commit is contained in:
parent
ef1086778b
commit
5efce0d36c
9 changed files with 633 additions and 8 deletions
|
|
@ -6,6 +6,11 @@ import { deConfig } from './de';
|
|||
import { ruConfig } from './ru';
|
||||
import { zhConfig } from './zh';
|
||||
import { jaConfig } from './ja';
|
||||
import { itConfig } from './it';
|
||||
import { nlConfig } from './nl';
|
||||
import { ptConfig } from './pt';
|
||||
import { svConfig } from './sv';
|
||||
import { ukConfig } from './uk';
|
||||
|
||||
/**
|
||||
* Registry of all available language configurations
|
||||
|
|
@ -17,7 +22,12 @@ export const languageRegistry: LanguageRegistry = {
|
|||
de: deConfig,
|
||||
ru: ruConfig,
|
||||
zh: zhConfig,
|
||||
ja: jaConfig
|
||||
ja: jaConfig,
|
||||
it: itConfig,
|
||||
nl: nlConfig,
|
||||
pt: ptConfig,
|
||||
sv: svConfig,
|
||||
uk: ukConfig
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
83
src/locales/it.ts
Normal file
83
src/locales/it.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { NLPLanguageConfig } from './types';
|
||||
|
||||
/**
|
||||
* Italian language configuration for Natural Language Processing
|
||||
* Translated patterns for Italian-speaking users
|
||||
*/
|
||||
export const itConfig: NLPLanguageConfig = {
|
||||
code: 'it',
|
||||
name: 'Italiano',
|
||||
chronoLocale: 'it',
|
||||
|
||||
dateTriggers: {
|
||||
due: ['scadenza', 'entro', 'entro il', 'deve essere fatto entro', 'per il', 'termine'],
|
||||
scheduled: ['programmato per', 'programmato il', 'iniziare il', 'lavorare su', 'il', 'per']
|
||||
},
|
||||
|
||||
recurrence: {
|
||||
frequencies: {
|
||||
daily: ['giornaliero', 'giornaliera', 'quotidiano', 'quotidiana', 'ogni giorno', 'tutti i giorni', 'giornalmente'],
|
||||
weekly: ['settimanale', 'ogni settimana', 'tutte le settimane', 'settimanalmente', 'alla settimana'],
|
||||
monthly: ['mensile', 'ogni mese', 'tutti i mesi', 'mensilmente', 'al mese'],
|
||||
yearly: ['annuale', 'ogni anno', 'tutti gli anni', 'annualmente', 'all\'anno']
|
||||
},
|
||||
|
||||
every: ['ogni', 'tutti i', 'tutte le'],
|
||||
other: ['altro', 'altra', 'altri', 'altre'],
|
||||
|
||||
weekdays: {
|
||||
monday: ['lunedì'],
|
||||
tuesday: ['martedì'],
|
||||
wednesday: ['mercoledì'],
|
||||
thursday: ['giovedì'],
|
||||
friday: ['venerdì'],
|
||||
saturday: ['sabato'],
|
||||
sunday: ['domenica']
|
||||
},
|
||||
|
||||
pluralWeekdays: {
|
||||
monday: ['lunedì'],
|
||||
tuesday: ['martedì'],
|
||||
wednesday: ['mercoledì'],
|
||||
thursday: ['giovedì'],
|
||||
friday: ['venerdì'],
|
||||
saturday: ['sabati'],
|
||||
sunday: ['domeniche']
|
||||
},
|
||||
|
||||
ordinals: {
|
||||
first: ['primo', 'prima'],
|
||||
second: ['secondo', 'seconda'],
|
||||
third: ['terzo', 'terza'],
|
||||
fourth: ['quarto', 'quarta'],
|
||||
last: ['ultimo', 'ultima']
|
||||
},
|
||||
|
||||
periods: {
|
||||
day: ['giorno', 'giorni'],
|
||||
week: ['settimana', 'settimane'],
|
||||
month: ['mese', 'mesi'],
|
||||
year: ['anno', 'anni']
|
||||
}
|
||||
},
|
||||
|
||||
timeEstimate: {
|
||||
hours: ['h', 'hr', 'ore', 'ora', 'o'],
|
||||
minutes: ['m', 'min', 'minuto', 'minuti']
|
||||
},
|
||||
|
||||
fallbackStatus: {
|
||||
open: ['da fare', 'aperto', 'pendente', 'todo', 'in sospeso'],
|
||||
inProgress: ['in corso', 'in progresso', 'facendo', 'lavorando'],
|
||||
done: ['fatto', 'completato', 'finito', 'terminato', 'chiuso'],
|
||||
cancelled: ['cancellato', 'annullato', 'rimosso'],
|
||||
waiting: ['in attesa', 'aspettando', 'bloccato', 'fermo']
|
||||
},
|
||||
|
||||
fallbackPriority: {
|
||||
urgent: ['urgente', 'critico', 'critica', 'massimo', 'massima', 'prioritario', 'prioritaria'],
|
||||
high: ['alto', 'alta', 'importante', 'elevato', 'elevata'],
|
||||
normal: ['medio', 'media', 'normale', 'regolare', 'standard'],
|
||||
low: ['basso', 'bassa', 'minore', 'minimo', 'minima']
|
||||
}
|
||||
};
|
||||
83
src/locales/nl.ts
Normal file
83
src/locales/nl.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { NLPLanguageConfig } from './types';
|
||||
|
||||
/**
|
||||
* Dutch language configuration for Natural Language Processing
|
||||
* Translated patterns for Dutch-speaking users
|
||||
*/
|
||||
export const nlConfig: NLPLanguageConfig = {
|
||||
code: 'nl',
|
||||
name: 'Nederlands',
|
||||
chronoLocale: 'nl',
|
||||
|
||||
dateTriggers: {
|
||||
due: ['vervalt op', 'deadline', 'moet klaar zijn op', 'tegen', 'uiterlijk', 'voor'],
|
||||
scheduled: ['gepland voor', 'gepland op', 'beginnen op', 'werken aan', 'op', 'voor']
|
||||
},
|
||||
|
||||
recurrence: {
|
||||
frequencies: {
|
||||
daily: ['dagelijks', 'elke dag', 'alle dagen', 'per dag'],
|
||||
weekly: ['wekelijks', 'elke week', 'alle weken', 'per week'],
|
||||
monthly: ['maandelijks', 'elke maand', 'alle maanden', 'per maand'],
|
||||
yearly: ['jaarlijks', 'elk jaar', 'alle jaren', 'per jaar']
|
||||
},
|
||||
|
||||
every: ['elke', 'alle', 'iedere'],
|
||||
other: ['andere', 'ander'],
|
||||
|
||||
weekdays: {
|
||||
monday: ['maandag'],
|
||||
tuesday: ['dinsdag'],
|
||||
wednesday: ['woensdag'],
|
||||
thursday: ['donderdag'],
|
||||
friday: ['vrijdag'],
|
||||
saturday: ['zaterdag'],
|
||||
sunday: ['zondag']
|
||||
},
|
||||
|
||||
pluralWeekdays: {
|
||||
monday: ['maandagen'],
|
||||
tuesday: ['dinsdagen'],
|
||||
wednesday: ['woensdagen'],
|
||||
thursday: ['donderdagen'],
|
||||
friday: ['vrijdagen'],
|
||||
saturday: ['zaterdagen'],
|
||||
sunday: ['zondagen']
|
||||
},
|
||||
|
||||
ordinals: {
|
||||
first: ['eerste'],
|
||||
second: ['tweede'],
|
||||
third: ['derde'],
|
||||
fourth: ['vierde'],
|
||||
last: ['laatste']
|
||||
},
|
||||
|
||||
periods: {
|
||||
day: ['dag', 'dagen'],
|
||||
week: ['week', 'weken'],
|
||||
month: ['maand', 'maanden'],
|
||||
year: ['jaar', 'jaren']
|
||||
}
|
||||
},
|
||||
|
||||
timeEstimate: {
|
||||
hours: ['u', 'uur', 'uren', 'h'],
|
||||
minutes: ['m', 'min', 'minuut', 'minuten']
|
||||
},
|
||||
|
||||
fallbackStatus: {
|
||||
open: ['te doen', 'open', 'nog te doen', 'todo', 'openstaand'],
|
||||
inProgress: ['bezig', 'in behandeling', 'aan het werk', 'lopend', 'in uitvoering'],
|
||||
done: ['klaar', 'voltooid', 'gedaan', 'afgerond', 'gesloten'],
|
||||
cancelled: ['geannuleerd', 'afgezegd', 'ingetrokken'],
|
||||
waiting: ['wachtend', 'in de wacht', 'geblokkeerd', 'uitgesteld']
|
||||
},
|
||||
|
||||
fallbackPriority: {
|
||||
urgent: ['urgent', 'kritiek', 'hoogste', 'spoed', 'direct'],
|
||||
high: ['hoog', 'hoge', 'belangrijk', 'belangrijke'],
|
||||
normal: ['normaal', 'normale', 'gemiddeld', 'standaard'],
|
||||
low: ['laag', 'lage', 'klein', 'kleine', 'onbelangrijk']
|
||||
}
|
||||
};
|
||||
83
src/locales/pt.ts
Normal file
83
src/locales/pt.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { NLPLanguageConfig } from './types';
|
||||
|
||||
/**
|
||||
* Portuguese language configuration for Natural Language Processing
|
||||
* Translated patterns for Portuguese-speaking users
|
||||
*/
|
||||
export const ptConfig: NLPLanguageConfig = {
|
||||
code: 'pt',
|
||||
name: 'Português',
|
||||
chronoLocale: 'pt',
|
||||
|
||||
dateTriggers: {
|
||||
due: ['vencimento', 'prazo', 'deve estar pronto até', 'até', 'para', 'limite'],
|
||||
scheduled: ['programado para', 'agendado para', 'começar em', 'trabalhar em', 'em', 'no']
|
||||
},
|
||||
|
||||
recurrence: {
|
||||
frequencies: {
|
||||
daily: ['diário', 'diária', 'diariamente', 'todos os dias', 'cada dia', 'por dia'],
|
||||
weekly: ['semanal', 'semanalmente', 'toda semana', 'todas as semanas', 'por semana'],
|
||||
monthly: ['mensal', 'mensalmente', 'todo mês', 'todos os meses', 'por mês'],
|
||||
yearly: ['anual', 'anualmente', 'todo ano', 'todos os anos', 'por ano']
|
||||
},
|
||||
|
||||
every: ['todo', 'toda', 'todos', 'todas', 'cada'],
|
||||
other: ['outro', 'outra', 'outros', 'outras'],
|
||||
|
||||
weekdays: {
|
||||
monday: ['segunda', 'segunda-feira'],
|
||||
tuesday: ['terça', 'terça-feira'],
|
||||
wednesday: ['quarta', 'quarta-feira'],
|
||||
thursday: ['quinta', 'quinta-feira'],
|
||||
friday: ['sexta', 'sexta-feira'],
|
||||
saturday: ['sábado'],
|
||||
sunday: ['domingo']
|
||||
},
|
||||
|
||||
pluralWeekdays: {
|
||||
monday: ['segundas', 'segundas-feiras'],
|
||||
tuesday: ['terças', 'terças-feiras'],
|
||||
wednesday: ['quartas', 'quartas-feiras'],
|
||||
thursday: ['quintas', 'quintas-feiras'],
|
||||
friday: ['sextas', 'sextas-feiras'],
|
||||
saturday: ['sábados'],
|
||||
sunday: ['domingos']
|
||||
},
|
||||
|
||||
ordinals: {
|
||||
first: ['primeiro', 'primeira'],
|
||||
second: ['segundo', 'segunda'],
|
||||
third: ['terceiro', 'terceira'],
|
||||
fourth: ['quarto', 'quarta'],
|
||||
last: ['último', 'última']
|
||||
},
|
||||
|
||||
periods: {
|
||||
day: ['dia', 'dias'],
|
||||
week: ['semana', 'semanas'],
|
||||
month: ['mês', 'meses'],
|
||||
year: ['ano', 'anos']
|
||||
}
|
||||
},
|
||||
|
||||
timeEstimate: {
|
||||
hours: ['h', 'hr', 'hora', 'horas'],
|
||||
minutes: ['m', 'min', 'minuto', 'minutos']
|
||||
},
|
||||
|
||||
fallbackStatus: {
|
||||
open: ['a fazer', 'pendente', 'aberto', 'todo', 'por fazer'],
|
||||
inProgress: ['em andamento', 'em progresso', 'fazendo', 'trabalhando', 'executando'],
|
||||
done: ['feito', 'concluído', 'terminado', 'finalizado', 'completo'],
|
||||
cancelled: ['cancelado', 'anulado', 'suspenso'],
|
||||
waiting: ['aguardando', 'esperando', 'bloqueado', 'em espera']
|
||||
},
|
||||
|
||||
fallbackPriority: {
|
||||
urgent: ['urgente', 'crítico', 'crítica', 'máximo', 'máxima', 'prioritário', 'prioritária'],
|
||||
high: ['alto', 'alta', 'importante', 'elevado', 'elevada'],
|
||||
normal: ['médio', 'média', 'normal', 'regular', 'padrão'],
|
||||
low: ['baixo', 'baixa', 'menor', 'mínimo', 'mínima']
|
||||
}
|
||||
};
|
||||
83
src/locales/sv.ts
Normal file
83
src/locales/sv.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { NLPLanguageConfig } from './types';
|
||||
|
||||
/**
|
||||
* Swedish language configuration for Natural Language Processing
|
||||
* Translated patterns for Swedish-speaking users
|
||||
*/
|
||||
export const svConfig: NLPLanguageConfig = {
|
||||
code: 'sv',
|
||||
name: 'Svenska',
|
||||
chronoLocale: 'sv',
|
||||
|
||||
dateTriggers: {
|
||||
due: ['förfaller', 'deadline', 'måste vara klar', 'senast', 'till', 'innan'],
|
||||
scheduled: ['schemalagd', 'planerad för', 'börja', 'arbeta med', 'den', 'på']
|
||||
},
|
||||
|
||||
recurrence: {
|
||||
frequencies: {
|
||||
daily: ['dagligen', 'varje dag', 'alla dagar', 'per dag'],
|
||||
weekly: ['veckovis', 'varje vecka', 'alla veckor', 'per vecka'],
|
||||
monthly: ['månadsvis', 'varje månad', 'alla månader', 'per månad'],
|
||||
yearly: ['årligen', 'varje år', 'alla år', 'per år']
|
||||
},
|
||||
|
||||
every: ['varje', 'alla', 'var'],
|
||||
other: ['annan', 'annat', 'andra'],
|
||||
|
||||
weekdays: {
|
||||
monday: ['måndag'],
|
||||
tuesday: ['tisdag'],
|
||||
wednesday: ['onsdag'],
|
||||
thursday: ['torsdag'],
|
||||
friday: ['fredag'],
|
||||
saturday: ['lördag'],
|
||||
sunday: ['söndag']
|
||||
},
|
||||
|
||||
pluralWeekdays: {
|
||||
monday: ['måndagar'],
|
||||
tuesday: ['tisdagar'],
|
||||
wednesday: ['onsdagar'],
|
||||
thursday: ['torsdagar'],
|
||||
friday: ['fredagar'],
|
||||
saturday: ['lördagar'],
|
||||
sunday: ['söndagar']
|
||||
},
|
||||
|
||||
ordinals: {
|
||||
first: ['första'],
|
||||
second: ['andra'],
|
||||
third: ['tredje'],
|
||||
fourth: ['fjärde'],
|
||||
last: ['sista']
|
||||
},
|
||||
|
||||
periods: {
|
||||
day: ['dag', 'dagar'],
|
||||
week: ['vecka', 'veckor'],
|
||||
month: ['månad', 'månader'],
|
||||
year: ['år']
|
||||
}
|
||||
},
|
||||
|
||||
timeEstimate: {
|
||||
hours: ['t', 'tim', 'timme', 'timmar', 'h'],
|
||||
minutes: ['m', 'min', 'minut', 'minuter']
|
||||
},
|
||||
|
||||
fallbackStatus: {
|
||||
open: ['att göra', 'öppen', 'kvar', 'todo', 'väntande'],
|
||||
inProgress: ['pågående', 'arbetar', 'gör', 'i process', 'under arbete'],
|
||||
done: ['klar', 'färdig', 'slutförd', 'avslutad', 'gjord'],
|
||||
cancelled: ['avbruten', 'inställd', 'avbokad'],
|
||||
waiting: ['väntar', 'blockerad', 'pausad', 'vilande']
|
||||
},
|
||||
|
||||
fallbackPriority: {
|
||||
urgent: ['brådskande', 'kritisk', 'högsta', 'akut', 'omedelbar'],
|
||||
high: ['hög', 'viktig', 'förhöjd', 'prioriterad'],
|
||||
normal: ['normal', 'medel', 'standard', 'vanlig'],
|
||||
low: ['låg', 'mindre', 'minimal', 'obetydlig']
|
||||
}
|
||||
};
|
||||
83
src/locales/uk.ts
Normal file
83
src/locales/uk.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { NLPLanguageConfig } from './types';
|
||||
|
||||
/**
|
||||
* Ukrainian language configuration for Natural Language Processing
|
||||
* Translated patterns for Ukrainian-speaking users
|
||||
*/
|
||||
export const ukConfig: NLPLanguageConfig = {
|
||||
code: 'uk',
|
||||
name: 'Українська',
|
||||
chronoLocale: 'uk',
|
||||
|
||||
dateTriggers: {
|
||||
due: ['термін', 'дедлайн', 'має бути готово до', 'до', 'не пізніше', 'крайній термін'],
|
||||
scheduled: ['заплановано на', 'запланований', 'почати', 'працювати над', 'на', 'в']
|
||||
},
|
||||
|
||||
recurrence: {
|
||||
frequencies: {
|
||||
daily: ['щодня', 'щоденно', 'кожен день', 'всі дні', 'на день'],
|
||||
weekly: ['щотижня', 'щотижнево', 'кожен тиждень', 'всі тижні', 'на тиждень'],
|
||||
monthly: ['щомісяця', 'щомісячно', 'кожен місяць', 'всі місяці', 'на місяць'],
|
||||
yearly: ['щороку', 'щорічно', 'кожен рік', 'всі роки', 'на рік']
|
||||
},
|
||||
|
||||
every: ['кожен', 'кожна', 'кожне', 'всі'],
|
||||
other: ['інший', 'інша', 'інше', 'інші'],
|
||||
|
||||
weekdays: {
|
||||
monday: ['понеділок'],
|
||||
tuesday: ['вівторок'],
|
||||
wednesday: ['середа'],
|
||||
thursday: ['четвер'],
|
||||
friday: ['п\'ятниця'],
|
||||
saturday: ['субота'],
|
||||
sunday: ['неділя']
|
||||
},
|
||||
|
||||
pluralWeekdays: {
|
||||
monday: ['понеділки'],
|
||||
tuesday: ['вівторки'],
|
||||
wednesday: ['середи'],
|
||||
thursday: ['четверги'],
|
||||
friday: ['п\'ятниці'],
|
||||
saturday: ['суботи'],
|
||||
sunday: ['неділі']
|
||||
},
|
||||
|
||||
ordinals: {
|
||||
first: ['перший', 'перша', 'перше'],
|
||||
second: ['другий', 'друга', 'друге'],
|
||||
third: ['третій', 'третя', 'третє'],
|
||||
fourth: ['четвертий', 'четверта', 'четверте'],
|
||||
last: ['останній', 'остання', 'останнє']
|
||||
},
|
||||
|
||||
periods: {
|
||||
day: ['день', 'дні', 'днів'],
|
||||
week: ['тиждень', 'тижні', 'тижнів'],
|
||||
month: ['місяць', 'місяці', 'місяців'],
|
||||
year: ['рік', 'роки', 'років']
|
||||
}
|
||||
},
|
||||
|
||||
timeEstimate: {
|
||||
hours: ['г', 'год', 'година', 'години', 'годин'],
|
||||
minutes: ['хв', 'мін', 'хвилина', 'хвилини', 'хвилин']
|
||||
},
|
||||
|
||||
fallbackStatus: {
|
||||
open: ['зробити', 'відкритий', 'очікує', 'todo', 'в очікуванні'],
|
||||
inProgress: ['в роботі', 'виконується', 'роблю', 'працюю', 'в процесі'],
|
||||
done: ['готово', 'виконано', 'завершено', 'закінчено', 'зроблено'],
|
||||
cancelled: ['скасовано', 'відмінено', 'припинено'],
|
||||
waiting: ['чекаю', 'очікую', 'заблоковано', 'призупинено']
|
||||
},
|
||||
|
||||
fallbackPriority: {
|
||||
urgent: ['терміново', 'критично', 'найвищий', 'невідкладно', 'пріоритетно'],
|
||||
high: ['високий', 'висока', 'важливо', 'підвищений'],
|
||||
normal: ['середній', 'середня', 'нормально', 'звичайно', 'стандартно'],
|
||||
low: ['низький', 'низька', 'менший', 'мінімальний', 'незначний']
|
||||
}
|
||||
};
|
||||
|
|
@ -240,7 +240,7 @@ export class NaturalLanguageParser {
|
|||
|
||||
// Build regex patterns from language config with proper escaping and boundary handling
|
||||
// Use lookahead/lookbehind for non-ASCII languages where \b doesn't work well
|
||||
const isNonAscii = ['ru', 'zh', 'ja'].includes(this.languageConfig.code);
|
||||
const isNonAscii = ['ru', 'zh', 'ja', 'uk'].includes(this.languageConfig.code);
|
||||
const boundary = isNonAscii ? '(?:^|\\s)' : '\\b';
|
||||
const endBoundary = isNonAscii ? '(?=\\s|$)' : '\\b';
|
||||
|
||||
|
|
@ -293,7 +293,7 @@ export class NaturalLanguageParser {
|
|||
const langConfig = this.languageConfig.fallbackStatus;
|
||||
|
||||
// Use appropriate boundary matching for different languages
|
||||
const isNonAscii = ['ru', 'zh', 'ja'].includes(this.languageConfig.code);
|
||||
const isNonAscii = ['ru', 'zh', 'ja', 'uk'].includes(this.languageConfig.code);
|
||||
const boundary = isNonAscii ? '(?:^|\\s)' : '\\b';
|
||||
const endBoundary = isNonAscii ? '(?=\\s|$)' : '\\b';
|
||||
|
||||
|
|
@ -574,7 +574,7 @@ export class NaturalLanguageParser {
|
|||
const patterns = [];
|
||||
|
||||
// Use appropriate boundary matching for different languages
|
||||
const isNonAscii = ['ru', 'zh', 'ja'].includes(this.languageConfig.code);
|
||||
const isNonAscii = ['ru', 'zh', 'ja', 'uk'].includes(this.languageConfig.code);
|
||||
const boundary = isNonAscii ? '(?:^|\\s)' : '\\b';
|
||||
const endBoundary = isNonAscii ? '(?=\\s|$)' : '\\b';
|
||||
|
||||
|
|
@ -793,7 +793,7 @@ export class NaturalLanguageParser {
|
|||
const langConfig = this.languageConfig.timeEstimate;
|
||||
|
||||
// Use appropriate boundary matching for different languages
|
||||
const isNonAscii = ['ru', 'zh', 'ja'].includes(this.languageConfig.code);
|
||||
const isNonAscii = ['ru', 'zh', 'ja', 'uk'].includes(this.languageConfig.code);
|
||||
const boundary = isNonAscii ? '(?:^|\\s)' : '\\b';
|
||||
const endBoundary = isNonAscii ? '(?=\\s|$)' : '\\b';
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,36 @@ describe('Language Configuration System', () => {
|
|||
expect(languageRegistry['ja'].name).toBe('日本語');
|
||||
expect(languageRegistry['ja'].code).toBe('ja');
|
||||
});
|
||||
|
||||
it('should contain Italian configuration', () => {
|
||||
expect(languageRegistry['it']).toBeDefined();
|
||||
expect(languageRegistry['it'].name).toBe('Italiano');
|
||||
expect(languageRegistry['it'].code).toBe('it');
|
||||
});
|
||||
|
||||
it('should contain Dutch configuration', () => {
|
||||
expect(languageRegistry['nl']).toBeDefined();
|
||||
expect(languageRegistry['nl'].name).toBe('Nederlands');
|
||||
expect(languageRegistry['nl'].code).toBe('nl');
|
||||
});
|
||||
|
||||
it('should contain Portuguese configuration', () => {
|
||||
expect(languageRegistry['pt']).toBeDefined();
|
||||
expect(languageRegistry['pt'].name).toBe('Português');
|
||||
expect(languageRegistry['pt'].code).toBe('pt');
|
||||
});
|
||||
|
||||
it('should contain Swedish configuration', () => {
|
||||
expect(languageRegistry['sv']).toBeDefined();
|
||||
expect(languageRegistry['sv'].name).toBe('Svenska');
|
||||
expect(languageRegistry['sv'].code).toBe('sv');
|
||||
});
|
||||
|
||||
it('should contain Ukrainian configuration', () => {
|
||||
expect(languageRegistry['uk']).toBeDefined();
|
||||
expect(languageRegistry['uk'].name).toBe('Українська');
|
||||
expect(languageRegistry['uk'].code).toBe('uk');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAvailableLanguages', () => {
|
||||
|
|
@ -55,7 +85,12 @@ describe('Language Configuration System', () => {
|
|||
{ value: 'de', label: 'Deutsch' },
|
||||
{ value: 'ru', label: 'Русский' },
|
||||
{ value: 'zh', label: '中文' },
|
||||
{ value: 'ja', label: '日本語' }
|
||||
{ value: 'ja', label: '日本語' },
|
||||
{ value: 'it', label: 'Italiano' },
|
||||
{ value: 'nl', label: 'Nederlands' },
|
||||
{ value: 'pt', label: 'Português' },
|
||||
{ value: 'sv', label: 'Svenska' },
|
||||
{ value: 'uk', label: 'Українська' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -97,7 +132,7 @@ describe('Language Configuration System', () => {
|
|||
it('should fallback to English for unsupported system language', () => {
|
||||
// Mock navigator.language
|
||||
const originalNavigator = global.navigator;
|
||||
const mockNavigator = { language: 'it-IT' }; // Italian not supported yet
|
||||
const mockNavigator = { language: 'ko-KR' }; // Korean not supported
|
||||
Object.defineProperty(global, 'navigator', {
|
||||
value: mockNavigator,
|
||||
writable: true
|
||||
|
|
@ -134,7 +169,7 @@ describe('Language Configuration System', () => {
|
|||
|
||||
describe('Language Configuration Structure', () => {
|
||||
it('should have consistent structure across all languages', () => {
|
||||
const languages = ['en', 'es', 'fr', 'de', 'ru', 'zh', 'ja'];
|
||||
const languages = ['en', 'es', 'fr', 'de', 'ru', 'zh', 'ja', 'it', 'nl', 'pt', 'sv', 'uk'];
|
||||
|
||||
languages.forEach(langCode => {
|
||||
const config = languageRegistry[langCode];
|
||||
|
|
|
|||
|
|
@ -253,6 +253,171 @@ describe('NaturalLanguageParser Multi-Language', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Italian Language', () => {
|
||||
let parser: NaturalLanguageParser;
|
||||
|
||||
beforeEach(() => {
|
||||
// Use empty configs to test language fallback patterns
|
||||
parser = new NaturalLanguageParser([], [], true, 'it');
|
||||
});
|
||||
|
||||
it('should parse Italian priority keywords', () => {
|
||||
const result = parser.parseInput('riunione urgente domani');
|
||||
expect(result.priority).toBe('urgent');
|
||||
expect(result.title).toMatch(/riunione/);
|
||||
});
|
||||
|
||||
it('should parse Italian status keywords', () => {
|
||||
const result = parser.parseInput('attività completato');
|
||||
expect(result.status).toBe('done');
|
||||
expect(result.title).toBe('attività');
|
||||
});
|
||||
|
||||
it('should parse Italian time estimates', () => {
|
||||
const result = parser.parseInput('attività 2 ore 30 minuti');
|
||||
expect(result.estimate).toBe(150); // 2*60 + 30 = 150 minutes
|
||||
expect(result.title).toBe('attività');
|
||||
});
|
||||
|
||||
it('should parse Italian recurrence patterns', () => {
|
||||
const result = parser.parseInput('riunione giornaliera team');
|
||||
expect(result.recurrence).toBe('FREQ=DAILY');
|
||||
expect(result.title).toMatch(/riunione.*team/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dutch Language', () => {
|
||||
let parser: NaturalLanguageParser;
|
||||
|
||||
beforeEach(() => {
|
||||
// Use empty configs to test language fallback patterns
|
||||
parser = new NaturalLanguageParser([], [], true, 'nl');
|
||||
});
|
||||
|
||||
it('should parse Dutch priority keywords', () => {
|
||||
const result = parser.parseInput('vergadering urgent morgen');
|
||||
expect(result.priority).toBe('urgent');
|
||||
expect(result.title).toMatch(/vergadering/);
|
||||
});
|
||||
|
||||
it('should parse Dutch status keywords', () => {
|
||||
const result = parser.parseInput('taak voltooid');
|
||||
expect(result.status).toBe('done');
|
||||
expect(result.title).toBe('taak');
|
||||
});
|
||||
|
||||
it('should parse Dutch time estimates', () => {
|
||||
const result = parser.parseInput('taak 2 uur 30 minuten');
|
||||
expect(result.estimate).toBe(150); // 2*60 + 30 = 150 minutes
|
||||
expect(result.title).toBe('taak');
|
||||
});
|
||||
|
||||
it('should parse Dutch recurrence patterns', () => {
|
||||
const result = parser.parseInput('vergadering dagelijks team');
|
||||
expect(result.recurrence).toBe('FREQ=DAILY');
|
||||
expect(result.title).toMatch(/vergadering.*team/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Portuguese Language', () => {
|
||||
let parser: NaturalLanguageParser;
|
||||
|
||||
beforeEach(() => {
|
||||
// Use empty configs to test language fallback patterns
|
||||
parser = new NaturalLanguageParser([], [], true, 'pt');
|
||||
});
|
||||
|
||||
it('should parse Portuguese priority keywords', () => {
|
||||
const result = parser.parseInput('reunião urgente amanhã');
|
||||
expect(result.priority).toBe('urgent');
|
||||
expect(result.title).toMatch(/reunião/);
|
||||
});
|
||||
|
||||
it('should parse Portuguese status keywords', () => {
|
||||
const result = parser.parseInput('tarefa concluído');
|
||||
expect(result.status).toBe('done');
|
||||
expect(result.title).toBe('tarefa');
|
||||
});
|
||||
|
||||
it('should parse Portuguese time estimates', () => {
|
||||
const result = parser.parseInput('tarefa 2 horas 30 minutos');
|
||||
expect(result.estimate).toBe(150); // 2*60 + 30 = 150 minutes
|
||||
expect(result.title).toBe('tarefa');
|
||||
});
|
||||
|
||||
it('should parse Portuguese recurrence patterns', () => {
|
||||
const result = parser.parseInput('reunião diária equipe');
|
||||
expect(result.recurrence).toBe('FREQ=DAILY');
|
||||
expect(result.title).toMatch(/reunião.*equipe/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Swedish Language', () => {
|
||||
let parser: NaturalLanguageParser;
|
||||
|
||||
beforeEach(() => {
|
||||
// Use empty configs to test language fallback patterns
|
||||
parser = new NaturalLanguageParser([], [], true, 'sv');
|
||||
});
|
||||
|
||||
it('should parse Swedish priority keywords', () => {
|
||||
const result = parser.parseInput('möte brådskande imorgon');
|
||||
expect(result.priority).toBe('urgent');
|
||||
expect(result.title).toMatch(/möte/);
|
||||
});
|
||||
|
||||
it('should parse Swedish status keywords', () => {
|
||||
const result = parser.parseInput('uppgift klar');
|
||||
expect(result.status).toBe('done');
|
||||
expect(result.title).toBe('uppgift');
|
||||
});
|
||||
|
||||
it('should parse Swedish time estimates', () => {
|
||||
const result = parser.parseInput('uppgift 2 timmar 30 minuter');
|
||||
expect(result.estimate).toBe(150); // 2*60 + 30 = 150 minutes
|
||||
expect(result.title).toBe('uppgift');
|
||||
});
|
||||
|
||||
it('should parse Swedish recurrence patterns', () => {
|
||||
const result = parser.parseInput('möte dagligen team');
|
||||
expect(result.recurrence).toBe('FREQ=DAILY');
|
||||
expect(result.title).toMatch(/möte.*team/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Ukrainian Language', () => {
|
||||
let parser: NaturalLanguageParser;
|
||||
|
||||
beforeEach(() => {
|
||||
// Use empty configs to test language fallback patterns
|
||||
parser = new NaturalLanguageParser([], [], true, 'uk');
|
||||
});
|
||||
|
||||
it('should parse Ukrainian priority keywords', () => {
|
||||
const result = parser.parseInput('зустріч терміново завтра');
|
||||
expect(result.priority).toBe('urgent');
|
||||
expect(result.title).toMatch(/зустріч/);
|
||||
});
|
||||
|
||||
it('should parse Ukrainian status keywords', () => {
|
||||
const result = parser.parseInput('завдання виконано');
|
||||
expect(result.status).toBe('done');
|
||||
expect(result.title).toBe('завдання');
|
||||
});
|
||||
|
||||
it('should parse Ukrainian time estimates', () => {
|
||||
const result = parser.parseInput('завдання 2 години 30 хвилин');
|
||||
expect(result.estimate).toBe(150); // 2*60 + 30 = 150 minutes
|
||||
expect(result.title).toBe('завдання');
|
||||
});
|
||||
|
||||
it('should parse Ukrainian recurrence patterns', () => {
|
||||
const result = parser.parseInput('зустріч щодня команда');
|
||||
expect(result.recurrence).toBe('FREQ=DAILY');
|
||||
expect(result.title).toMatch(/зустріч.*команда/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Language Fallback', () => {
|
||||
it('should fallback to English for unsupported language codes', () => {
|
||||
const parser = new NaturalLanguageParser(mockStatusConfigs, mockPriorityConfigs, true, 'unsupported');
|
||||
|
|
|
|||
Loading…
Reference in a new issue