diff --git a/components/email/__tests__/rich-text-editor-a11y.test.tsx b/components/email/__tests__/rich-text-editor-a11y.test.tsx
index 12931f3a3..af348308e 100644
--- a/components/email/__tests__/rich-text-editor-a11y.test.tsx
+++ b/components/email/__tests__/rich-text-editor-a11y.test.tsx
@@ -164,8 +164,11 @@ describe('RichTextEditor image descriptions (MA-02)', () => {
const altDialog = () => screen.getByRole('dialog', { name: 'title' });
const html = (editor: Editor) => editor.getHTML();
- it('asks for a description when an image is dropped in, and never uses the file name', async () => {
+ it('offers a description for a dropped image, and never uses the file name', async () => {
const { editor } = await dropImage();
+ // Nothing asks on the drop; the chip on the image does, when clicked.
+ expect(screen.queryByRole('dialog', { name: 'title' })).toBeNull();
+ fireEvent.click(await screen.findByTestId('image-alt-add'));
const dialog = await waitFor(() => altDialog());
expect(html(editor)).not.toContain('IMG_2041');
expect(html(editor)).not.toMatch(/alt=/);
@@ -178,6 +181,9 @@ describe('RichTextEditor image descriptions (MA-02)', () => {
it('marks an image decorative with an empty alt', async () => {
const { editor } = await dropImage();
+ // Nothing asks on the drop; the chip on the image does, when clicked.
+ expect(screen.queryByRole('dialog', { name: 'title' })).toBeNull();
+ fireEvent.click(await screen.findByTestId('image-alt-add'));
const dialog = await waitFor(() => altDialog());
fireEvent.click(within(dialog).getByLabelText('decorative'));
expect(within(dialog).getByLabelText('label')).toBeDisabled();
diff --git a/components/email/__tests__/rich-text-editor-drop.test.tsx b/components/email/__tests__/rich-text-editor-drop.test.tsx
index 235cffce7..215b153b2 100644
--- a/components/email/__tests__/rich-text-editor-drop.test.tsx
+++ b/components/email/__tests__/rich-text-editor-drop.test.tsx
@@ -1,4 +1,4 @@
-import { act, render, waitFor } from '@testing-library/react';
+import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import React from 'react';
import type { Editor } from '@tiptap/core';
@@ -72,4 +72,33 @@ describe('RichTextEditor file drop', () => {
expect(editor.getText()).toBe('Hello');
expect(onDrop).not.toHaveBeenCalled();
});
+
+ // A pasted or dropped image asks nothing: an optional chip on it offers a description.
+ it('opens no dialog for a dropped image, and offers "Add description" on it', async () => {
+ const { editor } = await setup(async () => ({ src: 'data:image/png;base64,AAAA' }));
+ const file = new File(['png'], 'photo.png', { type: 'image/png' });
+
+ act(() => { editor.view.dom.dispatchEvent(fileDrop(file)); });
+
+ const chip = await screen.findByTestId('image-alt-add');
+ expect(chip).toHaveTextContent('add_description');
+ expect(screen.queryByRole('dialog')).toBeNull();
+
+ fireEvent.click(chip);
+ expect(await screen.findByRole('dialog')).toBeInTheDocument();
+ });
+
+ it('shows no chip on an image marked decorative', async () => {
+ let editor: Editor | null = null;
+ render(
+ Hi 
'}
+ onChange={() => {}}
+ onImageUpload={async () => null}
+ onEditorReady={(ed) => { editor = ed as unknown as Editor; }}
+ />
+ );
+ await waitFor(() => expect(editor).not.toBeNull());
+ expect(screen.queryByTestId('image-alt-add')).toBeNull();
+ });
});
diff --git a/components/email/resizable-image.tsx b/components/email/resizable-image.tsx
index bbed84d3c..fbe3ffc20 100644
--- a/components/email/resizable-image.tsx
+++ b/components/email/resizable-image.tsx
@@ -12,10 +12,14 @@ export interface ResizableImageOptions {
onEditAlt?: (pos: number) => void;
/** The name of the button that does it ("Image description"). */
editAltLabel?: string;
+ /** The chip on an image that has no description yet ("Add description"). */
+ addAltLabel?: string;
}
function ResizableImageView({ node, updateAttributes, selected, extension, getPos }: NodeViewProps) {
- const { onEditAlt, editAltLabel } = extension.options as ResizableImageOptions;
+ const { onEditAlt, editAltLabel, addAltLabel } = extension.options as ResizableImageOptions;
+ // No description and not marked decorative (""): an optional chip asks for one.
+ const undescribed = node.attrs.alt === null || node.attrs.alt === undefined;
const imgRef = useRef(null);
const [resizing, setResizing] = useState(false);
// A remote image (a draft from another client, pasted web content) stays
@@ -93,6 +97,23 @@ function ResizableImageView({ node, updateAttributes, selected, extension, getPo
style={{ width: "100%", height: "auto", display: "block" }}
draggable={false}
/>
+ {undescribed && onEditAlt && addAltLabel && !selected && (
+ // Optional, never in the way: pasting or dropping an image asks nothing.
+
+ )}
{selected && onEditAlt && (
// Edit what the image says to screen readers. Reachable with Tab
// while the image is selected, and from the toolbar.
@@ -140,7 +161,7 @@ export const ResizableImage = Node.create({
name: "image",
addOptions() {
- return { onEditAlt: undefined, editAltLabel: undefined };
+ return { onEditAlt: undefined, editAltLabel: undefined, addAltLabel: undefined };
},
group: "inline",
inline: true,
diff --git a/components/email/rich-text-editor.tsx b/components/email/rich-text-editor.tsx
index d4e4ac6cd..532291294 100644
--- a/components/email/rich-text-editor.tsx
+++ b/components/email/rich-text-editor.tsx
@@ -471,6 +471,7 @@ export function RichTextEditor({
}: RichTextEditorProps) {
const rtlEditingSupport = useSettingsStore((st) => st.rtlEditingSupport);
const tComposer = useTranslations("email_composer");
+ const tUx = useTranslations("compose_ux");
const tQuote = useTranslations("email_viewer");
const onImageUploadRef = React.useRef(onImageUpload);
onImageUploadRef.current = onImageUpload;
@@ -521,6 +522,7 @@ export function RichTextEditor({
ResizableImage.configure({
onEditAlt: (pos: number) => setAltQueue((queue) => [...queue, { pos }]),
editAltLabel: tComposer("image_alt.edit"),
+ addAltLabel: tUx("add_description"),
}),
Placeholder.configure({
placeholder,
@@ -615,11 +617,11 @@ export function RichTextEditor({
if (result) {
const { state } = view;
const pos = view.posAtCoords({ left: event.clientX, top: event.clientY });
- // No alt yet (never the file name): the user is asked next.
+ // No alt yet (never the file name).
const node = state.schema.nodes.image.create({ src: result.src, alt: null, cid: result.cid });
const tr = state.tr.insert(pos?.pos ?? state.selection.anchor, node);
view.dispatch(tr);
- setAltQueue((queue) => [...queue, { src: result.src }]);
+ // No dialog: the image offers "Add description" itself.
}
});
}
@@ -638,7 +640,7 @@ export function RichTextEditor({
const node = state.schema.nodes.image.create({ src: result.src, alt: null, cid: result.cid });
const tr = state.tr.replaceSelectionWith(node);
view.dispatch(tr);
- setAltQueue((queue) => [...queue, { src: result.src }]);
+ // No dialog: the image offers "Add description" itself.
}
});
}
diff --git a/locales/ar/common.json b/locales/ar/common.json
index cb0736760..26c0c6673 100644
--- a/locales/ar/common.json
+++ b/locales/ar/common.json
@@ -1217,7 +1217,8 @@
"undo": "تراجع",
"discard_draft": "تجاهل المسودة",
"save_failed_close": "تعذّر حفظ المسودة. حاول مرة أخرى أو تجاهلها.",
- "try_again": "حاول مرة أخرى"
+ "try_again": "حاول مرة أخرى",
+ "add_description": "إضافة وصف"
},
"confirm_dialog": {
"confirm": "تأكيد",
diff --git a/locales/ca/common.json b/locales/ca/common.json
index 28e6ac887..ccaf06e04 100644
--- a/locales/ca/common.json
+++ b/locales/ca/common.json
@@ -1217,7 +1217,8 @@
"undo": "Desfés",
"discard_draft": "Descarta l'esborrany",
"save_failed_close": "No s'ha pogut desar l'esborrany. Torneu-ho a provar o descarteu-lo.",
- "try_again": "Torna-ho a provar"
+ "try_again": "Torna-ho a provar",
+ "add_description": "Afegeix una descripció"
},
"confirm_dialog": {
"confirm": "Confirma",
diff --git a/locales/cs/common.json b/locales/cs/common.json
index f1d8ed92f..151416cd7 100644
--- a/locales/cs/common.json
+++ b/locales/cs/common.json
@@ -1217,7 +1217,8 @@
"undo": "Zpět",
"discard_draft": "Zahodit koncept",
"save_failed_close": "Koncept se nepodařilo uložit. Zkuste to znovu, nebo ho zahoďte.",
- "try_again": "Zkusit znovu"
+ "try_again": "Zkusit znovu",
+ "add_description": "Přidat popis"
},
"confirm_dialog": {
"confirm": "Potvrdit",
diff --git a/locales/da/common.json b/locales/da/common.json
index a820dfaf4..bd60f75bf 100644
--- a/locales/da/common.json
+++ b/locales/da/common.json
@@ -1217,7 +1217,8 @@
"undo": "Fortryd",
"discard_draft": "Kassér kladde",
"save_failed_close": "Kladden kunne ikke gemmes. Prøv igen, eller kassér den.",
- "try_again": "Prøv igen"
+ "try_again": "Prøv igen",
+ "add_description": "Tilføj beskrivelse"
},
"confirm_dialog": {
"confirm": "Bekræft",
diff --git a/locales/de/common.json b/locales/de/common.json
index c23337444..b8a6eee95 100644
--- a/locales/de/common.json
+++ b/locales/de/common.json
@@ -1217,7 +1217,8 @@
"undo": "Rückgängig",
"discard_draft": "Entwurf verwerfen",
"save_failed_close": "Der Entwurf konnte nicht gespeichert werden. Versuchen Sie es erneut oder verwerfen Sie ihn.",
- "try_again": "Erneut versuchen"
+ "try_again": "Erneut versuchen",
+ "add_description": "Beschreibung hinzufügen"
},
"confirm_dialog": {
"confirm": "Bestätigen",
diff --git a/locales/en/common.json b/locales/en/common.json
index ea741cca9..d5fffaad2 100644
--- a/locales/en/common.json
+++ b/locales/en/common.json
@@ -1217,7 +1217,8 @@
"undo": "Undo",
"discard_draft": "Discard draft",
"save_failed_close": "The draft could not be saved. Try again, or discard it.",
- "try_again": "Try again"
+ "try_again": "Try again",
+ "add_description": "Add description"
},
"confirm_dialog": {
"confirm": "Confirm",
diff --git a/locales/es/common.json b/locales/es/common.json
index 0405a0dbf..98cd6c2d3 100644
--- a/locales/es/common.json
+++ b/locales/es/common.json
@@ -1217,7 +1217,8 @@
"undo": "Deshacer",
"discard_draft": "Descartar borrador",
"save_failed_close": "No se pudo guardar el borrador. Inténtelo de nuevo o descártelo.",
- "try_again": "Reintentar"
+ "try_again": "Reintentar",
+ "add_description": "Añadir descripción"
},
"confirm_dialog": {
"confirm": "Confirmar",
diff --git a/locales/et/common.json b/locales/et/common.json
index 4e9f89304..3b9e3d6be 100644
--- a/locales/et/common.json
+++ b/locales/et/common.json
@@ -1217,7 +1217,8 @@
"undo": "Võta tagasi",
"discard_draft": "Loobu mustandist",
"save_failed_close": "Mustandit ei saanud salvestada. Proovige uuesti või loobuge sellest.",
- "try_again": "Proovi uuesti"
+ "try_again": "Proovi uuesti",
+ "add_description": "Lisa kirjeldus"
},
"confirm_dialog": {
"confirm": "Kinnita",
diff --git a/locales/fa/common.json b/locales/fa/common.json
index bfe6d3cbb..a73b4df95 100644
--- a/locales/fa/common.json
+++ b/locales/fa/common.json
@@ -1217,7 +1217,8 @@
"undo": "واگرد",
"discard_draft": "دور انداختن پیشنویس",
"save_failed_close": "پیشنویس ذخیره نشد. دوباره تلاش کنید یا آن را دور بیندازید.",
- "try_again": "تلاش دوباره"
+ "try_again": "تلاش دوباره",
+ "add_description": "افزودن توضیح"
},
"confirm_dialog": {
"confirm": "تأیید",
diff --git a/locales/fr/common.json b/locales/fr/common.json
index be7312951..5ddb3438e 100644
--- a/locales/fr/common.json
+++ b/locales/fr/common.json
@@ -1217,7 +1217,8 @@
"undo": "Annuler",
"discard_draft": "Supprimer le brouillon",
"save_failed_close": "Le brouillon n'a pas pu être enregistré. Réessayez ou supprimez-le.",
- "try_again": "Réessayer"
+ "try_again": "Réessayer",
+ "add_description": "Ajouter une description"
},
"confirm_dialog": {
"confirm": "Confirmer",
diff --git a/locales/he/common.json b/locales/he/common.json
index 60853894b..a57187038 100644
--- a/locales/he/common.json
+++ b/locales/he/common.json
@@ -1160,7 +1160,8 @@
"undo": "ביטול",
"discard_draft": "מחיקת הטיוטה",
"save_failed_close": "לא ניתן היה לשמור את הטיוטה. נסו שוב או מחקו אותה.",
- "try_again": "ניסיון נוסף"
+ "try_again": "ניסיון נוסף",
+ "add_description": "הוספת תיאור"
},
"confirm_dialog": {
"confirm": "אשר",
diff --git a/locales/hu/common.json b/locales/hu/common.json
index 79efccd53..cfccee90d 100644
--- a/locales/hu/common.json
+++ b/locales/hu/common.json
@@ -1217,7 +1217,8 @@
"undo": "Visszavonás",
"discard_draft": "Piszkozat elvetése",
"save_failed_close": "A piszkozatot nem sikerült menteni. Próbálja újra, vagy vesse el.",
- "try_again": "Újra"
+ "try_again": "Újra",
+ "add_description": "Leírás hozzáadása"
},
"confirm_dialog": {
"confirm": "Megerősítés",
diff --git a/locales/it/common.json b/locales/it/common.json
index dbe2f779d..926421531 100644
--- a/locales/it/common.json
+++ b/locales/it/common.json
@@ -1217,7 +1217,8 @@
"undo": "Annulla",
"discard_draft": "Elimina bozza",
"save_failed_close": "Impossibile salvare la bozza. Riprova o eliminala.",
- "try_again": "Riprova"
+ "try_again": "Riprova",
+ "add_description": "Aggiungi descrizione"
},
"confirm_dialog": {
"confirm": "Conferma",
diff --git a/locales/ja/common.json b/locales/ja/common.json
index 271e3d791..1a48da88b 100644
--- a/locales/ja/common.json
+++ b/locales/ja/common.json
@@ -1217,7 +1217,8 @@
"undo": "元に戻す",
"discard_draft": "下書きを破棄",
"save_failed_close": "下書きを保存できませんでした。もう一度試すか、破棄してください。",
- "try_again": "再試行"
+ "try_again": "再試行",
+ "add_description": "説明を追加"
},
"confirm_dialog": {
"confirm": "確認",
diff --git a/locales/ko/common.json b/locales/ko/common.json
index 5e9bc4957..7fbd42927 100644
--- a/locales/ko/common.json
+++ b/locales/ko/common.json
@@ -1217,7 +1217,8 @@
"undo": "실행 취소",
"discard_draft": "임시 보관 메일 삭제",
"save_failed_close": "임시 보관 메일을 저장하지 못했습니다. 다시 시도하거나 삭제하세요.",
- "try_again": "다시 시도"
+ "try_again": "다시 시도",
+ "add_description": "설명 추가"
},
"confirm_dialog": {
"confirm": "확인",
diff --git a/locales/lv/common.json b/locales/lv/common.json
index 57d08584b..a99b90bb1 100644
--- a/locales/lv/common.json
+++ b/locales/lv/common.json
@@ -1217,7 +1217,8 @@
"undo": "Atsaukt",
"discard_draft": "Atmest melnrakstu",
"save_failed_close": "Melnrakstu neizdevās saglabāt. Mēģiniet vēlreiz vai atmetiet to.",
- "try_again": "Mēģināt vēlreiz"
+ "try_again": "Mēģināt vēlreiz",
+ "add_description": "Pievienot aprakstu"
},
"confirm_dialog": {
"confirm": "Apstiprināt",
diff --git a/locales/mn/common.json b/locales/mn/common.json
index 4d10e767b..204cc8105 100644
--- a/locales/mn/common.json
+++ b/locales/mn/common.json
@@ -1217,7 +1217,8 @@
"undo": "Буцаах",
"discard_draft": "Ноорог устгах",
"save_failed_close": "Ноорогийг хадгалж чадсангүй. Дахин оролдох эсвэл устгана уу.",
- "try_again": "Дахин оролдох"
+ "try_again": "Дахин оролдох",
+ "add_description": "Тайлбар нэмэх"
},
"confirm_dialog": {
"confirm": "Баталгаажуулах",
diff --git a/locales/nb/common.json b/locales/nb/common.json
index d77f2071e..755bcbc45 100644
--- a/locales/nb/common.json
+++ b/locales/nb/common.json
@@ -1217,7 +1217,8 @@
"undo": "Angre",
"discard_draft": "Forkast utkast",
"save_failed_close": "Utkastet kunne ikke lagres. Prøv igjen, eller forkast det.",
- "try_again": "Prøv igjen"
+ "try_again": "Prøv igjen",
+ "add_description": "Legg til beskrivelse"
},
"confirm_dialog": {
"confirm": "Bekreft",
diff --git a/locales/nl/common.json b/locales/nl/common.json
index a52de8a28..c9ed9c8dd 100644
--- a/locales/nl/common.json
+++ b/locales/nl/common.json
@@ -1217,7 +1217,8 @@
"undo": "Ongedaan maken",
"discard_draft": "Concept verwijderen",
"save_failed_close": "Het concept kon niet worden opgeslagen. Probeer het opnieuw of verwijder het.",
- "try_again": "Opnieuw proberen"
+ "try_again": "Opnieuw proberen",
+ "add_description": "Beschrijving toevoegen"
},
"confirm_dialog": {
"confirm": "Bevestigen",
diff --git a/locales/pl/common.json b/locales/pl/common.json
index 886c5c7d0..74467a752 100644
--- a/locales/pl/common.json
+++ b/locales/pl/common.json
@@ -1217,7 +1217,8 @@
"undo": "Cofnij",
"discard_draft": "Odrzuć wersję roboczą",
"save_failed_close": "Nie udało się zapisać wersji roboczej. Spróbuj ponownie lub ją odrzuć.",
- "try_again": "Spróbuj ponownie"
+ "try_again": "Spróbuj ponownie",
+ "add_description": "Dodaj opis"
},
"confirm_dialog": {
"confirm": "Potwierdź",
diff --git a/locales/pt/common.json b/locales/pt/common.json
index daaebc388..9e2b2d331 100644
--- a/locales/pt/common.json
+++ b/locales/pt/common.json
@@ -1217,7 +1217,8 @@
"undo": "Desfazer",
"discard_draft": "Descartar rascunho",
"save_failed_close": "Não foi possível guardar o rascunho. Tente novamente ou descarte-o.",
- "try_again": "Tentar novamente"
+ "try_again": "Tentar novamente",
+ "add_description": "Adicionar descrição"
},
"confirm_dialog": {
"confirm": "Confirmar",
diff --git a/locales/ro/common.json b/locales/ro/common.json
index ba56d909e..db1ed7bce 100644
--- a/locales/ro/common.json
+++ b/locales/ro/common.json
@@ -1217,7 +1217,8 @@
"undo": "Anulează",
"discard_draft": "Renunță la ciornă",
"save_failed_close": "Ciorna nu a putut fi salvată. Încercați din nou sau renunțați la ea.",
- "try_again": "Încearcă din nou"
+ "try_again": "Încearcă din nou",
+ "add_description": "Adaugă o descriere"
},
"confirm_dialog": {
"confirm": "Confirmare",
diff --git a/locales/ru/common.json b/locales/ru/common.json
index 6a1a7ff6a..810933d38 100644
--- a/locales/ru/common.json
+++ b/locales/ru/common.json
@@ -1217,7 +1217,8 @@
"undo": "Отменить",
"discard_draft": "Удалить черновик",
"save_failed_close": "Не удалось сохранить черновик. Попробуйте ещё раз или удалите его.",
- "try_again": "Повторить"
+ "try_again": "Повторить",
+ "add_description": "Добавить описание"
},
"confirm_dialog": {
"confirm": "Подтвердить",
diff --git a/locales/sk/common.json b/locales/sk/common.json
index 0d700da9a..72badccba 100644
--- a/locales/sk/common.json
+++ b/locales/sk/common.json
@@ -1217,7 +1217,8 @@
"undo": "Späť",
"discard_draft": "Zahodiť koncept",
"save_failed_close": "Koncept sa nepodarilo uložiť. Skúste to znova alebo ho zahoďte.",
- "try_again": "Skúsiť znova"
+ "try_again": "Skúsiť znova",
+ "add_description": "Pridať popis"
},
"confirm_dialog": {
"confirm": "Potvrdiť",
diff --git a/locales/th/common.json b/locales/th/common.json
index 467f5ebf4..75960886f 100644
--- a/locales/th/common.json
+++ b/locales/th/common.json
@@ -1565,7 +1565,8 @@
"undo": "เลิกทำ",
"discard_draft": "ทิ้งฉบับร่าง",
"save_failed_close": "บันทึกฉบับร่างไม่ได้ ลองอีกครั้งหรือทิ้งไป",
- "try_again": "ลองอีกครั้ง"
+ "try_again": "ลองอีกครั้ง",
+ "add_description": "เพิ่มคำอธิบาย"
},
"email_viewer": {
"read_receipt": {
diff --git a/locales/tr/common.json b/locales/tr/common.json
index 6fed03150..c98b22e1a 100644
--- a/locales/tr/common.json
+++ b/locales/tr/common.json
@@ -1217,7 +1217,8 @@
"undo": "Geri al",
"discard_draft": "Taslağı sil",
"save_failed_close": "Taslak kaydedilemedi. Yeniden deneyin ya da silin.",
- "try_again": "Yeniden dene"
+ "try_again": "Yeniden dene",
+ "add_description": "Açıklama ekle"
},
"confirm_dialog": {
"confirm": "Onayla",
diff --git a/locales/uk/common.json b/locales/uk/common.json
index 5a3b31482..61190aba0 100644
--- a/locales/uk/common.json
+++ b/locales/uk/common.json
@@ -1217,7 +1217,8 @@
"undo": "Скасувати",
"discard_draft": "Видалити чернетку",
"save_failed_close": "Не вдалося зберегти чернетку. Спробуйте ще раз або видаліть її.",
- "try_again": "Спробувати ще раз"
+ "try_again": "Спробувати ще раз",
+ "add_description": "Додати опис"
},
"confirm_dialog": {
"confirm": "Підтвердити",
diff --git a/locales/vi/common.json b/locales/vi/common.json
index df09e67d0..7f1cdde96 100644
--- a/locales/vi/common.json
+++ b/locales/vi/common.json
@@ -1229,7 +1229,8 @@
"undo": "Hoàn tác",
"discard_draft": "Hủy bản nháp",
"save_failed_close": "Không lưu được bản nháp. Hãy thử lại hoặc hủy bản nháp.",
- "try_again": "Thử lại"
+ "try_again": "Thử lại",
+ "add_description": "Thêm mô tả"
},
"confirm_dialog": {
"confirm": "Xác nhận",
diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json
index ede70a153..02b7f47bb 100644
--- a/locales/zh-TW/common.json
+++ b/locales/zh-TW/common.json
@@ -1217,7 +1217,8 @@
"undo": "復原",
"discard_draft": "捨棄草稿",
"save_failed_close": "無法儲存草稿。請再試一次或捨棄草稿。",
- "try_again": "再試一次"
+ "try_again": "再試一次",
+ "add_description": "新增描述"
},
"confirm_dialog": {
"confirm": "確認",
diff --git a/locales/zh/common.json b/locales/zh/common.json
index ccd831040..0b033d382 100644
--- a/locales/zh/common.json
+++ b/locales/zh/common.json
@@ -1217,7 +1217,8 @@
"undo": "撤销",
"discard_draft": "舍弃草稿",
"save_failed_close": "无法保存草稿。请重试或舍弃草稿。",
- "try_again": "重试"
+ "try_again": "重试",
+ "add_description": "添加描述"
},
"confirm_dialog": {
"confirm": "确认",