fix: lower German-teaser threshold for short job-room descriptions
job-room's search API returns a short keyword-anchored teaser (always < 500 chars), not the full posting. The >=6 DE/FR-word threshold was calibrated against full page text and is unreachable on a couple of sentences, so a genuinely German teaser (Consult & Pepper's "Senior Fullstack Software Engineer") could clear it on word count alone despite jobs.ch itself showing "Deutsch (Fliessend)" on the full posting. Use a lower threshold for short text; the full-page >=6 threshold stays as-is for longer descriptions, where it was tuned to avoid false positives.
This commit is contained in:
+19
-3
@@ -43,9 +43,18 @@ function applyFilters(title: string): boolean {
|
||||
const MS_STACK_RE = /\.net\b|\bc#|sharepoint|dynamics 365|power platform|\bsccm\b|intune|successfactors|\bsap\b/i;
|
||||
|
||||
// DE/FR indicator words, counted against a description we already have in hand.
|
||||
// Mirrors the ≥ 6 threshold isEnglishContent uses on fetched pages.
|
||||
// Mirrors the ≥ 6 threshold isEnglishContent uses on fetched pages — but see
|
||||
// the adaptive threshold below for why short snippets need a lower bar.
|
||||
// du/dein*/dich/dir/einem/einer/eines/der/dem added 2026-09-08: job-room's
|
||||
// search API "Senior Fullstack Software Engineer" (Consult & Pepper) teaser —
|
||||
// "Du arbeitest in einem agilen Umfeld nach Scrum mit folgendem Tech-Stack:
|
||||
// ... TypeScript, Angular, HTML, CSS, PostgreSQL, ..." — is entirely German
|
||||
// but has zero umlauts and only one hit ("mit") on the original list, so it
|
||||
// sailed through as English despite jobs.ch itself showing "Deutsch
|
||||
// (Fliessend)" on the full posting. These are unambiguous German/French
|
||||
// function words with no common English collision.
|
||||
const DE_FR_WORDS_RE =
|
||||
/\b(und|oder|wir|Sie|Ihre|Ihrer|Kenntnisse|Erfahrung|Aufgaben|Anforderungen|Stelle|Unternehmen|Mitarbeiter|Bereich|nous|vous|votre|pour|avec|des|les)\b/g;
|
||||
/\b(und|oder|wir|Sie|Ihre|Ihrer|Kenntnisse|Erfahrung|Aufgaben|Anforderungen|Stelle|Unternehmen|Mitarbeiter|Bereich|nous|vous|votre|pour|avec|des|les|du|dein\w*|dich|dir|einem|einer|eines|der|dem)\b/g;
|
||||
|
||||
// English-language AND not demanding German above B1 — mirrors isViableContent/
|
||||
// isViableUrl in fetch.ts, but against a description we already have in hand
|
||||
@@ -61,7 +70,14 @@ function isViableDescription(description?: string): { ok: boolean; reason?: stri
|
||||
if (!description) return { ok: true }; // nothing to judge — let the URL check in index.ts decide
|
||||
const text = description.replace(/<[^>]+>/g, " ").replace(/&[a-z]+;/g, " ");
|
||||
if (hasNonEnglishUmlaut(text)) return { ok: false, reason: "non-English (umlaut)" };
|
||||
if ((text.match(DE_FR_WORDS_RE) ?? []).length >= 6) return { ok: false, reason: "non-English" };
|
||||
// job-room's search API returns a short keyword-anchored teaser (measured:
|
||||
// always < 500 chars), not the full posting — ≥6 is calibrated against full
|
||||
// page text and is unreachable on a couple of sentences, so a genuinely
|
||||
// German teaser can clear it on word count alone. Lower the bar for short
|
||||
// text; leave the full-page threshold (≥6) alone for longer descriptions,
|
||||
// where it was tuned to avoid false positives on English text.
|
||||
const threshold = text.length < 500 ? 3 : 6;
|
||||
if ((text.match(DE_FR_WORDS_RE) ?? []).length >= threshold) return { ok: false, reason: "non-English" };
|
||||
if (requiresGerman(text)) return { ok: false, reason: "requires German > B1" };
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user