mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
49 lines
1.9 KiB
TypeScript
49 lines
1.9 KiB
TypeScript
import type { Email } from '@/lib/jmap/types';
|
|
|
|
/** A BCP 47 tag as mail carries it: "fr", "de-CH", "zh-Hant-TW". */
|
|
const LANGUAGE_TAG = /^[a-z]{2,3}(?:-[a-z0-9]{1,8})*$/i;
|
|
|
|
function cleanTag(value: string | null | undefined): string | null {
|
|
if (!value) return null;
|
|
// Content-Language may list several ("de, en"); the first one is the body's.
|
|
const first = value.split(',')[0].trim();
|
|
return LANGUAGE_TAG.test(first) ? first : null;
|
|
}
|
|
|
|
function headerValue(headers: Email['headers'], name: string): string | null {
|
|
if (!headers || Array.isArray(headers)) return null;
|
|
const key = Object.keys(headers).find((k) => k.toLowerCase() === name);
|
|
if (!key) return null;
|
|
const value = headers[key];
|
|
return Array.isArray(value) ? value[0] ?? null : value ?? null;
|
|
}
|
|
|
|
/** The `lang` an HTML body declares on its <html> or <body> element. */
|
|
export function htmlDeclaredLanguage(html: string | null | undefined): string | null {
|
|
if (!html) return null;
|
|
const head = html.slice(0, 20000);
|
|
for (const tag of ['html', 'body']) {
|
|
const match = new RegExp(`<${tag}\\b[^>]*?\\s(?:xml:)?lang\\s*=\\s*["']?([^"'\\s>]+)`, 'i').exec(head);
|
|
const lang = cleanTag(match?.[1]);
|
|
if (lang) return lang;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* The language a message body is written in, for `lang` on the frame it
|
|
* renders in, so a screen reader reads a French mail with a French voice.
|
|
* Taken from the body part's Content-Language, the message header, or the
|
|
* HTML's own `lang`, in that order; null when the mail doesn't say.
|
|
*/
|
|
export function mailLanguage(
|
|
email: Pick<Email, 'htmlBody' | 'textBody' | 'headers'> | null | undefined,
|
|
html?: string | null,
|
|
): string | null {
|
|
if (!email) return htmlDeclaredLanguage(html);
|
|
const part = email.htmlBody?.[0] ?? email.textBody?.[0];
|
|
return cleanTag(part?.language?.[0])
|
|
?? cleanTag(headerValue(email.headers, 'content-language'))
|
|
?? htmlDeclaredLanguage(html);
|
|
}
|