fix: warn about template fill-ins like [Name] before sending

A lone capitalised word in brackets now counts as a fill-in, so two
cases of ordinary text changed: the cross-language case uses lowercase
"[votre] [ваше]", and the broken word-list case uses "[Your name]", so
both still test what they were written for.
This commit is contained in:
Linus Rath
2026-11-04 06:36:09 +01:00
parent 80321e21a2
commit 4ec1c128f6
2 changed files with 24 additions and 3 deletions
+18 -2
View File
@@ -31,6 +31,22 @@ describe('findUnfilledPlaceholders', () => {
expect(findUnfilledPlaceholders('', 'Treffpunkt: [ÜBERGABEORT]')).toEqual(['[ÜBERGABEORT]']);
});
// A template's "Hi [Name]" went out unchanged: only capitals and
// "your/insert" prompts counted.
it('finds capitalised words in brackets that a template left to fill in', () => {
expect(findUnfilledPlaceholders('', ['Hi [Name],', '[Step] is done. Regards, [First Name] at [Company]. [Date]'].join('\n'), EN)).toEqual([
'[Name]',
'[Step]',
'[First Name]',
'[Company]',
'[Date]',
]);
});
it('leaves longer bracketed phrases and links alone', () => {
expect(findUnfilledPlaceholders('', 'See [Chapter Four Part Two] and [Name](https://example.com).', EN)).toEqual([]);
});
it('finds fill-ins that ask the sender to write something', () => {
expect(findUnfilledPlaceholders('', 'Best regards,\n[Your Name]\n[Insert date]', EN)).toEqual([
'[Your Name]',
@@ -94,7 +110,7 @@ describe('findUnfilledPlaceholders', () => {
it('skips word lists that are not lists of strings', () => {
const broken = [{ before: 'your' }, { before: [1, '', '-', null] }, null, { after: {} }] as unknown as PlaceholderWords[];
expect(findUnfilledPlaceholders('', '[Your Name] [TIME]', broken)).toEqual(['[TIME]']);
expect(findUnfilledPlaceholders('', '[Your name] [TIME]', broken)).toEqual(['[TIME]']);
});
it('finds the circles that stand in for a name in Japanese and Korean', () => {
@@ -104,7 +120,7 @@ describe('findUnfilledPlaceholders', () => {
it('leaves bracketed words and headings in other languages alone', () => {
const text = [
'【重要】お知らせ 【通知】',
'[Votre] [Ваше] [您的]',
'[votre] [ваше] [您的]',
'Le [ton] juste. Vote [tu].',
'Siehe [Anlage 2] und [Kapitel 3].',
'Termin: ○ und ◯.',
+6 -1
View File
@@ -10,6 +10,11 @@ const TEMPLATE_PLACEHOLDER = /\{\{\s*[\w.-]+\s*\}\}/g;
// Markdown link.
const CAPS_BRACKET = /\[[\p{Lu}\d][\p{Lu}\d _./-]{0,28}[\p{Lu}\d]\](?!\()/gu;
// [Name], [Company], [First Name]: one to three capitalised words, the way
// templates name a fill-in. A lowercase word ([Yours truly]) or a number
// ([Anlage 2]) makes it ordinary text.
const TITLE_BRACKET = /\[\p{Lu}[\p{Ll}'’-]+(?: \p{Lu}[\p{Ll}'’-]+){0,2}\](?!\()/gu;
/**
* The words that mark a fill-in in one language. They live in each locale
* file under email_composer.placeholder_words, so a new translation brings
@@ -113,7 +118,7 @@ function collect(text: string, patterns: RegExp[]): Match[] {
* to check (see PlaceholderWords).
*/
export function findUnfilledPlaceholders(subject: string, bodyText: string, words: PlaceholderWords[] = []): string[] {
const bodyPatterns = [TEMPLATE_PLACEHOLDER, CAPS_BRACKET, CIRCLE_MARKER];
const bodyPatterns = [TEMPLATE_PLACEHOLDER, CAPS_BRACKET, TITLE_BRACKET, CIRCLE_MARKER];
const prompt = promptBracketPattern(words);
const unfinished = unfinishedWordPattern(words);
if (prompt) bodyPatterns.push(prompt);