Skip to content

Commit

Permalink
Merge pull request #398 from jebbench/enhance-dutch-support
Browse files Browse the repository at this point in the history
Added more support for Dutch.
  • Loading branch information
wanasit committed Jun 18, 2021
2 parents 405a27b + 7bb00af commit 0cfef4e
Show file tree
Hide file tree
Showing 11 changed files with 1,012 additions and 8 deletions.
16 changes: 11 additions & 5 deletions src/locales/nl/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,14 @@ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType } = {
min: "minute",
mins: "minute",
minute: "minute",
minuut: "minute",
minuten: "minute",
minuutje: "minute",
h: "hour",
hr: "hour",
hrs: "hour",
uur: "hour",
u: "hour",
uren: "hour",
dag: "d",
dagen: "d",
Expand All @@ -160,18 +163,21 @@ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType } = {

//-----------------------------

export const NUMBER_PATTERN = `(?:${matchAnyPattern(INTEGER_WORD_DICTIONARY)}|[0-9]+|[0-9]+\\.[0-9]+|een?|halve?)`;
export const NUMBER_PATTERN = `(?:${matchAnyPattern(
INTEGER_WORD_DICTIONARY
)}|[0-9]+|[0-9]+[\\.,][0-9]+|halve?|half|paar)`;

export function parseNumberPattern(match: string): number {
const num = match.toLowerCase();
if (INTEGER_WORD_DICTIONARY[num] !== undefined) {
return INTEGER_WORD_DICTIONARY[num];
} else if (num === "een") {
return 1;
} else if (num.match(/halve?/)) {
} else if (num === "paar") {
return 2;
} else if (num === "half" || num.match(/halve?/)) {
return 0.5;
}
return parseFloat(num);
// Replace "," with "." to support some European languages
return parseFloat(num.replace(",", "."));
}

//-----------------------------
Expand Down
15 changes: 12 additions & 3 deletions src/locales/nl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import NLSlashMonthFormatParser from "./parsers/NLSlashMonthFormatParser";
import NLTimeExpressionParser from "./parsers/NLTimeExpressionParser";
import NLCasualYearMonthDayParser from "./parsers/NLCasualYearMonthDayParser";
import NLCasualDateTimeParser from "./parsers/NLCasualDateTimeParser";
import NLTimeUnitCasualRelativeFormatParser from "./parsers/NLTimeUnitCasualRelativeFormatParser";
import NLRelativeDateFormatParser from "./parsers/NLRelativeDateFormatParser";
import NLTimeUnitAgoFormatParser from "./parsers/NLTimeUnitAgoFormatParser";
import NLTimeUnitLaterFormatParser from "./parsers/NLTimeUnitLaterFormatParser";

// Shortcuts
export const casual = new Chrono(createCasualConfiguration());
Expand All @@ -38,6 +42,9 @@ export function createCasualConfiguration(littleEndian = true): Configuration {
option.parsers.unshift(new NLCasualDateParser());
option.parsers.unshift(new NLCasualTimeParser());
option.parsers.unshift(new NLCasualDateTimeParser());
option.parsers.unshift(new NLMonthNameParser());
option.parsers.unshift(new NLRelativeDateFormatParser());
option.parsers.unshift(new NLTimeUnitCasualRelativeFormatParser());
return option;
}

Expand All @@ -49,13 +56,15 @@ export function createConfiguration(strictMode = true, littleEndian = true): Con
{
parsers: [
new SlashDateFormatParser(littleEndian),
new NLTimeUnitWithinFormatParser(),
new NLMonthNameMiddleEndianParser(),
new NLMonthNameParser(),
new NLTimeExpressionParser(),
new NLTimeUnitWithinFormatParser(),
new NLSlashMonthFormatParser(),
new NLWeekdayParser(),
new NLCasualYearMonthDayParser(),
new NLSlashMonthFormatParser(),
new NLTimeExpressionParser(strictMode),
new NLTimeUnitAgoFormatParser(strictMode),
new NLTimeUnitLaterFormatParser(strictMode),
],
refiners: [new NLMergeDateTimeRefiner(), new NLMergeDateRangeRefiner()],
},
Expand Down
70 changes: 70 additions & 0 deletions src/locales/nl/parsers/NLRelativeDateFormatParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { TIME_UNIT_DICTIONARY } from "../constants";
import { ParsingContext } from "../../../chrono";
import { ParsingComponents } from "../../../results";
import dayjs from "dayjs";
import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
import { matchAnyPattern } from "../../../utils/pattern";

const PATTERN = new RegExp(
`(dit|deze|komende|volgend|volgende|afgelopen|vorige)\\s*(${matchAnyPattern(TIME_UNIT_DICTIONARY)})(?=\\s*)` +
"(?=\\W|$)",
"i"
);

const MODIFIER_WORD_GROUP = 1;
const RELATIVE_WORD_GROUP = 2;

export default class NLRelativeDateFormatParser extends AbstractParserWithWordBoundaryChecking {
innerPattern(): RegExp {
return PATTERN;
}

innerExtract(context: ParsingContext, match: RegExpMatchArray): ParsingComponents {
const modifier = match[MODIFIER_WORD_GROUP].toLowerCase();
const unitWord = match[RELATIVE_WORD_GROUP].toLowerCase();
const timeunit = TIME_UNIT_DICTIONARY[unitWord];

if (modifier == "volgend" || modifier == "volgende" || modifier == "komende") {
const timeUnits = {};
timeUnits[timeunit] = 1;
return ParsingComponents.createRelativeFromRefDate(context.refDate, timeUnits);
}

if (modifier == "afgelopen" || modifier == "vorige") {
const timeUnits = {};
timeUnits[timeunit] = -1;
return ParsingComponents.createRelativeFromRefDate(context.refDate, timeUnits);
}

const components = context.createParsingComponents();
let date = dayjs(context.refDate);

// This week
if (unitWord.match(/week/i)) {
date = date.add(-date.get("d"), "d");
components.imply("day", date.date());
components.imply("month", date.month() + 1);
components.imply("year", date.year());
}

// This month
else if (unitWord.match(/maand/i)) {
date = date.add(-date.date() + 1, "d");
components.imply("day", date.date());
components.assign("year", date.year());
components.assign("month", date.month() + 1);
}

// This year
else if (unitWord.match(/jaar/i)) {
date = date.add(-date.date() + 1, "d");
date = date.add(-date.month(), "month");

components.imply("day", date.date());
components.imply("month", date.month() + 1);
components.assign("year", date.year());
}

return components;
}
}
4 changes: 4 additions & 0 deletions src/locales/nl/parsers/NLTimeExpressionParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export default class NLTimeExpressionParser extends AbstractTimeExpressionParser
return "\\s*(?:\\-|\\–|\\~|\\〜|om|\\?)\\s*";
}

