mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 16:14:07 +00:00
feat: put pictures dropped on an open composer into the message text
This commit is contained in:
@@ -2790,6 +2790,19 @@ export function EmailComposer({
|
||||
useEffect(() => setComposerFileTarget(
|
||||
(files) => void addFiles(files),
|
||||
() => !!composerRootEl?.isConnected && composerRootEl.getClientRects().length > 0,
|
||||
// The file drop overlay's "Insert into the message": the editor's own paste, so the
|
||||
// upload, the place and the question for a description are the same as pasting.
|
||||
{
|
||||
available: () => !!editorRef.current && !editorRef.current.isDestroyed,
|
||||
insert: (files) => {
|
||||
const editor = editorRef.current;
|
||||
if (!editor || editor.isDestroyed) return;
|
||||
const transfer = new DataTransfer();
|
||||
for (const file of files) transfer.items.add(file);
|
||||
editor.commands.focus();
|
||||
editor.view.dom.dispatchEvent(new ClipboardEvent('paste', { clipboardData: transfer, cancelable: true }));
|
||||
},
|
||||
},
|
||||
), [addFiles, composerRootEl]);
|
||||
// The id plugins know this composer by (registered below, with the handle).
|
||||
const [pluginComposerId, setPluginComposerId] = useState<string | null>(null);
|
||||
|
||||
@@ -131,6 +131,21 @@ describe('a drag from outside', () => {
|
||||
clear();
|
||||
});
|
||||
|
||||
it('a picture dropped on "Insert into the message" goes into the text, as a paste would', async () => {
|
||||
const take = vi.fn();
|
||||
const insert = vi.fn();
|
||||
const clear = setComposerFileTarget(take, () => true, { available: () => true, insert });
|
||||
const picture = new File(['png'], 'chart.png', { type: 'image/png' });
|
||||
render(<FileDropHost />);
|
||||
dragIn([picture]);
|
||||
expect(zone('insert-inline').textContent).toContain('zone_insert_inline');
|
||||
fireEvent.drop(zone('insert-inline'), { dataTransfer: transfer([picture]) });
|
||||
await flush();
|
||||
expect(insert).toHaveBeenCalledWith([picture]);
|
||||
expect(take).not.toHaveBeenCalled();
|
||||
clear();
|
||||
});
|
||||
|
||||
it('on the Files screen, "Upload to <folder>" hands the files to that folder', async () => {
|
||||
const take = vi.fn();
|
||||
const clear = setFilesDropTarget({ folder: 'Reports', take, visible: () => true });
|
||||
|
||||
@@ -68,6 +68,7 @@ function currentPlace(pathname: string, translate: (key: string) => string): { p
|
||||
const folder = importFolder(pathname, translate);
|
||||
const place: DropPlace = {
|
||||
composer: !!visibleComposer(),
|
||||
inlineImages: !!visibleComposer()?.images?.available(),
|
||||
popout: isPopoutPath(window.location.pathname),
|
||||
files: !!client?.supportsFiles?.() && features.isFeatureEnabled("filesEnabled"),
|
||||
filesFolder: filesDropTarget()?.folder ?? null,
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
import { forwardRef, type ComponentType, type ReactNode } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { CalendarPlus, FolderInput, MailOpen, Paperclip, SquarePen, Upload, UserPlus } from "@/components/icons";
|
||||
import { CalendarPlus, FolderInput, ImageIcon, MailOpen, Paperclip, SquarePen, Upload, UserPlus } from "@/components/icons";
|
||||
import type { FileDropNote, FileDropZone, FileDropZoneId } from "@/lib/file-drop/zones";
|
||||
import { cn, formatFileSize } from "@/lib/utils";
|
||||
|
||||
const ICONS: Record<FileDropZoneId, ComponentType<{ className?: string }>> = {
|
||||
"attach-here": Paperclip,
|
||||
"insert-inline": ImageIcon,
|
||||
"new-message": SquarePen,
|
||||
upload: Upload,
|
||||
calendar: CalendarPlus,
|
||||
@@ -21,6 +22,7 @@ export function useZoneText() {
|
||||
const label = (zone: FileDropZone): string => {
|
||||
switch (zone.id) {
|
||||
case "attach-here": return t("zone_attach_here");
|
||||
case "insert-inline": return t("zone_insert_inline");
|
||||
case "new-message": return t("zone_new_message");
|
||||
case "upload": return zone.folder ? t("zone_upload_folder", { folder: zone.folder }) : t("zone_upload_files");
|
||||
case "calendar": return t("zone_calendar");
|
||||
|
||||
@@ -4,10 +4,17 @@
|
||||
* on is the app's own native one, which is no new file.
|
||||
*/
|
||||
|
||||
/** Pictures put in the text where the cursor is, as pasting them does. Only while the rich text editor is there. */
|
||||
export interface ComposerImages {
|
||||
available: () => boolean;
|
||||
insert: (files: File[]) => void;
|
||||
}
|
||||
|
||||
interface ComposerTarget {
|
||||
take: (files: File[]) => unknown;
|
||||
/** The Pro shell keeps composers mounted on tabs out of sight. */
|
||||
visible: () => boolean;
|
||||
images?: ComposerImages;
|
||||
}
|
||||
|
||||
const composerTargets: ComposerTarget[] = [];
|
||||
@@ -15,8 +22,8 @@ const composerTargets: ComposerTarget[] = [];
|
||||
let ownDrag = false;
|
||||
|
||||
/** A composer on screen takes files itself. */
|
||||
export function setComposerFileTarget(take: ComposerTarget['take'], visible: ComposerTarget['visible']): () => void {
|
||||
const target = { take, visible };
|
||||
export function setComposerFileTarget(take: ComposerTarget['take'], visible: ComposerTarget['visible'], images?: ComposerImages): () => void {
|
||||
const target = { take, visible, images };
|
||||
composerTargets.push(target);
|
||||
return () => {
|
||||
const at = composerTargets.indexOf(target);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { defaultZone, fileDropZones, filesWithinLimits, kindForType, MAX_ZONES,
|
||||
|
||||
const place = (patch: Partial<DropPlace> = {}): DropPlace => ({
|
||||
composer: false,
|
||||
inlineImages: false,
|
||||
popout: false,
|
||||
files: true,
|
||||
filesFolder: null,
|
||||
@@ -59,6 +60,16 @@ describe('the file drop zones', () => {
|
||||
expect(zones).toEqual(['attach-here', 'open-eml', 'new-message', 'upload']);
|
||||
});
|
||||
|
||||
it('pictures go into the text of an open rich text composer, beside attaching them', () => {
|
||||
expect(ids(drag(['image', 'image']), place({ composer: true, inlineImages: true }))).toEqual(['attach-here', 'insert-inline', 'new-message', 'upload']);
|
||||
// Plain text, a mix, or no composer: no such choice.
|
||||
expect(ids(drag(['image']), place({ composer: true }))).toEqual(['attach-here', 'new-message', 'upload']);
|
||||
expect(ids(drag(['image', 'other']), place({ composer: true, inlineImages: true }))).toEqual(['attach-here', 'new-message', 'upload']);
|
||||
expect(ids(drag(['image']), place({ inlineImages: true }))).toEqual(['new-message', 'upload']);
|
||||
expect(kindForType('image/png')).toBe('image');
|
||||
expect(kindForType('image/svg+xml')).toBe('other');
|
||||
});
|
||||
|
||||
it('in a pop-out, only its own composer', () => {
|
||||
expect(ids(drag(['calendar']), place({ popout: true, composer: true }))).toEqual(['attach-here']);
|
||||
expect(ids(drag(['other']), place({ popout: true }))).toEqual([]);
|
||||
|
||||
@@ -39,6 +39,13 @@ export async function runFileDropZone(zone: FileDropZoneId, files: File[], env:
|
||||
else handOffDraft({ files });
|
||||
return;
|
||||
}
|
||||
case 'insert-inline': {
|
||||
const composer = visibleComposer();
|
||||
if (composer?.images?.available()) composer.images.insert(files);
|
||||
else if (composer) composer.take(files);
|
||||
else handOffDraft({ files });
|
||||
return;
|
||||
}
|
||||
case 'new-message':
|
||||
handOffDraft({ files });
|
||||
return;
|
||||
|
||||
+15
-3
@@ -13,9 +13,9 @@
|
||||
|
||||
import { kindForFile, type HandoffKind } from '@/lib/launch-handoff';
|
||||
|
||||
export type FileDropZoneId = 'attach-here' | 'new-message' | 'upload' | 'calendar' | 'contacts' | 'open-eml' | 'import-eml';
|
||||
export type FileDropZoneId = 'attach-here' | 'insert-inline' | 'new-message' | 'upload' | 'calendar' | 'contacts' | 'open-eml' | 'import-eml';
|
||||
|
||||
export type DroppedKind = Exclude<HandoffKind, 'compose'> | 'other';
|
||||
export type DroppedKind = Exclude<HandoffKind, 'compose'> | 'image' | 'other';
|
||||
|
||||
export interface DropFiles {
|
||||
/** Unknown during a drag in some browsers (Safari gives no items). */
|
||||
@@ -29,6 +29,8 @@ export interface DropFiles {
|
||||
export interface DropPlace {
|
||||
/** A composer is on screen and takes files. */
|
||||
composer: boolean;
|
||||
/** Its rich text editor can take pictures into the text. */
|
||||
inlineImages: boolean;
|
||||
/** A pop-out window: only its own composer, nothing that needs the main window. */
|
||||
popout: boolean;
|
||||
/** Files is on for this account and its server has it. */
|
||||
@@ -66,15 +68,23 @@ export interface FileDropZone {
|
||||
export const MAX_ZONES = 4;
|
||||
|
||||
/** What a dragged item's MIME type says. Empty or anything else: no special choice. */
|
||||
/** What a mail can show in its text. Not SVG: it can carry script, and mail clients drop it. */
|
||||
const INLINE_IMAGE_TYPES = new Set(['image/png', 'image/jpeg', 'image/gif', 'image/webp']);
|
||||
const INLINE_IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp']);
|
||||
|
||||
function dropped(kind: HandoffKind | null): DroppedKind {
|
||||
return kind && kind !== 'compose' ? kind : 'other';
|
||||
}
|
||||
|
||||
export function kindForType(type: string): DroppedKind {
|
||||
if (INLINE_IMAGE_TYPES.has(type.toLowerCase())) return 'image';
|
||||
return dropped(kindForFile({ name: '', type }));
|
||||
}
|
||||
|
||||
export function kindForName(file: { name: string; type?: string }): DroppedKind {
|
||||
const dot = file.name.lastIndexOf('.');
|
||||
const extension = dot >= 0 ? file.name.slice(dot).toLowerCase() : '';
|
||||
if (INLINE_IMAGE_TYPES.has((file.type ?? '').toLowerCase()) || INLINE_IMAGE_EXTENSIONS.has(extension)) return 'image';
|
||||
return dropped(kindForFile(file));
|
||||
}
|
||||
|
||||
@@ -109,6 +119,8 @@ export function fileDropZones(files: DropFiles, place: DropPlace): FileDropZone[
|
||||
|
||||
// What this kind of file is for, best first. Not in a pop-out: each opens on its own page.
|
||||
const special: FileDropZone[] = [];
|
||||
// Pictures into the text, as pasting them does: also in a pop-out, it's that composer's own.
|
||||
if (kind === 'image' && place.composer && place.inlineImages) special.push({ id: 'insert-inline' });
|
||||
if (!place.popout) {
|
||||
if (kind === 'eml' && one) special.push({ id: 'open-eml' });
|
||||
if (kind === 'eml' && place.importFolder) special.push(serverOnly(place, { id: 'import-eml', folder: place.importFolder }));
|
||||
@@ -132,7 +144,7 @@ export function filesWithinLimits(
|
||||
const perFile = place.maxUploadBytes;
|
||||
const fitting = perFile > 0 ? files.filter((file) => file.size <= perFile) : [...files];
|
||||
const tooLarge = files.filter((file) => !fitting.includes(file));
|
||||
const mail = zone === 'attach-here' || zone === 'new-message';
|
||||
const mail = zone === 'attach-here' || zone === 'new-message' || zone === 'insert-inline';
|
||||
const total = fitting.reduce((sum, file) => sum + file.size, 0);
|
||||
const overMessage = mail && place.maxMessageBytes > 0 && total > place.maxMessageBytes;
|
||||
return { fitting: overMessage ? [] : fitting, tooLarge, overMessage };
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, zero {لا ملفات} one {ملف واحد} two {ملفان} few {# ملفات} many {# ملفًا} other {# ملف}}",
|
||||
"files_summary_size": "{count, plural, zero {لا ملفات} one {ملف واحد} two {ملفان} few {# ملفات} many {# ملفًا} other {# ملف}}, {size}",
|
||||
"zone_attach_here": "إرفاق بهذه الرسالة",
|
||||
"zone_insert_inline": "إدراج في نص الرسالة",
|
||||
"zone_new_message": "إرفاق برسالة جديدة",
|
||||
"zone_upload_files": "رفع إلى الملفات",
|
||||
"zone_upload_folder": "رفع إلى {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# fitxer} other {# fitxers}}",
|
||||
"files_summary_size": "{count, plural, one {# fitxer} other {# fitxers}}, {size}",
|
||||
"zone_attach_here": "Adjunta a aquest missatge",
|
||||
"zone_insert_inline": "Insereix al missatge",
|
||||
"zone_new_message": "Adjunta a un missatge nou",
|
||||
"zone_upload_files": "Puja a Fitxers",
|
||||
"zone_upload_folder": "Puja a {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# soubor} few {# soubory} many {# souboru} other {# souborů}}",
|
||||
"files_summary_size": "{count, plural, one {# soubor} few {# soubory} many {# souboru} other {# souborů}}, {size}",
|
||||
"zone_attach_here": "Připojit k této zprávě",
|
||||
"zone_insert_inline": "Vložit do zprávy",
|
||||
"zone_new_message": "Připojit k nové zprávě",
|
||||
"zone_upload_files": "Nahrát do Souborů",
|
||||
"zone_upload_folder": "Nahrát do {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# fil} other {# filer}}",
|
||||
"files_summary_size": "{count, plural, one {# fil} other {# filer}}, {size}",
|
||||
"zone_attach_here": "Vedhæft til denne besked",
|
||||
"zone_insert_inline": "Indsæt i beskeden",
|
||||
"zone_new_message": "Vedhæft til en ny besked",
|
||||
"zone_upload_files": "Upload til Filer",
|
||||
"zone_upload_folder": "Upload til {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# Datei} other {# Dateien}}",
|
||||
"files_summary_size": "{count, plural, one {# Datei} other {# Dateien}}, {size}",
|
||||
"zone_attach_here": "An diese Nachricht anhängen",
|
||||
"zone_insert_inline": "In die Nachricht einfügen",
|
||||
"zone_new_message": "An neue Nachricht anhängen",
|
||||
"zone_upload_files": "In Dateien hochladen",
|
||||
"zone_upload_folder": "In {folder} hochladen",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# file} other {# files}}",
|
||||
"files_summary_size": "{count, plural, one {# file} other {# files}}, {size}",
|
||||
"zone_attach_here": "Attach to this message",
|
||||
"zone_insert_inline": "Insert into the message",
|
||||
"zone_new_message": "Attach to a new message",
|
||||
"zone_upload_files": "Upload to Files",
|
||||
"zone_upload_folder": "Upload to {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# archivo} other {# archivos}}",
|
||||
"files_summary_size": "{count, plural, one {# archivo} other {# archivos}}, {size}",
|
||||
"zone_attach_here": "Adjuntar a este mensaje",
|
||||
"zone_insert_inline": "Insertar en el mensaje",
|
||||
"zone_new_message": "Adjuntar a un mensaje nuevo",
|
||||
"zone_upload_files": "Subir a Archivos",
|
||||
"zone_upload_folder": "Subir a {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# fail} other {# faili}}",
|
||||
"files_summary_size": "{count, plural, one {# fail} other {# faili}}, {size}",
|
||||
"zone_attach_here": "Lisa sellele kirjale",
|
||||
"zone_insert_inline": "Lisa kirja sisse",
|
||||
"zone_new_message": "Lisa uuele kirjale",
|
||||
"zone_upload_files": "Laadi üles kausta Failid",
|
||||
"zone_upload_folder": "Laadi üles kausta {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# فایل} other {# فایل}}",
|
||||
"files_summary_size": "{count, plural, one {# فایل} other {# فایل}}, {size}",
|
||||
"zone_attach_here": "پیوست به این پیام",
|
||||
"zone_insert_inline": "درج در متن پیام",
|
||||
"zone_new_message": "پیوست به پیام جدید",
|
||||
"zone_upload_files": "بارگذاری در فایلها",
|
||||
"zone_upload_folder": "بارگذاری در {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# fichier} other {# fichiers}}",
|
||||
"files_summary_size": "{count, plural, one {# fichier} other {# fichiers}}, {size}",
|
||||
"zone_attach_here": "Joindre à ce message",
|
||||
"zone_insert_inline": "Insérer dans le message",
|
||||
"zone_new_message": "Joindre à un nouveau message",
|
||||
"zone_upload_files": "Envoyer dans Fichiers",
|
||||
"zone_upload_folder": "Envoyer dans {folder}",
|
||||
|
||||
@@ -7115,6 +7115,7 @@
|
||||
"files_summary": "{count, plural, one {קובץ אחד} two {שני קבצים} other {# קבצים}}",
|
||||
"files_summary_size": "{count, plural, one {קובץ אחד} two {שני קבצים} other {# קבצים}}, {size}",
|
||||
"zone_attach_here": "צירוף להודעה הזו",
|
||||
"zone_insert_inline": "הוספה לגוף ההודעה",
|
||||
"zone_new_message": "צירוף להודעה חדשה",
|
||||
"zone_upload_files": "העלאה לקבצים",
|
||||
"zone_upload_folder": "העלאה אל {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# fájl} other {# fájl}}",
|
||||
"files_summary_size": "{count, plural, one {# fájl} other {# fájl}}, {size}",
|
||||
"zone_attach_here": "Csatolás ehhez az üzenethez",
|
||||
"zone_insert_inline": "Beszúrás az üzenetbe",
|
||||
"zone_new_message": "Csatolás új üzenethez",
|
||||
"zone_upload_files": "Feltöltés a Fájlokba",
|
||||
"zone_upload_folder": "Feltöltés ide: {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# file} other {# file}}",
|
||||
"files_summary_size": "{count, plural, one {# file} other {# file}}, {size}",
|
||||
"zone_attach_here": "Allega a questo messaggio",
|
||||
"zone_insert_inline": "Inserisci nel messaggio",
|
||||
"zone_new_message": "Allega a un nuovo messaggio",
|
||||
"zone_upload_files": "Carica in File",
|
||||
"zone_upload_folder": "Carica in {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, other {# 個のファイル}}",
|
||||
"files_summary_size": "{count, plural, other {# 個のファイル}}, {size}",
|
||||
"zone_attach_here": "このメッセージに添付",
|
||||
"zone_insert_inline": "メッセージ本文に挿入",
|
||||
"zone_new_message": "新規メッセージに添付",
|
||||
"zone_upload_files": "ファイルにアップロード",
|
||||
"zone_upload_folder": "{folder} にアップロード",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, other {파일 #개}}",
|
||||
"files_summary_size": "{count, plural, other {파일 #개}}, {size}",
|
||||
"zone_attach_here": "이 메시지에 첨부",
|
||||
"zone_insert_inline": "메시지 본문에 삽입",
|
||||
"zone_new_message": "새 메시지에 첨부",
|
||||
"zone_upload_files": "파일에 업로드",
|
||||
"zone_upload_folder": "{folder}에 업로드",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, zero {# failu} one {# fails} other {# faili}}",
|
||||
"files_summary_size": "{count, plural, zero {# failu} one {# fails} other {# faili}}, {size}",
|
||||
"zone_attach_here": "Pievienot šim ziņojumam",
|
||||
"zone_insert_inline": "Ievietot ziņojumā",
|
||||
"zone_new_message": "Pievienot jaunam ziņojumam",
|
||||
"zone_upload_files": "Augšupielādēt failos",
|
||||
"zone_upload_folder": "Augšupielādēt mapē {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# файл} other {# файл}}",
|
||||
"files_summary_size": "{count, plural, one {# файл} other {# файл}}, {size}",
|
||||
"zone_attach_here": "Энэ мессежид хавсаргах",
|
||||
"zone_insert_inline": "Мессежийн дотор оруулах",
|
||||
"zone_new_message": "Шинэ мессежид хавсаргах",
|
||||
"zone_upload_files": "Файл руу байршуулах",
|
||||
"zone_upload_folder": "{folder} руу байршуулах",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# fil} other {# filer}}",
|
||||
"files_summary_size": "{count, plural, one {# fil} other {# filer}}, {size}",
|
||||
"zone_attach_here": "Legg ved denne meldingen",
|
||||
"zone_insert_inline": "Sett inn i meldingen",
|
||||
"zone_new_message": "Legg ved en ny melding",
|
||||
"zone_upload_files": "Last opp til Filer",
|
||||
"zone_upload_folder": "Last opp til {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# bestand} other {# bestanden}}",
|
||||
"files_summary_size": "{count, plural, one {# bestand} other {# bestanden}}, {size}",
|
||||
"zone_attach_here": "Bij dit bericht voegen",
|
||||
"zone_insert_inline": "In het bericht invoegen",
|
||||
"zone_new_message": "Bij een nieuw bericht voegen",
|
||||
"zone_upload_files": "Uploaden naar Bestanden",
|
||||
"zone_upload_folder": "Uploaden naar {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# plik} few {# pliki} many {# plików} other {# pliku}}",
|
||||
"files_summary_size": "{count, plural, one {# plik} few {# pliki} many {# plików} other {# pliku}}, {size}",
|
||||
"zone_attach_here": "Załącz do tej wiadomości",
|
||||
"zone_insert_inline": "Wstaw do wiadomości",
|
||||
"zone_new_message": "Załącz do nowej wiadomości",
|
||||
"zone_upload_files": "Prześlij do Plików",
|
||||
"zone_upload_folder": "Prześlij do {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# arquivo} other {# arquivos}}",
|
||||
"files_summary_size": "{count, plural, one {# arquivo} other {# arquivos}}, {size}",
|
||||
"zone_attach_here": "Anexar a esta mensagem",
|
||||
"zone_insert_inline": "Inserir na mensagem",
|
||||
"zone_new_message": "Anexar a uma nova mensagem",
|
||||
"zone_upload_files": "Enviar para Arquivos",
|
||||
"zone_upload_folder": "Enviar para {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# fișier} few {# fișiere} other {# de fișiere}}",
|
||||
"files_summary_size": "{count, plural, one {# fișier} few {# fișiere} other {# de fișiere}}, {size}",
|
||||
"zone_attach_here": "Atașați la acest mesaj",
|
||||
"zone_insert_inline": "Inserați în mesaj",
|
||||
"zone_new_message": "Atașați la un mesaj nou",
|
||||
"zone_upload_files": "Încărcați în Fișiere",
|
||||
"zone_upload_folder": "Încărcați în {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# файл} few {# файла} many {# файлов} other {# файла}}",
|
||||
"files_summary_size": "{count, plural, one {# файл} few {# файла} many {# файлов} other {# файла}}, {size}",
|
||||
"zone_attach_here": "Прикрепить к этому письму",
|
||||
"zone_insert_inline": "Вставить в текст письма",
|
||||
"zone_new_message": "Прикрепить к новому письму",
|
||||
"zone_upload_files": "Загрузить в Файлы",
|
||||
"zone_upload_folder": "Загрузить в {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# súbor} few {# súbory} many {# súboru} other {# súborov}}",
|
||||
"files_summary_size": "{count, plural, one {# súbor} few {# súbory} many {# súboru} other {# súborov}}, {size}",
|
||||
"zone_attach_here": "Priložiť k tejto správe",
|
||||
"zone_insert_inline": "Vložiť do správy",
|
||||
"zone_new_message": "Priložiť k novej správe",
|
||||
"zone_upload_files": "Nahrať do Súborov",
|
||||
"zone_upload_folder": "Nahrať do {folder}",
|
||||
|
||||
@@ -7122,6 +7122,7 @@
|
||||
"files_summary": "{count, plural, other {# ไฟล์}}",
|
||||
"files_summary_size": "{count, plural, other {# ไฟล์}}, {size}",
|
||||
"zone_attach_here": "แนบกับข้อความนี้",
|
||||
"zone_insert_inline": "แทรกลงในข้อความ",
|
||||
"zone_new_message": "แนบกับข้อความใหม่",
|
||||
"zone_upload_files": "อัปโหลดไปยังไฟล์",
|
||||
"zone_upload_folder": "อัปโหลดไปยัง {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# dosya} other {# dosya}}",
|
||||
"files_summary_size": "{count, plural, one {# dosya} other {# dosya}}, {size}",
|
||||
"zone_attach_here": "Bu iletiye ekle",
|
||||
"zone_insert_inline": "İleti metnine ekle",
|
||||
"zone_new_message": "Yeni bir iletiye ekle",
|
||||
"zone_upload_files": "Dosyalar'a yükle",
|
||||
"zone_upload_folder": "{folder} klasörüne yükle",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, one {# файл} few {# файли} many {# файлів} other {# файлу}}",
|
||||
"files_summary_size": "{count, plural, one {# файл} few {# файли} many {# файлів} other {# файлу}}, {size}",
|
||||
"zone_attach_here": "Прикріпити до цього повідомлення",
|
||||
"zone_insert_inline": "Вставити в текст повідомлення",
|
||||
"zone_new_message": "Прикріпити до нового повідомлення",
|
||||
"zone_upload_files": "Завантажити у Файли",
|
||||
"zone_upload_folder": "Завантажити в {folder}",
|
||||
|
||||
@@ -7126,6 +7126,7 @@
|
||||
"files_summary": "{count, plural, other {# tệp}}",
|
||||
"files_summary_size": "{count, plural, other {# tệp}}, {size}",
|
||||
"zone_attach_here": "Đính kèm vào thư này",
|
||||
"zone_insert_inline": "Chèn vào nội dung thư",
|
||||
"zone_new_message": "Đính kèm vào thư mới",
|
||||
"zone_upload_files": "Tải lên Tệp",
|
||||
"zone_upload_folder": "Tải lên {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, other {# 個檔案}}",
|
||||
"files_summary_size": "{count, plural, other {# 個檔案}}, {size}",
|
||||
"zone_attach_here": "附加到此郵件",
|
||||
"zone_insert_inline": "插入到郵件內文",
|
||||
"zone_new_message": "附加到新郵件",
|
||||
"zone_upload_files": "上傳到檔案",
|
||||
"zone_upload_folder": "上傳到 {folder}",
|
||||
|
||||
@@ -7114,6 +7114,7 @@
|
||||
"files_summary": "{count, plural, other {# 个文件}}",
|
||||
"files_summary_size": "{count, plural, other {# 个文件}}, {size}",
|
||||
"zone_attach_here": "附加到此邮件",
|
||||
"zone_insert_inline": "插入到邮件正文",
|
||||
"zone_new_message": "附加到新邮件",
|
||||
"zone_upload_files": "上传到文件",
|
||||
"zone_upload_folder": "上传到 {folder}",
|
||||
|
||||
Reference in New Issue
Block a user