mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 00:14:18 +00:00
feat: list To and Cc in the header of a forwarded message
This commit is contained in:
@@ -379,3 +379,39 @@ describe('reply quote attribution markers', () => {
|
||||
expect(html).toContain('<p data-reply-quote-start="true"><b>From:</b> Bob</p>');
|
||||
});
|
||||
});
|
||||
|
||||
// The forwarded header had From, Date and Subject only; Gmail and Outlook
|
||||
// also say who the mail went to.
|
||||
describe('forwarded header recipients', () => {
|
||||
afterEach(() => {
|
||||
useSettingsStore.setState({ plainTextMode: false, signaturePosition: 'below_quote' });
|
||||
setIdentities([ME]);
|
||||
});
|
||||
const FORWARDED = {
|
||||
...REPLY_TO,
|
||||
to: [{ name: 'Anna Becker', email: 'anna@example.com' }, { email: 'team@example.com' }],
|
||||
cc: [{ name: 'Carol', email: 'carol@example.com' }],
|
||||
};
|
||||
|
||||
it('lists To and Cc in a plain text forward', async () => {
|
||||
useSettingsStore.setState({ plainTextMode: true });
|
||||
const onSend = vi.fn();
|
||||
render(<EmailComposer mode="forward" replyTo={FORWARDED} prefill={{ to: ['dave@example.com'] }} onSend={onSend} />);
|
||||
const sent = await send(onSend);
|
||||
expect(sent.body).toContain('subject_label: Question\nforward_to_label: Anna Becker <anna@example.com>, team@example.com\nforward_cc_label: Carol <carol@example.com>\n\nOriginal text');
|
||||
});
|
||||
|
||||
// escapeHtml is mocked to pass values through in this file.
|
||||
it('lists To and Cc in an HTML forward', () => {
|
||||
render(<EmailComposer mode="forward" replyTo={FORWARDED} />);
|
||||
const html = screen.getByTestId('rich-text-editor').getAttribute('data-html')!;
|
||||
expect(html).toContain('subject_label: Question<br>forward_to_label: Anna Becker <anna@example.com>, team@example.com<br>forward_cc_label: Carol <carol@example.com><br><br>');
|
||||
});
|
||||
|
||||
it('leaves out a Cc line when nobody was copied', () => {
|
||||
render(<EmailComposer mode="forward" replyTo={{ ...FORWARDED, cc: [] }} />);
|
||||
const html = screen.getByTestId('rich-text-editor').getAttribute('data-html')!;
|
||||
expect(html).toContain('forward_to_label: ');
|
||||
expect(html).not.toContain('forward_cc_label');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -53,6 +53,7 @@ import { plainOwnRange, readOwnContent, readSelection as readEditorSelection, re
|
||||
import { actionsFor, runAction } from "@/lib/plugin-sandbox/actions";
|
||||
import { matchesCombo } from "@/lib/plugin-sandbox/shortcuts";
|
||||
import { cn, formatFileSize, formatDateTime, generateUUID } from "@/lib/utils";
|
||||
import { forwardRecipientLines } from "@/lib/quote-header";
|
||||
import { ScheduleSendDialog } from "./schedule-send-dialog";
|
||||
import { debug } from "@/lib/debug";
|
||||
import { toast } from "@/stores/toast-store";
|
||||
@@ -839,6 +840,7 @@ export function EmailComposer({
|
||||
? `${from.name} <${from.email}>`
|
||||
: (from.email || from.name || tCommon('unknown')))
|
||||
: tCommon('unknown');
|
||||
const forwardLines = forwardRecipientLines(replyTo, { toLabel: tWords('forward_to_label'), ccLabel: tWords('forward_cc_label') });
|
||||
|
||||
const originalText = replyTo.body || (replyTo.htmlBody ? htmlToPlainText(replyTo.htmlBody) : '');
|
||||
const quotedText = originalText.split('\n').map(line => `> ${line}`).join('\n');
|
||||
@@ -859,7 +861,7 @@ export function EmailComposer({
|
||||
}
|
||||
|
||||
if (mode === 'forward') {
|
||||
return `${prefix}${signatureBlock}\n\n${tQuote('forwarded_separator')}\n${tQuote('from_label')}: ${fromStrFull}\n${tQuote('date_label')}: ${date}\n${tQuote('subject_label')}: ${replyTo.subject || ''}\n\n${originalText}`;
|
||||
return `${prefix}${signatureBlock}\n\n${tQuote('forwarded_separator')}\n${tQuote('from_label')}: ${fromStrFull}\n${tQuote('date_label')}: ${date}\n${tQuote('subject_label')}: ${replyTo.subject || ''}${forwardLines.map((line) => `\n${line.label}: ${line.value}`).join('')}\n\n${originalText}`;
|
||||
} else if (mode === 'reply' || mode === 'replyAll') {
|
||||
return `${prefix}${signatureBlock}\n\n${tQuote('reply_line', { date, from: fromStrFull })}\n${quotedText}`;
|
||||
}
|
||||
@@ -889,6 +891,8 @@ export function EmailComposer({
|
||||
? `${from.name} <${from.email}>`
|
||||
: (from.email || from.name || tCommon('unknown')))
|
||||
: tCommon('unknown');
|
||||
const forwardLinesHtml = forwardRecipientLines(replyTo, { toLabel: tWords('forward_to_label'), ccLabel: tWords('forward_cc_label') })
|
||||
.map((line) => `${line.label}: ${escapeHtml(line.value)}<br>`).join('');
|
||||
|
||||
// Like a new mail, an empty line above the signature gives the cursor a
|
||||
// place for the reply. Without it the reply started on the "-- " line (#1503).
|
||||
@@ -937,7 +941,7 @@ export function EmailComposer({
|
||||
// HTML-escape user-controlled values: an unescaped sender "Name <email>"
|
||||
// has its "<email>" eaten as a bogus HTML tag by the rich-text editor (#482).
|
||||
const quoteHeader = mode === 'forward'
|
||||
? `${tQuote('forwarded_separator')}<br>${tQuote('from_label')}: ${escapeHtml(fromStrFull)}<br>${tQuote('date_label')}: ${escapeHtml(date)}<br>${tQuote('subject_label')}: ${escapeHtml(replyTo.subject || '')}<br><br>`
|
||||
? `${tQuote('forwarded_separator')}<br>${tQuote('from_label')}: ${escapeHtml(fromStrFull)}<br>${tQuote('date_label')}: ${escapeHtml(date)}<br>${tQuote('subject_label')}: ${escapeHtml(replyTo.subject || '')}<br>${forwardLinesHtml}<br>`
|
||||
: `${tQuote('reply_line', { date: escapeHtml(date), from: escapeHtml(fromStrFull) })}<br>`;
|
||||
// Embed the original as a QuotedHtml island (verbatim, schema-free) so
|
||||
// its layout survives the editor round-trip. Sanitize first to strip
|
||||
@@ -953,7 +957,7 @@ export function EmailComposer({
|
||||
if (replyTo.body) {
|
||||
const escapedOriginal = replyTo.body.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>');
|
||||
if (mode === 'forward') {
|
||||
return `${head}<br><br>${tQuote('forwarded_separator')}<br>${tQuote('from_label')}: ${escapeHtml(fromStrFull)}<br>${tQuote('date_label')}: ${escapeHtml(date)}<br>${tQuote('subject_label')}: ${escapeHtml(replyTo.subject || '')}<br><br>${escapedOriginal}`;
|
||||
return `${head}<br><br>${tQuote('forwarded_separator')}<br>${tQuote('from_label')}: ${escapeHtml(fromStrFull)}<br>${tQuote('date_label')}: ${escapeHtml(date)}<br>${tQuote('subject_label')}: ${escapeHtml(replyTo.subject || '')}<br>${forwardLinesHtml}<br>${escapedOriginal}`;
|
||||
} else if (mode === 'reply' || mode === 'replyAll') {
|
||||
return `${head}<br><br>${replyHeader(`<p>${tQuote('reply_line', { date: escapeHtml(date), from: escapeHtml(fromStrFull) })}<br></p>`)}<blockquote style="margin:0 0 0 0.8ex;border-left:2px solid #ccc;padding-left:1ex">${escapedOriginal}</blockquote>`;
|
||||
}
|
||||
|
||||
@@ -90,3 +90,35 @@ describe('buildQuoteHeader (#482 — sender address survives HTML rendering)', (
|
||||
expect(h.text).not.toContain('<noname@x.tld>');
|
||||
});
|
||||
});
|
||||
|
||||
// Gmail and Outlook list who the forwarded mail went to.
|
||||
describe('buildQuoteHeader forward recipients', () => {
|
||||
const email = {
|
||||
from: [sender],
|
||||
to: [{ name: 'Anna Becker', email: 'anna@example.com' }, { email: 'team@example.com' }],
|
||||
cc: [{ name: 'Carol <x>', email: 'carol@example.com' }],
|
||||
subject: 'Hello',
|
||||
receivedAt: '2026-01-01T10:00:00Z',
|
||||
};
|
||||
|
||||
it('adds To and Cc lines after the subject', async () => {
|
||||
const h = await buildQuoteHeader({ mode: 'forward', email, ...base });
|
||||
expect(h.text).toContain('Subject: Hello\nTo: Anna Becker <anna@example.com>, team@example.com\nCc: Carol <x> <carol@example.com>\n');
|
||||
expect(h.html).toContain('To: Anna Becker <anna@example.com>, team@example.com<br>Cc: Carol <x> <carol@example.com><br>');
|
||||
});
|
||||
|
||||
it('uses the caller\'s To and Cc labels', async () => {
|
||||
const labels = {
|
||||
formatReplyLine: () => '', forwardedSeparator: '--', fromLabel: 'Von', dateLabel: 'Datum', subjectLabel: 'Betreff',
|
||||
toLabel: 'An', ccLabel: 'Kopie',
|
||||
};
|
||||
const h = await buildQuoteHeader({ mode: 'forward', email, labels, ...base });
|
||||
expect(h.text).toContain('\nAn: Anna Becker <anna@example.com>, team@example.com\nKopie: Carol <x> <carol@example.com>\n');
|
||||
});
|
||||
|
||||
it('leaves out a line for an empty list', async () => {
|
||||
const h = await buildQuoteHeader({ mode: 'forward', email: { ...email, cc: [] }, ...base });
|
||||
expect(h.text).toContain('\nTo: ');
|
||||
expect(h.text).not.toContain('Cc:');
|
||||
});
|
||||
});
|
||||
|
||||
+38
-2
@@ -20,6 +20,36 @@ export interface QuoteHeaderLabels {
|
||||
fromLabel: string;
|
||||
dateLabel: string;
|
||||
subjectLabel: string;
|
||||
/** The forward header's To and Cc lines; left out when not given. */
|
||||
toLabel?: string;
|
||||
ccLabel?: string;
|
||||
}
|
||||
|
||||
type HeaderAddress = { email?: string; name?: string };
|
||||
|
||||
/** "Name <address>, address", as the forward header's From line writes one. */
|
||||
function formatAddressList(list: HeaderAddress[] | undefined): string {
|
||||
return (list ?? [])
|
||||
.filter((r) => r.email)
|
||||
.map((r) => (r.name && r.name !== r.email ? `${r.name} <${r.email}>` : r.email as string))
|
||||
.join(", ");
|
||||
}
|
||||
|
||||
/**
|
||||
* The To and Cc lines of a forward header, as Gmail and Outlook write them:
|
||||
* one per list that has anyone on it, for a label the caller gave. Values
|
||||
* are plain text; escape them for HTML.
|
||||
*/
|
||||
export function forwardRecipientLines(
|
||||
email: { to?: HeaderAddress[]; cc?: HeaderAddress[] },
|
||||
labels: { toLabel?: string; ccLabel?: string },
|
||||
): { label: string; value: string }[] {
|
||||
const lines: { label: string; value: string }[] = [];
|
||||
const to = formatAddressList(email.to);
|
||||
const cc = formatAddressList(email.cc);
|
||||
if (labels.toLabel && to) lines.push({ label: labels.toLabel, value: to });
|
||||
if (labels.ccLabel && cc) lines.push({ label: labels.ccLabel, value: cc });
|
||||
return lines;
|
||||
}
|
||||
|
||||
interface BuildArgs {
|
||||
@@ -49,6 +79,8 @@ const DEFAULT_LABELS: QuoteHeaderLabels = {
|
||||
fromLabel: "From",
|
||||
dateLabel: "Date",
|
||||
subjectLabel: "Subject",
|
||||
toLabel: "To",
|
||||
ccLabel: "Cc",
|
||||
};
|
||||
|
||||
function defaultHeader(args: BuildArgs): QuoteHeader {
|
||||
@@ -73,12 +105,16 @@ function defaultHeader(args: BuildArgs): QuoteHeader {
|
||||
const subject = email.subject || "";
|
||||
|
||||
if (mode === "forward") {
|
||||
const text = `${labels.forwardedSeparator}\n${labels.fromLabel}: ${fromStrFull}\n${labels.dateLabel}: ${date}\n${labels.subjectLabel}: ${subject}\n`;
|
||||
const recipients = forwardRecipientLines(email, labels);
|
||||
const text = `${labels.forwardedSeparator}\n${labels.fromLabel}: ${fromStrFull}\n${labels.dateLabel}: ${date}\n${labels.subjectLabel}: ${subject}\n`
|
||||
+ recipients.map((line) => `${line.label}: ${line.value}\n`).join("");
|
||||
// Escape the interpolated values for the HTML variant: the sender string is
|
||||
// "Name <email>", and the unescaped "<email>" would be parsed as an HTML tag
|
||||
// by the rich-text composer and silently dropped (#482). Subject/name are
|
||||
// likewise user-controlled. Label/separator strings are trusted i18n text.
|
||||
const html = `<div>${labels.forwardedSeparator}<br>${labels.fromLabel}: ${escapeHtml(fromStrFull)}<br>${labels.dateLabel}: ${escapeHtml(date)}<br>${labels.subjectLabel}: ${escapeHtml(subject)}<br><br></div>`;
|
||||
const html = `<div>${labels.forwardedSeparator}<br>${labels.fromLabel}: ${escapeHtml(fromStrFull)}<br>${labels.dateLabel}: ${escapeHtml(date)}<br>${labels.subjectLabel}: ${escapeHtml(subject)}<br>`
|
||||
+ recipients.map((line) => `${line.label}: ${escapeHtml(line.value)}<br>`).join("")
|
||||
+ `<br></div>`;
|
||||
return { html, text, wrapInBlockquote: false };
|
||||
}
|
||||
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "تشفير…",
|
||||
"encrypt_hint": "يتطلب إضافة S/MIME أو PGP. يفتح الإعدادات.",
|
||||
"quick_reply_to": "الرد على {names}…",
|
||||
"open_full_editor": "فتح المحرر الكامل"
|
||||
"open_full_editor": "فتح المحرر الكامل",
|
||||
"forward_to_label": "إلى",
|
||||
"forward_cc_label": "نسخة"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "المرشحات",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Xifra…",
|
||||
"encrypt_hint": "Cal un complement S/MIME o PGP. Obre la configuració.",
|
||||
"quick_reply_to": "Respon a {names}…",
|
||||
"open_full_editor": "Obre l'editor complet"
|
||||
"open_full_editor": "Obre l'editor complet",
|
||||
"forward_to_label": "Per a",
|
||||
"forward_cc_label": "CC"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtres",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Šifrovat…",
|
||||
"encrypt_hint": "Vyžaduje doplněk S/MIME nebo PGP. Otevře Nastavení.",
|
||||
"quick_reply_to": "Odpověď pro {names}…",
|
||||
"open_full_editor": "Otevřít v plném editoru"
|
||||
"open_full_editor": "Otevřít v plném editoru",
|
||||
"forward_to_label": "Komu",
|
||||
"forward_cc_label": "Kopie"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtry",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Krypter…",
|
||||
"encrypt_hint": "Kræver en S/MIME- eller PGP-tilføjelse. Åbner Indstillinger.",
|
||||
"quick_reply_to": "Svar til {names}…",
|
||||
"open_full_editor": "Åbn fuld editor"
|
||||
"open_full_editor": "Åbn fuld editor",
|
||||
"forward_to_label": "Til",
|
||||
"forward_cc_label": "Cc"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtre",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Verschlüsseln…",
|
||||
"encrypt_hint": "Dafür brauchen Sie eine S/MIME- oder PGP-Erweiterung. Öffnet die Einstellungen.",
|
||||
"quick_reply_to": "Antwort an {names}…",
|
||||
"open_full_editor": "Im vollen Editor öffnen"
|
||||
"open_full_editor": "Im vollen Editor öffnen",
|
||||
"forward_to_label": "An",
|
||||
"forward_cc_label": "Cc"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filter",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Encrypt…",
|
||||
"encrypt_hint": "Needs an S/MIME or PGP add-on. Opens Settings.",
|
||||
"quick_reply_to": "Reply to {names}…",
|
||||
"open_full_editor": "Open full editor"
|
||||
"open_full_editor": "Open full editor",
|
||||
"forward_to_label": "To",
|
||||
"forward_cc_label": "Cc"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filters",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Cifrar…",
|
||||
"encrypt_hint": "Necesita un complemento S/MIME o PGP. Abre la Configuración.",
|
||||
"quick_reply_to": "Responder a {names}…",
|
||||
"open_full_editor": "Abrir el editor completo"
|
||||
"open_full_editor": "Abrir el editor completo",
|
||||
"forward_to_label": "Para",
|
||||
"forward_cc_label": "CC"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtros",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Krüpteeri…",
|
||||
"encrypt_hint": "Vajab S/MIME või PGP lisa. Avab seaded.",
|
||||
"quick_reply_to": "Vastus saajale {names}…",
|
||||
"open_full_editor": "Ava täielik redaktor"
|
||||
"open_full_editor": "Ava täielik redaktor",
|
||||
"forward_to_label": "Saajale",
|
||||
"forward_cc_label": "Koopia"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtrid",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "رمزگذاری…",
|
||||
"encrypt_hint": "به افزونهٔ S/MIME یا PGP نیاز دارد. تنظیمات را باز میکند.",
|
||||
"quick_reply_to": "پاسخ به {names}…",
|
||||
"open_full_editor": "باز کردن ویرایشگر کامل"
|
||||
"open_full_editor": "باز کردن ویرایشگر کامل",
|
||||
"forward_to_label": "به",
|
||||
"forward_cc_label": "رونوشت"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "فیلترها",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Chiffrer…",
|
||||
"encrypt_hint": "Nécessite une extension S/MIME ou PGP. Ouvre les Paramètres.",
|
||||
"quick_reply_to": "Répondre à {names}…",
|
||||
"open_full_editor": "Ouvrir l'éditeur complet"
|
||||
"open_full_editor": "Ouvrir l'éditeur complet",
|
||||
"forward_to_label": "À",
|
||||
"forward_cc_label": "Cc"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtres",
|
||||
|
||||
@@ -6399,7 +6399,9 @@
|
||||
"encrypt": "הצפנה…",
|
||||
"encrypt_hint": "דורש תוסף S/MIME או PGP. פותח את ההגדרות.",
|
||||
"quick_reply_to": "תשובה אל {names}…",
|
||||
"open_full_editor": "פתיחה בעורך המלא"
|
||||
"open_full_editor": "פתיחה בעורך המלא",
|
||||
"forward_to_label": "אל",
|
||||
"forward_cc_label": "עותק"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "מסננים",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Titkosítás…",
|
||||
"encrypt_hint": "S/MIME vagy PGP bővítmény kell hozzá. Megnyitja a Beállításokat.",
|
||||
"quick_reply_to": "Válasz neki: {names}…",
|
||||
"open_full_editor": "Megnyitás a teljes szerkesztőben"
|
||||
"open_full_editor": "Megnyitás a teljes szerkesztőben",
|
||||
"forward_to_label": "Címzett",
|
||||
"forward_cc_label": "Másolat"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Szűrők",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Cifra…",
|
||||
"encrypt_hint": "Serve un componente aggiuntivo S/MIME o PGP. Apre le Impostazioni.",
|
||||
"quick_reply_to": "Rispondi a {names}…",
|
||||
"open_full_editor": "Apri l'editor completo"
|
||||
"open_full_editor": "Apri l'editor completo",
|
||||
"forward_to_label": "A",
|
||||
"forward_cc_label": "Cc"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtri",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "暗号化…",
|
||||
"encrypt_hint": "S/MIME または PGP のアドオンが必要です。設定を開きます。",
|
||||
"quick_reply_to": "{names} に返信…",
|
||||
"open_full_editor": "フルエディターで開く"
|
||||
"open_full_editor": "フルエディターで開く",
|
||||
"forward_to_label": "宛先",
|
||||
"forward_cc_label": "CC"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "フィルター",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "암호화…",
|
||||
"encrypt_hint": "S/MIME 또는 PGP 추가 기능이 필요합니다. 설정을 엽니다.",
|
||||
"quick_reply_to": "{names}에게 답장…",
|
||||
"open_full_editor": "전체 편집기에서 열기"
|
||||
"open_full_editor": "전체 편집기에서 열기",
|
||||
"forward_to_label": "받는 사람",
|
||||
"forward_cc_label": "참조"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "필터",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Šifrēt…",
|
||||
"encrypt_hint": "Nepieciešams S/MIME vai PGP papildinājums. Atver iestatījumus.",
|
||||
"quick_reply_to": "Atbilde: {names}…",
|
||||
"open_full_editor": "Atvērt pilnajā redaktorā"
|
||||
"open_full_editor": "Atvērt pilnajā redaktorā",
|
||||
"forward_to_label": "Kam",
|
||||
"forward_cc_label": "Kopija"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtri",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Шифрлэх…",
|
||||
"encrypt_hint": "S/MIME эсвэл PGP нэмэлт хэрэгтэй. Тохиргоог нээнэ.",
|
||||
"quick_reply_to": "{names} руу хариу бичих…",
|
||||
"open_full_editor": "Бүтэн засварлагчид нээх"
|
||||
"open_full_editor": "Бүтэн засварлагчид нээх",
|
||||
"forward_to_label": "Хэнд",
|
||||
"forward_cc_label": "Хуулбар"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Шүүлтүүр",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Krypter…",
|
||||
"encrypt_hint": "Krever et S/MIME- eller PGP-tillegg. Åpner Innstillinger.",
|
||||
"quick_reply_to": "Svar til {names}…",
|
||||
"open_full_editor": "Åpne i full redigering"
|
||||
"open_full_editor": "Åpne i full redigering",
|
||||
"forward_to_label": "Til",
|
||||
"forward_cc_label": "Kopi"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtre",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Versleutelen…",
|
||||
"encrypt_hint": "Vereist een S/MIME- of PGP-add-on. Opent Instellingen.",
|
||||
"quick_reply_to": "Antwoord aan {names}…",
|
||||
"open_full_editor": "Openen in volledige editor"
|
||||
"open_full_editor": "Openen in volledige editor",
|
||||
"forward_to_label": "Aan",
|
||||
"forward_cc_label": "CC"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filters",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Zaszyfruj…",
|
||||
"encrypt_hint": "Wymaga dodatku S/MIME lub PGP. Otwiera Ustawienia.",
|
||||
"quick_reply_to": "Odpowiedź do: {names}…",
|
||||
"open_full_editor": "Otwórz w pełnym edytorze"
|
||||
"open_full_editor": "Otwórz w pełnym edytorze",
|
||||
"forward_to_label": "Do",
|
||||
"forward_cc_label": "DW"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtry",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Cifrar…",
|
||||
"encrypt_hint": "Precisa de um extra S/MIME ou PGP. Abre as Configurações.",
|
||||
"quick_reply_to": "Responder a {names}…",
|
||||
"open_full_editor": "Abrir no editor completo"
|
||||
"open_full_editor": "Abrir no editor completo",
|
||||
"forward_to_label": "Para",
|
||||
"forward_cc_label": "Cc"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtros",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Criptează…",
|
||||
"encrypt_hint": "Necesită un supliment S/MIME sau PGP. Deschide Setările.",
|
||||
"quick_reply_to": "Răspuns către {names}…",
|
||||
"open_full_editor": "Deschide în editorul complet"
|
||||
"open_full_editor": "Deschide în editorul complet",
|
||||
"forward_to_label": "Către",
|
||||
"forward_cc_label": "Cc"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtre",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Зашифровать…",
|
||||
"encrypt_hint": "Нужно дополнение S/MIME или PGP. Открывает настройки.",
|
||||
"quick_reply_to": "Ответ для {names}…",
|
||||
"open_full_editor": "Открыть в полном редакторе"
|
||||
"open_full_editor": "Открыть в полном редакторе",
|
||||
"forward_to_label": "Кому",
|
||||
"forward_cc_label": "Копия"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Фильтры",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Šifrovať…",
|
||||
"encrypt_hint": "Vyžaduje doplnok S/MIME alebo PGP. Otvorí Nastavenia.",
|
||||
"quick_reply_to": "Odpoveď pre {names}…",
|
||||
"open_full_editor": "Otvoriť v plnom editore"
|
||||
"open_full_editor": "Otvoriť v plnom editore",
|
||||
"forward_to_label": "Komu",
|
||||
"forward_cc_label": "Kópia"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtre",
|
||||
|
||||
@@ -3319,7 +3319,9 @@
|
||||
"encrypt": "เข้ารหัส…",
|
||||
"encrypt_hint": "ต้องใช้ส่วนเสริม S/MIME หรือ PGP จะเปิดการตั้งค่า",
|
||||
"quick_reply_to": "ตอบกลับถึง {names}…",
|
||||
"open_full_editor": "เปิดในตัวแก้ไขเต็มรูปแบบ"
|
||||
"open_full_editor": "เปิดในตัวแก้ไขเต็มรูปแบบ",
|
||||
"forward_to_label": "ถึง",
|
||||
"forward_cc_label": "สำเนา"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "ตัวกรอง",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Şifrele…",
|
||||
"encrypt_hint": "S/MIME veya PGP eklentisi gerekir. Ayarlar'ı açar.",
|
||||
"quick_reply_to": "Yanıtla: {names}…",
|
||||
"open_full_editor": "Tam düzenleyicide aç"
|
||||
"open_full_editor": "Tam düzenleyicide aç",
|
||||
"forward_to_label": "Kime",
|
||||
"forward_cc_label": "Cc"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Filtreler",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "Зашифрувати…",
|
||||
"encrypt_hint": "Потрібен додаток S/MIME або PGP. Відкриває налаштування.",
|
||||
"quick_reply_to": "Відповідь для {names}…",
|
||||
"open_full_editor": "Відкрити в повному редакторі"
|
||||
"open_full_editor": "Відкрити в повному редакторі",
|
||||
"forward_to_label": "Кому",
|
||||
"forward_cc_label": "Копія"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Фільтри",
|
||||
|
||||
@@ -6410,7 +6410,9 @@
|
||||
"encrypt": "Mã hóa…",
|
||||
"encrypt_hint": "Cần tiện ích S/MIME hoặc PGP. Mở Cài đặt.",
|
||||
"quick_reply_to": "Trả lời {names}…",
|
||||
"open_full_editor": "Mở trong trình soạn thảo đầy đủ"
|
||||
"open_full_editor": "Mở trong trình soạn thảo đầy đủ",
|
||||
"forward_to_label": "Đến",
|
||||
"forward_cc_label": "CC"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "Bộ lọc",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "加密…",
|
||||
"encrypt_hint": "需要 S/MIME 或 PGP 附加元件。將開啟設定。",
|
||||
"quick_reply_to": "回覆給 {names}…",
|
||||
"open_full_editor": "在完整編輯器中開啟"
|
||||
"open_full_editor": "在完整編輯器中開啟",
|
||||
"forward_to_label": "收件者",
|
||||
"forward_cc_label": "副本"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "篩選",
|
||||
|
||||
@@ -6398,7 +6398,9 @@
|
||||
"encrypt": "加密…",
|
||||
"encrypt_hint": "需要 S/MIME 或 PGP 附加组件。将打开设置。",
|
||||
"quick_reply_to": "回复 {names}…",
|
||||
"open_full_editor": "在完整编辑器中打开"
|
||||
"open_full_editor": "在完整编辑器中打开",
|
||||
"forward_to_label": "收件人",
|
||||
"forward_cc_label": "抄送"
|
||||
},
|
||||
"plugin_list": {
|
||||
"filters": "筛选",
|
||||
|
||||
Reference in New Issue
Block a user