primarySuffix(): string {
return "(?:\\s*(?:uur))?(?!/)(?=\\W|$)";
}

extractPrimaryTimeComponents(context: ParsingContext, match: RegExpMatchArray): ParsingComponents | null {
// This looks more like a year e.g. 2020
if (match[0].match(/^\s*\d{4}\s*$/)) {
Expand Down
26 changes: 26 additions & 0 deletions src/locales/nl/parsers/NLTimeUnitAgoFormatParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ParsingContext } from "../../../chrono";
import { parseTimeUnits, TIME_UNITS_PATTERN } from "../constants";
import { ParsingComponents } from "../../../results";
import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
import { reverseTimeUnits } from "../../../utils/timeunits";

const PATTERN = new RegExp("" + "(" + TIME_UNITS_PATTERN + ")" + "(?:geleden|voor|eerder)(?=(?:\\W|$))", "i");

const STRICT_PATTERN = new RegExp("" + "(" + TIME_UNITS_PATTERN + ")" + "geleden(?=(?:\\W|$))", "i");

export default class NLTimeUnitAgoFormatParser extends AbstractParserWithWordBoundaryChecking {
constructor(private strictMode: boolean) {
super();
}

innerPattern(): RegExp {
return this.strictMode ? STRICT_PATTERN : PATTERN;
}

innerExtract(context: ParsingContext, match: RegExpMatchArray) {
const timeUnits = parseTimeUnits(match[1]);
const outputTimeUnits = reverseTimeUnits(timeUnits);

return ParsingComponents.createRelativeFromRefDate(context.refDate, outputTimeUnits);
}
}
27 changes: 27 additions & 0 deletions src/locales/nl/parsers/NLTimeUnitCasualRelativeFormatParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { TIME_UNITS_PATTERN, parseTimeUnits } from "../constants";
import { ParsingContext } from "../../../chrono";
import { ParsingComponents } from "../../../results";
import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
import { reverseTimeUnits } from "../../../utils/timeunits";

const PATTERN = new RegExp(`(deze|vorige|afgelopen|komende|over|\\+|-)\\s*(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");

export default class NLTimeUnitCasualRelativeFormatParser extends AbstractParserWithWordBoundaryChecking {
innerPattern(): RegExp {
return PATTERN;
}

innerExtract(context: ParsingContext, match: RegExpMatchArray): ParsingComponents {
const prefix = match[1].toLowerCase();
let timeUnits = parseTimeUnits(match[2]);
switch (prefix) {
case "vorige":
case "afgelopen":
case "-":
timeUnits = reverseTimeUnits(timeUnits);
break;
}

return ParsingComponents.createRelativeFromRefDate(context.refDate, timeUnits);
}
}
27 changes: 27 additions & 0 deletions src/locales/nl/parsers/NLTimeUnitLaterFormatParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ParsingContext } from "../../../chrono";
import { parseTimeUnits, TIME_UNITS_PATTERN } from "../constants";
import { ParsingComponents } from "../../../results";
import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";

const PATTERN = new RegExp(
"" + "(" + TIME_UNITS_PATTERN + ")" + "(later|na|vanaf nu|voortaan|vooruit|uit)" + "(?=(?:\\W|$))",
"i"
);

const STRICT_PATTERN = new RegExp("" + "(" + TIME_UNITS_PATTERN + ")" + "(later|vanaf nu)" + "(?=(?:\\W|$))", "i");
const GROUP_NUM_TIMEUNITS = 1;

export default class NLTimeUnitLaterFormatParser extends AbstractParserWithWordBoundaryChecking {
constructor(private strictMode: boolean) {
super();
}

innerPattern(): RegExp {
return this.strictMode ? STRICT_PATTERN : PATTERN;
}

innerExtract(context: ParsingContext, match: RegExpMatchArray) {
const fragments = parseTimeUnits(match[GROUP_NUM_TIMEUNITS]);
return ParsingComponents.createRelativeFromRefDate(context.refDate, fragments);
}
}
Loading

0 comments on commit 0cfef4e

Please sign in to comment.