mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: make the formatting toolbar one tab stop that says which styles are on
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import React from 'react';
|
||||
import type { Editor } from '@tiptap/core';
|
||||
@@ -49,3 +49,80 @@ describe('RichTextEditor editing area (SR-07)', () => {
|
||||
await waitFor(() => expect(editor.view.dom).not.toHaveAttribute('aria-invalid'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('RichTextEditor formatting toolbar (SR-08)', () => {
|
||||
const toolbar = () => screen.getByRole('toolbar', { name: 'formatting' });
|
||||
const item = (name: string) => toolbar().querySelector<HTMLButtonElement>(`[data-toolbar-item="${name}"]`)!;
|
||||
const tabStops = () => Array.from(toolbar().querySelectorAll<HTMLElement>('[data-toolbar-item]')).filter((el) => el.tabIndex === 0);
|
||||
|
||||
it('is one Tab stop and moves with the arrows, Home and End', async () => {
|
||||
await setup();
|
||||
expect(tabStops()).toEqual([item('font-size')]);
|
||||
|
||||
item('font-size').focus();
|
||||
fireEvent.keyDown(item('font-size'), { key: 'ArrowRight' });
|
||||
expect(document.activeElement).toBe(item('bold'));
|
||||
expect(tabStops()).toEqual([item('bold')]);
|
||||
fireEvent.keyDown(item('bold'), { key: 'ArrowLeft' });
|
||||
expect(document.activeElement).toBe(item('font-size'));
|
||||
// End skips undo and redo while they are disabled.
|
||||
fireEvent.keyDown(item('font-size'), { key: 'End' });
|
||||
expect(document.activeElement).toBe(item('clear-formatting'));
|
||||
fireEvent.keyDown(item('clear-formatting'), { key: 'Home' });
|
||||
expect(document.activeElement).toBe(item('font-size'));
|
||||
});
|
||||
|
||||
it('says which toggles are on', async () => {
|
||||
// As in the composer, a change re-renders the editor through its parent.
|
||||
let editor: Editor | null = null;
|
||||
function Host() {
|
||||
const [html, setHtml] = React.useState('<p>Hello</p>');
|
||||
return <RichTextEditor content={html} onChange={setHtml} onEditorReady={(ed) => { editor = ed as unknown as Editor; }} />;
|
||||
}
|
||||
render(<Host />);
|
||||
await waitFor(() => expect(editor).not.toBeNull());
|
||||
expect(item('bold')).toHaveAttribute('aria-pressed', 'false');
|
||||
act(() => { editor!.chain().selectAll().toggleBold().run(); });
|
||||
await waitFor(() => expect(item('bold')).toHaveAttribute('aria-pressed', 'true'));
|
||||
expect(item('italic')).toHaveAttribute('aria-pressed', 'false');
|
||||
});
|
||||
|
||||
it('opens the font size menu by keyboard, moves inside it and gives the focus back on Escape', async () => {
|
||||
await setup();
|
||||
const button = item('font-size');
|
||||
expect(button).toHaveAttribute('aria-haspopup', 'menu');
|
||||
expect(button).toHaveAttribute('aria-expanded', 'false');
|
||||
|
||||
fireEvent.click(button);
|
||||
expect(button).toHaveAttribute('aria-expanded', 'true');
|
||||
const menu = screen.getByRole('menu', { name: 'font_size' });
|
||||
const entries = within(menu).getAllByRole('menuitemradio');
|
||||
await waitFor(() => expect(document.activeElement).toBe(entries[0]));
|
||||
fireEvent.keyDown(entries[0], { key: 'ArrowDown' });
|
||||
expect(document.activeElement).toBe(entries[1]);
|
||||
|
||||
fireEvent.keyDown(entries[1], { key: 'Escape' });
|
||||
expect(screen.queryByRole('menu', { name: 'font_size' })).toBeNull();
|
||||
expect(document.activeElement).toBe(button);
|
||||
});
|
||||
|
||||
it('names the colour swatches and moves through them as a grid', async () => {
|
||||
await setup();
|
||||
fireEvent.click(item('text-color'));
|
||||
const menu = screen.getByRole('menu', { name: 'text_color' });
|
||||
const swatches = within(menu).getAllByRole('menuitemradio');
|
||||
expect(swatches[0]).toHaveAccessibleName('colors.black');
|
||||
await waitFor(() => expect(document.activeElement).toBe(swatches[0]));
|
||||
fireEvent.keyDown(swatches[0], { key: 'ArrowDown' });
|
||||
expect(document.activeElement).toBe(swatches[8]);
|
||||
fireEvent.keyDown(swatches[8], { key: 'ArrowRight' });
|
||||
expect(document.activeElement).toBe(swatches[9]);
|
||||
});
|
||||
|
||||
it('jumps from the text to the toolbar with Alt+F10', async () => {
|
||||
const { editor } = await setup();
|
||||
editor.commands.focus();
|
||||
fireEvent.keyDown(editor.view.dom, { key: 'F10', altKey: true });
|
||||
expect(document.activeElement).toBe(item('font-size'));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useCallback, useState, useRef } from "react";
|
||||
import React, { useEffect, useCallback, useLayoutEffect, useState, useRef } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { useEditor, EditorContent, type Editor } from "@tiptap/react";
|
||||
import type { SuggestionProps } from "@tiptap/suggestion";
|
||||
@@ -146,35 +146,142 @@ export function editorA11yAttributes({ ariaLabel, ariaInvalid, ariaDescribedBy }
|
||||
};
|
||||
}
|
||||
|
||||
function ToolbarButton({
|
||||
active,
|
||||
onClick,
|
||||
children,
|
||||
title,
|
||||
disabled,
|
||||
}: {
|
||||
/**
|
||||
* One button of the formatting toolbar. `item` names it for the toolbar's
|
||||
* roving focus (one Tab stop, arrows inside). A toggle passes `pressed`, a
|
||||
* button that opens a popover passes `popup` and `expanded`.
|
||||
*/
|
||||
const ToolbarButton = React.forwardRef<HTMLButtonElement, {
|
||||
item: string;
|
||||
active?: boolean;
|
||||
pressed?: boolean;
|
||||
popup?: "menu" | "dialog";
|
||||
expanded?: boolean;
|
||||
onClick: () => void;
|
||||
children: React.ReactNode;
|
||||
title: string;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
}>(function ToolbarButton({ item, active, pressed, popup, expanded, onClick, children, title, disabled }, ref) {
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
type="button"
|
||||
data-toolbar-item={item}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
title={title}
|
||||
aria-label={title}
|
||||
aria-pressed={pressed === undefined ? undefined : pressed}
|
||||
aria-haspopup={popup}
|
||||
aria-expanded={popup ? !!expanded : undefined}
|
||||
className={cn(
|
||||
// Bigger on a phone, where 28px was hard to hit with a finger.
|
||||
"p-1.5 max-md:p-2 rounded hover:bg-accent transition-colors",
|
||||
active && "bg-accent text-accent-foreground",
|
||||
(active || pressed) && "bg-accent text-accent-foreground",
|
||||
disabled && "opacity-40 cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* A toolbar popover (font size, colours, table) that works from the
|
||||
* keyboard: it takes the focus when it opens (the checked entry, else the
|
||||
* first), the arrows move inside it (in a grid with `columns`), and Escape
|
||||
* or Tab close it and put the focus back on its button.
|
||||
*/
|
||||
function ToolbarPopover({
|
||||
label,
|
||||
role,
|
||||
columns,
|
||||
onClose,
|
||||
triggerRef,
|
||||
className,
|
||||
children,
|
||||
}: {
|
||||
label: string;
|
||||
role: "menu" | "dialog";
|
||||
/** Arrange the leading `[data-grid-item]` entries in rows of this many. */
|
||||
columns?: number;
|
||||
onClose: () => void;
|
||||
triggerRef: React.RefObject<HTMLButtonElement | null>;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const items = () =>
|
||||
Array.from(ref.current?.querySelectorAll<HTMLElement>("button:not([disabled])") ?? []);
|
||||
|
||||
useEffect(() => {
|
||||
const list = items();
|
||||
const checked = list.find((el) => el.getAttribute("aria-checked") === "true");
|
||||
(checked ?? list[0])?.focus();
|
||||
}, []);
|
||||
|
||||
const close = (returnFocus: boolean) => {
|
||||
onClose();
|
||||
if (returnFocus) triggerRef.current?.focus();
|
||||
};
|
||||
|
||||
const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
const list = items();
|
||||
const i = list.indexOf(document.activeElement as HTMLElement);
|
||||
const rtl = ref.current ? getComputedStyle(ref.current).direction === "rtl" : false;
|
||||
const gridCount = columns ? list.filter((el) => el.hasAttribute("data-grid-item")).length : 0;
|
||||
const inGrid = columns && i >= 0 && i < gridCount;
|
||||
let next = -1;
|
||||
switch (e.key) {
|
||||
case "Escape":
|
||||
case "Tab":
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
close(true);
|
||||
return;
|
||||
case "ArrowDown":
|
||||
next = inGrid ? (i + columns! < gridCount ? i + columns! : gridCount < list.length ? gridCount : i) : Math.min(i + 1, list.length - 1);
|
||||
break;
|
||||
case "ArrowUp":
|
||||
next = inGrid ? (i - columns! >= 0 ? i - columns! : i) : i === gridCount && gridCount > 0 ? gridCount - 1 : Math.max(i - 1, 0);
|
||||
break;
|
||||
case "ArrowRight":
|
||||
case "ArrowLeft": {
|
||||
if (!columns) return;
|
||||
const forward = (e.key === "ArrowRight") !== rtl;
|
||||
next = forward ? Math.min(i + 1, list.length - 1) : Math.max(i - 1, 0);
|
||||
break;
|
||||
}
|
||||
case "Home":
|
||||
next = 0;
|
||||
break;
|
||||
case "End":
|
||||
next = list.length - 1;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
list[next]?.focus();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
role={role}
|
||||
aria-label={label}
|
||||
onKeyDown={onKeyDown}
|
||||
// Focus moving out (a click elsewhere, a screen reader jump) closes it.
|
||||
onBlur={(e) => {
|
||||
const to = e.relatedTarget as Node | null;
|
||||
if (to && !ref.current?.contains(to) && !triggerRef.current?.contains(to)) close(false);
|
||||
}}
|
||||
className={cn("absolute z-50 top-full start-0 mt-1 bg-popover border border-border rounded-md shadow-md", className)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ToolbarSeparator() {
|
||||
@@ -191,6 +298,14 @@ const TEXT_COLORS = [
|
||||
"#000000", "#5f6368", "#9aa0a6", "#c5221f", "#e8710a", "#f9ab00", "#188038", "#1967d2",
|
||||
"#7627bb", "#c2185b", "#795548", "#fa5252", "#fd7e14", "#40c057", "#4dabf7", "#e64980",
|
||||
];
|
||||
// What each swatch is called, for screen readers and the tooltip
|
||||
// (email_composer.toolbar.colors.*).
|
||||
const TEXT_COLOR_NAMES: Record<string, string> = {
|
||||
"#000000": "black", "#5f6368": "dark_gray", "#9aa0a6": "gray", "#c5221f": "dark_red",
|
||||
"#e8710a": "orange", "#f9ab00": "yellow", "#188038": "green", "#1967d2": "blue",
|
||||
"#7627bb": "purple", "#c2185b": "dark_pink", "#795548": "brown", "#fa5252": "red",
|
||||
"#fd7e14": "light_orange", "#40c057": "light_green", "#4dabf7": "light_blue", "#e64980": "pink",
|
||||
};
|
||||
|
||||
function TableSizePicker({ onPick }: { onPick: (rows: number, cols: number) => void }) {
|
||||
const t = useTranslations("email_composer.toolbar");
|
||||
@@ -210,7 +325,10 @@ function TableSizePicker({ onPick }: { onPick: (rows: number, cols: number) => v
|
||||
<button
|
||||
key={i}
|
||||
type="button"
|
||||
data-grid-item=""
|
||||
aria-label={t("table_size", { rows: r + 1, cols: c + 1 })}
|
||||
onMouseEnter={() => setHover({ r, c })}
|
||||
onFocus={() => setHover({ r, c })}
|
||||
onClick={() => onPick(r + 1, c + 1)}
|
||||
className={cn(
|
||||
"w-4 h-4 border border-border/60 rounded-[2px] transition-colors",
|
||||
@@ -220,7 +338,7 @@ function TableSizePicker({ onPick }: { onPick: (rows: number, cols: number) => v
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground mt-1.5 text-center">
|
||||
<div className="text-xs text-muted-foreground mt-1.5 text-center" aria-hidden>
|
||||
{hover ? `${hover.r + 1} × ${hover.c + 1}` : t("pick_size")}
|
||||
</div>
|
||||
</div>
|
||||
@@ -259,6 +377,8 @@ export function RichTextEditor({
|
||||
mentionContactsRef.current = { findMentionContacts, mentionAddsTo, onMentionAdd };
|
||||
const [mention, setMention] = useState<SuggestionProps<MentionCandidate, MentionCandidate> | null>(null);
|
||||
const mentionListRef = useRef<RecipientMentionListHandle>(null);
|
||||
// Alt+F10 in the text moves to the toolbar, as in other editors.
|
||||
const focusToolbarRef = useRef<(() => boolean) | null>(null);
|
||||
|
||||
const editor = useEditor({
|
||||
extensions: [
|
||||
@@ -351,6 +471,13 @@ export function RichTextEditor({
|
||||
content,
|
||||
editorProps: {
|
||||
attributes: editorA11yAttributes({ ariaLabel, ariaInvalid, ariaDescribedBy }),
|
||||
handleKeyDown: (_view, event) => {
|
||||
if (event.altKey && event.key === "F10" && focusToolbarRef.current?.()) {
|
||||
event.preventDefault();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
handleDrop: (view, event) => {
|
||||
if (!event.dataTransfer?.files?.length) return false;
|
||||
const upload = onImageUploadRef.current;
|
||||
@@ -482,6 +609,37 @@ export function RichTextEditor({
|
||||
const bgColorWrapperRef = useRef<HTMLDivElement>(null);
|
||||
const [fontSizeMenuOpen, setFontSizeMenuOpen] = useState(false);
|
||||
const fontSizeWrapperRef = useRef<HTMLDivElement>(null);
|
||||
const fontSizeButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const colorButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const bgColorButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const tableButtonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
// The toolbar is one Tab stop (roving tabindex): the button that had the
|
||||
// focus last keeps it, the arrows, Home and End move inside.
|
||||
const toolbarRef = useRef<HTMLDivElement>(null);
|
||||
const rovingItemRef = useRef<string | null>(null);
|
||||
const toolbarItems = () =>
|
||||
Array.from(toolbarRef.current?.querySelectorAll<HTMLButtonElement>("[data-toolbar-item]") ?? [])
|
||||
.filter((el) => !el.disabled);
|
||||
const syncToolbarTabStops = (focus?: HTMLButtonElement) => {
|
||||
const all = Array.from(toolbarRef.current?.querySelectorAll<HTMLButtonElement>("[data-toolbar-item]") ?? []);
|
||||
const enabled = all.filter((el) => !el.disabled);
|
||||
const current = focus ?? enabled.find((el) => el.dataset.toolbarItem === rovingItemRef.current) ?? enabled[0];
|
||||
if (current) rovingItemRef.current = current.dataset.toolbarItem ?? null;
|
||||
for (const el of all) el.tabIndex = el === current ? 0 : -1;
|
||||
};
|
||||
// After every render: buttons come and go (RTL, undo turning disabled).
|
||||
useLayoutEffect(() => {
|
||||
syncToolbarTabStops();
|
||||
});
|
||||
focusToolbarRef.current = () => {
|
||||
const toolbar = toolbarRef.current;
|
||||
if (!toolbar || getComputedStyle(toolbar).display === "none") return false;
|
||||
syncToolbarTabStops();
|
||||
const stop = toolbar.querySelector<HTMLButtonElement>('[data-toolbar-item][tabindex="0"]');
|
||||
stop?.focus();
|
||||
return !!stop && document.activeElement === stop;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!fontSizeMenuOpen) return;
|
||||
@@ -534,8 +692,82 @@ export function RichTextEditor({
|
||||
}
|
||||
|
||||
const currentFontSize: string | null = editor.getAttributes("textStyle").fontSize ?? null;
|
||||
const currentColor: string | null = editor.getAttributes("textStyle").color ?? null;
|
||||
const currentBgColor: string | null = editor.getAttributes("textStyle").backgroundColor ?? null;
|
||||
|
||||
const onToolbarKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
const target = e.target as HTMLElement;
|
||||
// Keys inside a popover are the popover's.
|
||||
if (!target.hasAttribute("data-toolbar-item")) return;
|
||||
const items = toolbarItems();
|
||||
const i = items.indexOf(target as HTMLButtonElement);
|
||||
if (i < 0) return;
|
||||
const rtl = toolbarRef.current ? getComputedStyle(toolbarRef.current).direction === "rtl" : false;
|
||||
let next: number;
|
||||
if (e.key === (rtl ? "ArrowLeft" : "ArrowRight")) next = (i + 1) % items.length;
|
||||
else if (e.key === (rtl ? "ArrowRight" : "ArrowLeft")) next = (i - 1 + items.length) % items.length;
|
||||
else if (e.key === "Home") next = 0;
|
||||
else if (e.key === "End") next = items.length - 1;
|
||||
else return;
|
||||
e.preventDefault();
|
||||
syncToolbarTabStops(items[next]);
|
||||
items[next].focus();
|
||||
};
|
||||
const onToolbarFocus = (e: React.FocusEvent<HTMLDivElement>) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (target instanceof HTMLButtonElement && target.hasAttribute("data-toolbar-item")) syncToolbarTabStops(target);
|
||||
};
|
||||
|
||||
const renderPalette = (current: string | null, apply: (color: string) => void, removeLabel: string, remove: () => void) => (
|
||||
<>
|
||||
<div className="grid gap-0.5" style={{ gridTemplateColumns: "repeat(8, 1fr)" }}>
|
||||
{TEXT_COLORS.map((color) => {
|
||||
const name = tToolbar(`colors.${TEXT_COLOR_NAMES[color]}`);
|
||||
return (
|
||||
<button
|
||||
key={color}
|
||||
type="button"
|
||||
role="menuitemradio"
|
||||
aria-checked={current === color}
|
||||
aria-label={name}
|
||||
title={name}
|
||||
data-grid-item=""
|
||||
onClick={() => apply(color)}
|
||||
className={cn(
|
||||
"w-4 h-4 border border-border/60 rounded-[2px] transition-transform hover:scale-110 focus-visible:scale-110",
|
||||
current === color && "ring-1 ring-ring ring-offset-1"
|
||||
)}
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="h-px bg-border my-1.5" role="separator" />
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
className="flex items-center gap-2 px-2 py-1 text-sm rounded hover:bg-accent focus-visible:bg-accent text-start w-full"
|
||||
onClick={remove}
|
||||
>
|
||||
<RemoveFormatting className="w-4 h-4" aria-hidden /> {removeLabel}
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
|
||||
const tableMenuItem = (icon: React.ReactNode, label: string, run: () => void, destructive = false) => (
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-accent focus-visible:bg-accent text-start",
|
||||
destructive && "text-destructive"
|
||||
)}
|
||||
onClick={() => { run(); setTableMenuOpen(false); }}
|
||||
>
|
||||
{icon} {label}
|
||||
</button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col", hasError && "ring-2 ring-red-500 dark:ring-red-400 rounded", className)}>
|
||||
{/* Toolbar - sticky within the composer's scroll container so it stays
|
||||
@@ -545,7 +777,12 @@ export function RichTextEditor({
|
||||
through, which a backdrop blur alone did not guarantee (browsers
|
||||
that skip backdrop-filter drew the text sharp under the buttons). */}
|
||||
<div
|
||||
ref={toolbarRef}
|
||||
data-testid="rich-text-toolbar"
|
||||
role="toolbar"
|
||||
aria-label={tComposer("formatting")}
|
||||
onKeyDown={onToolbarKeyDown}
|
||||
onFocus={onToolbarFocus}
|
||||
className={cn(
|
||||
"sticky z-20 flex flex-wrap items-center gap-0.5 px-3 py-1.5 bg-[color-mix(in_oklab,var(--color-muted)_30%,var(--color-background))]",
|
||||
toolbarPlacement === 'bottom'
|
||||
@@ -559,9 +796,13 @@ export function RichTextEditor({
|
||||
once a size is chosen the button shows that number instead. */}
|
||||
<div ref={fontSizeWrapperRef} className="relative">
|
||||
<ToolbarButton
|
||||
ref={fontSizeButtonRef}
|
||||
item="font-size"
|
||||
active={fontSizeMenuOpen || !!currentFontSize}
|
||||
popup="menu"
|
||||
expanded={fontSizeMenuOpen}
|
||||
onClick={() => setFontSizeMenuOpen((v) => !v)}
|
||||
title={tToolbar("font_size")}
|
||||
title={currentFontSize ? `${tToolbar("font_size")}: ${currentFontSize}` : tToolbar("font_size")}
|
||||
>
|
||||
{currentFontSize ? (
|
||||
<span className="text-xs font-semibold min-w-6 text-center leading-4 tabular-nums">
|
||||
@@ -575,17 +816,25 @@ export function RichTextEditor({
|
||||
)}
|
||||
</ToolbarButton>
|
||||
{fontSizeMenuOpen && (
|
||||
<div className="absolute z-50 top-full start-0 mt-1 bg-popover border border-border rounded-md shadow-md p-1 min-w-[64px]">
|
||||
<ToolbarPopover
|
||||
label={tToolbar("font_size")}
|
||||
role="menu"
|
||||
triggerRef={fontSizeButtonRef}
|
||||
onClose={() => setFontSizeMenuOpen(false)}
|
||||
className="p-1 min-w-[64px]"
|
||||
>
|
||||
{FONT_SIZES.map((size) => (
|
||||
<button
|
||||
key={size}
|
||||
type="button"
|
||||
role="menuitemradio"
|
||||
aria-checked={currentFontSize === size}
|
||||
onClick={() => {
|
||||
editor.chain().focus().setFontSize(size).run();
|
||||
setFontSizeMenuOpen(false);
|
||||
}}
|
||||
className={cn(
|
||||
"block w-full text-start px-2 py-1 rounded hover:bg-accent transition-colors",
|
||||
"block w-full text-start px-2 py-1 rounded hover:bg-accent focus-visible:bg-accent transition-colors",
|
||||
currentFontSize === size && "bg-accent text-accent-foreground"
|
||||
)}
|
||||
style={{ fontSize: size, lineHeight: 1.4 }}
|
||||
@@ -593,46 +842,51 @@ export function RichTextEditor({
|
||||
{size}
|
||||
</button>
|
||||
))}
|
||||
<div className="h-px bg-border my-1" />
|
||||
<div className="h-px bg-border my-1" role="separator" />
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-2 py-1 text-sm rounded hover:bg-accent text-start w-full"
|
||||
role="menuitem"
|
||||
className="flex items-center gap-2 px-2 py-1 text-sm rounded hover:bg-accent focus-visible:bg-accent text-start w-full"
|
||||
onClick={() => {
|
||||
editor.chain().focus().unsetFontSize().run();
|
||||
setFontSizeMenuOpen(false);
|
||||
}}
|
||||
>
|
||||
<RemoveFormatting className="w-4 h-4" /> {tToolbar("font_size_default")}
|
||||
<RemoveFormatting className="w-4 h-4" aria-hidden /> {tToolbar("font_size_default")}
|
||||
</button>
|
||||
</div>
|
||||
</ToolbarPopover>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ToolbarSeparator />
|
||||
|
||||
<ToolbarButton
|
||||
active={editor.isActive("bold")}
|
||||
item="bold"
|
||||
pressed={editor.isActive("bold")}
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
title={tToolbar("bold")}
|
||||
>
|
||||
<Bold className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
active={editor.isActive("italic")}
|
||||
item="italic"
|
||||
pressed={editor.isActive("italic")}
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
title={tToolbar("italic")}
|
||||
>
|
||||
<Italic className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
active={editor.isActive("underline")}
|
||||
item="underline"
|
||||
pressed={editor.isActive("underline")}
|
||||
onClick={() => editor.chain().focus().toggleUnderline().run()}
|
||||
title={tToolbar("underline")}
|
||||
>
|
||||
<UnderlineIcon className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
active={editor.isActive("strike")}
|
||||
item="strike"
|
||||
pressed={editor.isActive("strike")}
|
||||
onClick={() => editor.chain().focus().toggleStrike().run()}
|
||||
title={tToolbar("strikethrough")}
|
||||
>
|
||||
@@ -640,45 +894,39 @@ export function RichTextEditor({
|
||||
</ToolbarButton>
|
||||
<div ref={colorWrapperRef} className="relative">
|
||||
<ToolbarButton
|
||||
active={!!editor.getAttributes("textStyle").color}
|
||||
ref={colorButtonRef}
|
||||
item="text-color"
|
||||
active={!!currentColor}
|
||||
popup="menu"
|
||||
expanded={colorMenuOpen}
|
||||
onClick={() => setColorMenuOpen((v) => !v)}
|
||||
title={tToolbar("text_color")}
|
||||
>
|
||||
{/* The icon itself previews the active colour - no layout shift. */}
|
||||
<Baseline className="w-4 h-4" style={{ color: editor.getAttributes("textStyle").color || undefined }} />
|
||||
<Baseline className="w-4 h-4" style={{ color: currentColor || undefined }} />
|
||||
</ToolbarButton>
|
||||
{colorMenuOpen && (
|
||||
<div className="absolute z-50 top-full start-0 mt-1 bg-popover border border-border rounded-md shadow-md p-2">
|
||||
<div className="grid gap-0.5" style={{ gridTemplateColumns: "repeat(8, 1fr)" }}>
|
||||
{TEXT_COLORS.map((color) => (
|
||||
<button
|
||||
key={color}
|
||||
type="button"
|
||||
title={color}
|
||||
onClick={() => {
|
||||
editor.chain().focus().setColor(color).run();
|
||||
setColorMenuOpen(false);
|
||||
}}
|
||||
className={cn(
|
||||
"w-4 h-4 border border-border/60 rounded-[2px] transition-transform hover:scale-110",
|
||||
editor.getAttributes("textStyle").color === color && "ring-1 ring-ring ring-offset-1"
|
||||
)}
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="h-px bg-border my-1.5" />
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-2 py-1 text-sm rounded hover:bg-accent text-start w-full"
|
||||
onClick={() => {
|
||||
<ToolbarPopover
|
||||
label={tToolbar("text_color")}
|
||||
role="menu"
|
||||
columns={8}
|
||||
triggerRef={colorButtonRef}
|
||||
onClose={() => setColorMenuOpen(false)}
|
||||
className="p-2"
|
||||
>
|
||||
{renderPalette(
|
||||
currentColor,
|
||||
(color) => {
|
||||
editor.chain().focus().setColor(color).run();
|
||||
setColorMenuOpen(false);
|
||||
},
|
||||
tToolbar("remove_color"),
|
||||
() => {
|
||||
editor.chain().focus().unsetColor().run();
|
||||
setColorMenuOpen(false);
|
||||
}}
|
||||
>
|
||||
<RemoveFormatting className="w-4 h-4" /> {tToolbar("remove_color")}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</ToolbarPopover>
|
||||
)}
|
||||
</div>
|
||||
{/* Background color - same palette, same grid and same swatch
|
||||
@@ -688,7 +936,11 @@ export function RichTextEditor({
|
||||
survives email round-trips like `color` does. */}
|
||||
<div ref={bgColorWrapperRef} className="relative">
|
||||
<ToolbarButton
|
||||
ref={bgColorButtonRef}
|
||||
item="background-color"
|
||||
active={!!currentBgColor}
|
||||
popup="menu"
|
||||
expanded={bgColorMenuOpen}
|
||||
onClick={() => setBgColorMenuOpen((v) => !v)}
|
||||
title={tToolbar("background_color")}
|
||||
>
|
||||
@@ -702,51 +954,43 @@ export function RichTextEditor({
|
||||
</span>
|
||||
</ToolbarButton>
|
||||
{bgColorMenuOpen && (
|
||||
<div className="absolute z-50 top-full start-0 mt-1 bg-popover border border-border rounded-md shadow-md p-2">
|
||||
<div className="grid gap-0.5" style={{ gridTemplateColumns: "repeat(8, 1fr)" }}>
|
||||
{TEXT_COLORS.map((color) => (
|
||||
<button
|
||||
key={color}
|
||||
type="button"
|
||||
title={color}
|
||||
onClick={() => {
|
||||
editor.chain().focus().setBackgroundColor(color).run();
|
||||
setBgColorMenuOpen(false);
|
||||
}}
|
||||
className={cn(
|
||||
"w-4 h-4 border border-border/60 rounded-[2px] transition-transform hover:scale-110",
|
||||
currentBgColor === color && "ring-1 ring-ring ring-offset-1"
|
||||
)}
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="h-px bg-border my-1.5" />
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-2 py-1 text-sm rounded hover:bg-accent text-start w-full"
|
||||
onClick={() => {
|
||||
<ToolbarPopover
|
||||
label={tToolbar("background_color")}
|
||||
role="menu"
|
||||
columns={8}
|
||||
triggerRef={bgColorButtonRef}
|
||||
onClose={() => setBgColorMenuOpen(false)}
|
||||
className="p-2"
|
||||
>
|
||||
{renderPalette(
|
||||
currentBgColor,
|
||||
(color) => {
|
||||
editor.chain().focus().setBackgroundColor(color).run();
|
||||
setBgColorMenuOpen(false);
|
||||
},
|
||||
tToolbar("remove_background_color"),
|
||||
() => {
|
||||
editor.chain().focus().unsetBackgroundColor().run();
|
||||
setBgColorMenuOpen(false);
|
||||
}}
|
||||
>
|
||||
<RemoveFormatting className="w-4 h-4" /> {tToolbar("remove_background_color")}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</ToolbarPopover>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ToolbarSeparator />
|
||||
|
||||
<ToolbarButton
|
||||
active={editor.isActive("heading", { level: 1 })}
|
||||
item="heading-1"
|
||||
pressed={editor.isActive("heading", { level: 1 })}
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
||||
title={tToolbar("heading_1")}
|
||||
>
|
||||
<Heading1 className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
active={editor.isActive("heading", { level: 2 })}
|
||||
item="heading-2"
|
||||
pressed={editor.isActive("heading", { level: 2 })}
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
||||
title={tToolbar("heading_2")}
|
||||
>
|
||||
@@ -756,28 +1000,32 @@ export function RichTextEditor({
|
||||
<ToolbarSeparator />
|
||||
|
||||
<ToolbarButton
|
||||
active={editor.isActive("bulletList")}
|
||||
item="bullet-list"
|
||||
pressed={editor.isActive("bulletList")}
|
||||
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
||||
title={tToolbar("bullet_list")}
|
||||
>
|
||||
<List className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
active={editor.isActive("orderedList")}
|
||||
item="ordered-list"
|
||||
pressed={editor.isActive("orderedList")}
|
||||
onClick={() => editor.chain().focus().toggleOrderedList().run()}
|
||||
title={tToolbar("ordered_list")}
|
||||
>
|
||||
<ListOrdered className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
active={editor.isActive("blockquote")}
|
||||
item="quote"
|
||||
pressed={editor.isActive("blockquote")}
|
||||
onClick={() => editor.chain().focus().toggleBlockquote().run()}
|
||||
title={tToolbar("quote")}
|
||||
>
|
||||
<Quote className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
active={editor.isActive("codeBlock")}
|
||||
item="code-block"
|
||||
pressed={editor.isActive("codeBlock")}
|
||||
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
|
||||
title={tToolbar("code_block")}
|
||||
>
|
||||
@@ -787,21 +1035,24 @@ export function RichTextEditor({
|
||||
<ToolbarSeparator />
|
||||
|
||||
<ToolbarButton
|
||||
active={editor.isActive({ textAlign: "left" })}
|
||||
item="align-left"
|
||||
pressed={editor.isActive({ textAlign: "left" })}
|
||||
onClick={() => editor.chain().focus().setTextAlign("left").run()}
|
||||
title={tToolbar("align_left")}
|
||||
>
|
||||
<AlignLeft className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
active={editor.isActive({ textAlign: "center" })}
|
||||
item="align-center"
|
||||
pressed={editor.isActive({ textAlign: "center" })}
|
||||
onClick={() => editor.chain().focus().setTextAlign("center").run()}
|
||||
title={tToolbar("align_center")}
|
||||
>
|
||||
<AlignCenter className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
active={editor.isActive({ textAlign: "right" })}
|
||||
item="align-right"
|
||||
pressed={editor.isActive({ textAlign: "right" })}
|
||||
onClick={() => editor.chain().focus().setTextAlign("right").run()}
|
||||
title={tToolbar("align_right")}
|
||||
>
|
||||
@@ -810,7 +1061,8 @@ export function RichTextEditor({
|
||||
|
||||
{rtlEditingSupport && (
|
||||
<ToolbarButton
|
||||
active={
|
||||
item="text-direction"
|
||||
pressed={
|
||||
(editor.getAttributes("paragraph").dir || editor.getAttributes("heading").dir) === "rtl"
|
||||
}
|
||||
onClick={() => {
|
||||
@@ -827,7 +1079,10 @@ export function RichTextEditor({
|
||||
<ToolbarSeparator />
|
||||
|
||||
<ToolbarButton
|
||||
item="link"
|
||||
active={editor.isActive("link")}
|
||||
popup="dialog"
|
||||
expanded={!!linkDialog}
|
||||
onClick={addLink}
|
||||
title={tToolbar("link")}
|
||||
>
|
||||
@@ -836,90 +1091,62 @@ export function RichTextEditor({
|
||||
|
||||
<div ref={tableWrapperRef} className="relative">
|
||||
<ToolbarButton
|
||||
ref={tableButtonRef}
|
||||
item="table"
|
||||
active={editor.isActive("table")}
|
||||
popup={editor.isActive("table") ? "menu" : "dialog"}
|
||||
expanded={tableMenuOpen}
|
||||
onClick={() => setTableMenuOpen((v) => !v)}
|
||||
title={tToolbar("table")}
|
||||
>
|
||||
<TableIcon className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
{tableMenuOpen && (
|
||||
<div className="absolute z-50 top-full start-0 mt-1 bg-popover border border-border rounded-md shadow-md p-2 min-w-[200px]">
|
||||
{editor.isActive("table") ? (
|
||||
editor.isActive("table") ? (
|
||||
<ToolbarPopover
|
||||
label={tToolbar("table")}
|
||||
role="menu"
|
||||
triggerRef={tableButtonRef}
|
||||
onClose={() => setTableMenuOpen(false)}
|
||||
className="p-2 min-w-[200px]"
|
||||
>
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-accent text-start"
|
||||
onClick={() => { editor.chain().focus().addRowBefore().run(); setTableMenuOpen(false); }}
|
||||
>
|
||||
<Rows3 className="w-4 h-4" /> {tToolbar("add_row_above")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-accent text-start"
|
||||
onClick={() => { editor.chain().focus().addRowAfter().run(); setTableMenuOpen(false); }}
|
||||
>
|
||||
<Rows3 className="w-4 h-4" /> {tToolbar("add_row_below")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-accent text-start"
|
||||
onClick={() => { editor.chain().focus().addColumnBefore().run(); setTableMenuOpen(false); }}
|
||||
>
|
||||
<Columns3 className="w-4 h-4" /> {tToolbar("add_column_before")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-accent text-start"
|
||||
onClick={() => { editor.chain().focus().addColumnAfter().run(); setTableMenuOpen(false); }}
|
||||
>
|
||||
<Columns3 className="w-4 h-4" /> {tToolbar("add_column_after")}
|
||||
</button>
|
||||
<div className="h-px bg-border my-1" />
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-accent text-start"
|
||||
onClick={() => { editor.chain().focus().deleteRow().run(); setTableMenuOpen(false); }}
|
||||
>
|
||||
<Trash2 className="w-4 h-4" /> {tToolbar("delete_row")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-accent text-start"
|
||||
onClick={() => { editor.chain().focus().deleteColumn().run(); setTableMenuOpen(false); }}
|
||||
>
|
||||
<Trash2 className="w-4 h-4" /> {tToolbar("delete_column")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-accent text-start"
|
||||
onClick={() => { editor.chain().focus().toggleHeaderRow().run(); setTableMenuOpen(false); }}
|
||||
>
|
||||
<Rows3 className="w-4 h-4" /> {tToolbar("toggle_header_row")}
|
||||
</button>
|
||||
<div className="h-px bg-border my-1" />
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-accent text-start text-destructive"
|
||||
onClick={() => { editor.chain().focus().deleteTable().run(); setTableMenuOpen(false); }}
|
||||
>
|
||||
<Trash2 className="w-4 h-4" /> {tToolbar("delete_table")}
|
||||
</button>
|
||||
{tableMenuItem(<Rows3 className="w-4 h-4" aria-hidden />, tToolbar("add_row_above"), () => editor.chain().focus().addRowBefore().run())}
|
||||
{tableMenuItem(<Rows3 className="w-4 h-4" aria-hidden />, tToolbar("add_row_below"), () => editor.chain().focus().addRowAfter().run())}
|
||||
{tableMenuItem(<Columns3 className="w-4 h-4" aria-hidden />, tToolbar("add_column_before"), () => editor.chain().focus().addColumnBefore().run())}
|
||||
{tableMenuItem(<Columns3 className="w-4 h-4" aria-hidden />, tToolbar("add_column_after"), () => editor.chain().focus().addColumnAfter().run())}
|
||||
<div className="h-px bg-border my-1" role="separator" />
|
||||
{tableMenuItem(<Trash2 className="w-4 h-4" aria-hidden />, tToolbar("delete_row"), () => editor.chain().focus().deleteRow().run())}
|
||||
{tableMenuItem(<Trash2 className="w-4 h-4" aria-hidden />, tToolbar("delete_column"), () => editor.chain().focus().deleteColumn().run())}
|
||||
{tableMenuItem(<Rows3 className="w-4 h-4" aria-hidden />, tToolbar("toggle_header_row"), () => editor.chain().focus().toggleHeaderRow().run())}
|
||||
<div className="h-px bg-border my-1" role="separator" />
|
||||
{tableMenuItem(<Trash2 className="w-4 h-4" aria-hidden />, tToolbar("delete_table"), () => editor.chain().focus().deleteTable().run(), true)}
|
||||
</div>
|
||||
) : (
|
||||
</ToolbarPopover>
|
||||
) : (
|
||||
<ToolbarPopover
|
||||
label={tToolbar("table")}
|
||||
role="dialog"
|
||||
columns={TABLE_PICKER_COLS}
|
||||
triggerRef={tableButtonRef}
|
||||
onClose={() => setTableMenuOpen(false)}
|
||||
className="p-2 min-w-[200px]"
|
||||
>
|
||||
<TableSizePicker
|
||||
onPick={(rows, cols) => {
|
||||
editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();
|
||||
setTableMenuOpen(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ToolbarPopover>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ToolbarSeparator />
|
||||
|
||||
<ToolbarButton
|
||||
item="clear-formatting"
|
||||
onClick={() => editor.chain().focus().clearNodes().unsetAllMarks().run()}
|
||||
title={tToolbar("clear_formatting")}
|
||||
>
|
||||
@@ -929,6 +1156,7 @@ export function RichTextEditor({
|
||||
<ToolbarSeparator />
|
||||
|
||||
<ToolbarButton
|
||||
item="undo"
|
||||
onClick={() => editor.chain().focus().undo().run()}
|
||||
disabled={!editor.can().undo()}
|
||||
title={tToolbar("undo")}
|
||||
@@ -936,6 +1164,7 @@ export function RichTextEditor({
|
||||
<Undo className="w-4 h-4" />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
item="redo"
|
||||
onClick={() => editor.chain().focus().redo().run()}
|
||||
disabled={!editor.can().redo()}
|
||||
title={tToolbar("redo")}
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "حجم الخط",
|
||||
"font_size_default": "افتراضي",
|
||||
"background_color": "لون الخلفية",
|
||||
"remove_background_color": "إزالة لون الخلفية"
|
||||
"remove_background_color": "إزالة لون الخلفية",
|
||||
"table_size": "جدول {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "أسود",
|
||||
"dark_gray": "رمادي داكن",
|
||||
"gray": "رمادي",
|
||||
"dark_red": "أحمر داكن",
|
||||
"orange": "برتقالي",
|
||||
"yellow": "أصفر",
|
||||
"green": "أخضر",
|
||||
"blue": "أزرق",
|
||||
"purple": "بنفسجي",
|
||||
"dark_pink": "وردي داكن",
|
||||
"brown": "بني",
|
||||
"red": "أحمر",
|
||||
"light_orange": "برتقالي فاتح",
|
||||
"light_green": "أخضر فاتح",
|
||||
"light_blue": "أزرق فاتح",
|
||||
"pink": "وردي"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "تم الإرسال - لكن التنظيف بعد الإرسال فشل، وقد تبقى مسودة قديمة.",
|
||||
"pop_out": "فتح الرد في نافذة منفصلة",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Mida de la lletra",
|
||||
"font_size_default": "Per defecte",
|
||||
"background_color": "Color de fons",
|
||||
"remove_background_color": "Elimina el color de fons"
|
||||
"remove_background_color": "Elimina el color de fons",
|
||||
"table_size": "Taula de {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Negre",
|
||||
"dark_gray": "Gris fosc",
|
||||
"gray": "Gris",
|
||||
"dark_red": "Vermell fosc",
|
||||
"orange": "Taronja",
|
||||
"yellow": "Groc",
|
||||
"green": "Verd",
|
||||
"blue": "Blau",
|
||||
"purple": "Morat",
|
||||
"dark_pink": "Rosa fosc",
|
||||
"brown": "Marró",
|
||||
"red": "Vermell",
|
||||
"light_orange": "Taronja clar",
|
||||
"light_green": "Verd clar",
|
||||
"light_blue": "Blau clar",
|
||||
"pink": "Rosa"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Enviat, però la neteja posterior a l'enviament ha fallat; és possible que quedi un esborrany obsolet.",
|
||||
"pop_out": "Obre la resposta en una finestra",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Velikost písma",
|
||||
"font_size_default": "Výchozí",
|
||||
"background_color": "Barva pozadí",
|
||||
"remove_background_color": "Odebrat barvu pozadí"
|
||||
"remove_background_color": "Odebrat barvu pozadí",
|
||||
"table_size": "Tabulka {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Černá",
|
||||
"dark_gray": "Tmavě šedá",
|
||||
"gray": "Šedá",
|
||||
"dark_red": "Tmavě červená",
|
||||
"orange": "Oranžová",
|
||||
"yellow": "Žlutá",
|
||||
"green": "Zelená",
|
||||
"blue": "Modrá",
|
||||
"purple": "Fialová",
|
||||
"dark_pink": "Tmavě růžová",
|
||||
"brown": "Hnědá",
|
||||
"red": "Červená",
|
||||
"light_orange": "Světle oranžová",
|
||||
"light_green": "Světle zelená",
|
||||
"light_blue": "Světle modrá",
|
||||
"pink": "Růžová"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Odesláno - ale následný úklid selhal, může zůstat zastaralý koncept.",
|
||||
"pop_out": "Otevřít odpověď v okně",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Skriftstørrelse",
|
||||
"font_size_default": "Standard",
|
||||
"background_color": "Baggrundsfarve",
|
||||
"remove_background_color": "Fjern baggrundsfarve"
|
||||
"remove_background_color": "Fjern baggrundsfarve",
|
||||
"table_size": "Tabel med {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Sort",
|
||||
"dark_gray": "Mørkegrå",
|
||||
"gray": "Grå",
|
||||
"dark_red": "Mørkerød",
|
||||
"orange": "Orange",
|
||||
"yellow": "Gul",
|
||||
"green": "Grøn",
|
||||
"blue": "Blå",
|
||||
"purple": "Lilla",
|
||||
"dark_pink": "Mørk pink",
|
||||
"brown": "Brun",
|
||||
"red": "Rød",
|
||||
"light_orange": "Lys orange",
|
||||
"light_green": "Lysegrøn",
|
||||
"light_blue": "Lyseblå",
|
||||
"pink": "Pink"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Sendt - men oprydningen bagefter mislykkedes, en forældet kladde kan blive stående.",
|
||||
"pop_out": "Åbn svaret i et vindue",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Schriftgröße",
|
||||
"font_size_default": "Standard",
|
||||
"background_color": "Hintergrundfarbe",
|
||||
"remove_background_color": "Hintergrundfarbe entfernen"
|
||||
"remove_background_color": "Hintergrundfarbe entfernen",
|
||||
"table_size": "Tabelle mit {rows} × {cols} Zellen",
|
||||
"colors": {
|
||||
"black": "Schwarz",
|
||||
"dark_gray": "Dunkelgrau",
|
||||
"gray": "Grau",
|
||||
"dark_red": "Dunkelrot",
|
||||
"orange": "Orange",
|
||||
"yellow": "Gelb",
|
||||
"green": "Grün",
|
||||
"blue": "Blau",
|
||||
"purple": "Lila",
|
||||
"dark_pink": "Dunkelrosa",
|
||||
"brown": "Braun",
|
||||
"red": "Rot",
|
||||
"light_orange": "Hellorange",
|
||||
"light_green": "Hellgrün",
|
||||
"light_blue": "Hellblau",
|
||||
"pink": "Rosa"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Gesendet - aber das Aufräumen danach schlug fehl, evtl. bleibt ein alter Entwurf sichtbar.",
|
||||
"pop_out": "Antwort in eigenem Fenster öffnen",
|
||||
|
||||
+20
-1
@@ -996,7 +996,26 @@
|
||||
"font_size": "Font size",
|
||||
"font_size_default": "Default",
|
||||
"background_color": "Background color",
|
||||
"remove_background_color": "Remove background color"
|
||||
"remove_background_color": "Remove background color",
|
||||
"table_size": "{rows} by {cols} table",
|
||||
"colors": {
|
||||
"black": "Black",
|
||||
"dark_gray": "Dark gray",
|
||||
"gray": "Gray",
|
||||
"dark_red": "Dark red",
|
||||
"orange": "Orange",
|
||||
"yellow": "Yellow",
|
||||
"green": "Green",
|
||||
"blue": "Blue",
|
||||
"purple": "Purple",
|
||||
"dark_pink": "Dark pink",
|
||||
"brown": "Brown",
|
||||
"red": "Red",
|
||||
"light_orange": "Light orange",
|
||||
"light_green": "Light green",
|
||||
"light_blue": "Light blue",
|
||||
"pink": "Pink"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Sent - but the post-send cleanup failed, a stale draft may remain.",
|
||||
"pop_out": "Pop out reply",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Tamaño de fuente",
|
||||
"font_size_default": "Predeterminado",
|
||||
"background_color": "Color de fondo",
|
||||
"remove_background_color": "Quitar color de fondo"
|
||||
"remove_background_color": "Quitar color de fondo",
|
||||
"table_size": "Tabla de {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Negro",
|
||||
"dark_gray": "Gris oscuro",
|
||||
"gray": "Gris",
|
||||
"dark_red": "Rojo oscuro",
|
||||
"orange": "Naranja",
|
||||
"yellow": "Amarillo",
|
||||
"green": "Verde",
|
||||
"blue": "Azul",
|
||||
"purple": "Morado",
|
||||
"dark_pink": "Rosa oscuro",
|
||||
"brown": "Marrón",
|
||||
"red": "Rojo",
|
||||
"light_orange": "Naranja claro",
|
||||
"light_green": "Verde claro",
|
||||
"light_blue": "Azul claro",
|
||||
"pink": "Rosa"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Enviado - pero la limpieza posterior falló, puede quedar un borrador obsoleto.",
|
||||
"pop_out": "Abrir la respuesta en una ventana",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Fondi suurus",
|
||||
"font_size_default": "Vaikimisi",
|
||||
"background_color": "Taustavärv",
|
||||
"remove_background_color": "Eemalda taustavärv"
|
||||
"remove_background_color": "Eemalda taustavärv",
|
||||
"table_size": "Tabel {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Must",
|
||||
"dark_gray": "Tumehall",
|
||||
"gray": "Hall",
|
||||
"dark_red": "Tumepunane",
|
||||
"orange": "Oranž",
|
||||
"yellow": "Kollane",
|
||||
"green": "Roheline",
|
||||
"blue": "Sinine",
|
||||
"purple": "Lilla",
|
||||
"dark_pink": "Tumeroosa",
|
||||
"brown": "Pruun",
|
||||
"red": "Punane",
|
||||
"light_orange": "Heleoranž",
|
||||
"light_green": "Heleroheline",
|
||||
"light_blue": "Helesinine",
|
||||
"pink": "Roosa"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Saadetud - kuid saatmisjärgne koristus ebaõnnestus, mustand võib alles jääda.",
|
||||
"pop_out": "Ava vastus eraldi aknas",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "اندازه فونت",
|
||||
"font_size_default": "پیشفرض",
|
||||
"background_color": "رنگ پسزمینه",
|
||||
"remove_background_color": "حذف رنگ پسزمینه"
|
||||
"remove_background_color": "حذف رنگ پسزمینه",
|
||||
"table_size": "جدول {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "سیاه",
|
||||
"dark_gray": "خاکستری تیره",
|
||||
"gray": "خاکستری",
|
||||
"dark_red": "قرمز تیره",
|
||||
"orange": "نارنجی",
|
||||
"yellow": "زرد",
|
||||
"green": "سبز",
|
||||
"blue": "آبی",
|
||||
"purple": "بنفش",
|
||||
"dark_pink": "صورتی تیره",
|
||||
"brown": "قهوهای",
|
||||
"red": "قرمز",
|
||||
"light_orange": "نارنجی روشن",
|
||||
"light_green": "سبز روشن",
|
||||
"light_blue": "آبی روشن",
|
||||
"pink": "صورتی"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "ارسال شد - اما پاکسازی پس از ارسال ناموفق بود، ممکن است پیشنویس قدیمی باقی بماند.",
|
||||
"pop_out": "باز کردن پاسخ در پنجره جدا",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Taille de police",
|
||||
"font_size_default": "Par défaut",
|
||||
"background_color": "Couleur d'arrière-plan",
|
||||
"remove_background_color": "Supprimer la couleur d'arrière-plan"
|
||||
"remove_background_color": "Supprimer la couleur d'arrière-plan",
|
||||
"table_size": "Tableau de {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Noir",
|
||||
"dark_gray": "Gris foncé",
|
||||
"gray": "Gris",
|
||||
"dark_red": "Rouge foncé",
|
||||
"orange": "Orange",
|
||||
"yellow": "Jaune",
|
||||
"green": "Vert",
|
||||
"blue": "Bleu",
|
||||
"purple": "Violet",
|
||||
"dark_pink": "Rose foncé",
|
||||
"brown": "Marron",
|
||||
"red": "Rouge",
|
||||
"light_orange": "Orange clair",
|
||||
"light_green": "Vert clair",
|
||||
"light_blue": "Bleu clair",
|
||||
"pink": "Rose"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Envoyé - mais le nettoyage après envoi a échoué, un ancien brouillon peut subsister.",
|
||||
"pop_out": "Ouvrir la réponse dans une fenêtre",
|
||||
|
||||
+20
-1
@@ -959,7 +959,26 @@
|
||||
"font_size": "גודל גופן",
|
||||
"font_size_default": "ברירת מחדל",
|
||||
"background_color": "צבע רקע",
|
||||
"remove_background_color": "הסר צבע רקע"
|
||||
"remove_background_color": "הסר צבע רקע",
|
||||
"table_size": "טבלה {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "שחור",
|
||||
"dark_gray": "אפור כהה",
|
||||
"gray": "אפור",
|
||||
"dark_red": "אדום כהה",
|
||||
"orange": "כתום",
|
||||
"yellow": "צהוב",
|
||||
"green": "ירוק",
|
||||
"blue": "כחול",
|
||||
"purple": "סגול",
|
||||
"dark_pink": "ורוד כהה",
|
||||
"brown": "חום",
|
||||
"red": "אדום",
|
||||
"light_orange": "כתום בהיר",
|
||||
"light_green": "ירוק בהיר",
|
||||
"light_blue": "כחול בהיר",
|
||||
"pink": "ורוד"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "נשלח - אך הניקוי שלאחר השליחה נכשל, ייתכן שתישאר טיוטה ישנה.",
|
||||
"pop_out": "פתיחת התשובה בחלון נפרד",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Betűméret",
|
||||
"font_size_default": "Alapértelmezett",
|
||||
"background_color": "Háttérszín",
|
||||
"remove_background_color": "Háttérszín eltávolítása"
|
||||
"remove_background_color": "Háttérszín eltávolítása",
|
||||
"table_size": "{rows} × {cols} méretű táblázat",
|
||||
"colors": {
|
||||
"black": "Fekete",
|
||||
"dark_gray": "Sötétszürke",
|
||||
"gray": "Szürke",
|
||||
"dark_red": "Sötétvörös",
|
||||
"orange": "Narancs",
|
||||
"yellow": "Sárga",
|
||||
"green": "Zöld",
|
||||
"blue": "Kék",
|
||||
"purple": "Lila",
|
||||
"dark_pink": "Sötét rózsaszín",
|
||||
"brown": "Barna",
|
||||
"red": "Piros",
|
||||
"light_orange": "Világos narancs",
|
||||
"light_green": "Világoszöld",
|
||||
"light_blue": "Világoskék",
|
||||
"pink": "Rózsaszín"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Elküldve - de az utólagos rendrakás nem sikerült, egy elavult piszkozat megmaradhat.",
|
||||
"pop_out": "Válasz megnyitása külön ablakban",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Dimensione carattere",
|
||||
"font_size_default": "Predefinita",
|
||||
"background_color": "Colore di sfondo",
|
||||
"remove_background_color": "Rimuovi colore di sfondo"
|
||||
"remove_background_color": "Rimuovi colore di sfondo",
|
||||
"table_size": "Tabella {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Nero",
|
||||
"dark_gray": "Grigio scuro",
|
||||
"gray": "Grigio",
|
||||
"dark_red": "Rosso scuro",
|
||||
"orange": "Arancione",
|
||||
"yellow": "Giallo",
|
||||
"green": "Verde",
|
||||
"blue": "Blu",
|
||||
"purple": "Viola",
|
||||
"dark_pink": "Rosa scuro",
|
||||
"brown": "Marrone",
|
||||
"red": "Rosso",
|
||||
"light_orange": "Arancione chiaro",
|
||||
"light_green": "Verde chiaro",
|
||||
"light_blue": "Azzurro",
|
||||
"pink": "Rosa"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Inviato - ma la pulizia successiva non è riuscita, potrebbe restare una bozza obsoleta.",
|
||||
"pop_out": "Apri la risposta in una finestra",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "文字サイズ",
|
||||
"font_size_default": "デフォルト",
|
||||
"background_color": "背景色",
|
||||
"remove_background_color": "背景色を削除"
|
||||
"remove_background_color": "背景色を削除",
|
||||
"table_size": "{rows} × {cols} の表",
|
||||
"colors": {
|
||||
"black": "黒",
|
||||
"dark_gray": "濃い灰色",
|
||||
"gray": "灰色",
|
||||
"dark_red": "濃い赤",
|
||||
"orange": "オレンジ",
|
||||
"yellow": "黄",
|
||||
"green": "緑",
|
||||
"blue": "青",
|
||||
"purple": "紫",
|
||||
"dark_pink": "濃いピンク",
|
||||
"brown": "茶",
|
||||
"red": "赤",
|
||||
"light_orange": "明るいオレンジ",
|
||||
"light_green": "明るい緑",
|
||||
"light_blue": "水色",
|
||||
"pink": "ピンク"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "送信されましたが、送信後の整理に失敗しました。古い下書きが残る場合があります。",
|
||||
"pop_out": "返信を別ウィンドウで開く",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "글자 크기",
|
||||
"font_size_default": "기본값",
|
||||
"background_color": "배경색",
|
||||
"remove_background_color": "배경색 제거"
|
||||
"remove_background_color": "배경색 제거",
|
||||
"table_size": "{rows} × {cols} 표",
|
||||
"colors": {
|
||||
"black": "검정",
|
||||
"dark_gray": "진한 회색",
|
||||
"gray": "회색",
|
||||
"dark_red": "진한 빨강",
|
||||
"orange": "주황",
|
||||
"yellow": "노랑",
|
||||
"green": "초록",
|
||||
"blue": "파랑",
|
||||
"purple": "보라",
|
||||
"dark_pink": "진한 분홍",
|
||||
"brown": "갈색",
|
||||
"red": "빨강",
|
||||
"light_orange": "연한 주황",
|
||||
"light_green": "연한 초록",
|
||||
"light_blue": "하늘색",
|
||||
"pink": "분홍"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "보냈지만 전송 후 정리에 실패했습니다. 오래된 임시 보관 메일이 남아 있을 수 있습니다.",
|
||||
"pop_out": "답장을 별도 창에서 열기",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Fonta izmērs",
|
||||
"font_size_default": "Noklusējums",
|
||||
"background_color": "Fona krāsa",
|
||||
"remove_background_color": "Noņemt fona krāsu"
|
||||
"remove_background_color": "Noņemt fona krāsu",
|
||||
"table_size": "Tabula {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Melna",
|
||||
"dark_gray": "Tumši pelēka",
|
||||
"gray": "Pelēka",
|
||||
"dark_red": "Tumši sarkana",
|
||||
"orange": "Oranža",
|
||||
"yellow": "Dzeltena",
|
||||
"green": "Zaļa",
|
||||
"blue": "Zila",
|
||||
"purple": "Violeta",
|
||||
"dark_pink": "Tumši rozā",
|
||||
"brown": "Brūna",
|
||||
"red": "Sarkana",
|
||||
"light_orange": "Gaiši oranža",
|
||||
"light_green": "Gaiši zaļa",
|
||||
"light_blue": "Gaiši zila",
|
||||
"pink": "Rozā"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Nosūtīts - bet pēcapstrāde neizdevās, var palikt novecojis melnraksts.",
|
||||
"pop_out": "Atvērt atbildi atsevišķā logā",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Фонтын хэмжээ",
|
||||
"font_size_default": "Үндсэн",
|
||||
"background_color": "Дэвсгэр өнгө",
|
||||
"remove_background_color": "Дэвсгэр өнгийг арилгах"
|
||||
"remove_background_color": "Дэвсгэр өнгийг арилгах",
|
||||
"table_size": "{rows} × {cols} хүснэгт",
|
||||
"colors": {
|
||||
"black": "Хар",
|
||||
"dark_gray": "Бараан саарал",
|
||||
"gray": "Саарал",
|
||||
"dark_red": "Бараан улаан",
|
||||
"orange": "Улбар шар",
|
||||
"yellow": "Шар",
|
||||
"green": "Ногоон",
|
||||
"blue": "Цэнхэр",
|
||||
"purple": "Нил ягаан",
|
||||
"dark_pink": "Бараан ягаан",
|
||||
"brown": "Хүрэн",
|
||||
"red": "Улаан",
|
||||
"light_orange": "Цайвар улбар шар",
|
||||
"light_green": "Цайвар ногоон",
|
||||
"light_blue": "Цайвар цэнхэр",
|
||||
"pink": "Ягаан"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Илгээсэн - гэхдээ илгээсний дараах цэвэрлэгээ амжилтгүй болсон тул хуучирсан ноорог үлдэж болзошгүй.",
|
||||
"pop_out": "Хариуг тусдаа цонхонд нээх",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Skriftstørrelse",
|
||||
"font_size_default": "Standard",
|
||||
"background_color": "Bakgrunnsfarge",
|
||||
"remove_background_color": "Fjern bakgrunnsfarge"
|
||||
"remove_background_color": "Fjern bakgrunnsfarge",
|
||||
"table_size": "Tabell med {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Svart",
|
||||
"dark_gray": "Mørkegrå",
|
||||
"gray": "Grå",
|
||||
"dark_red": "Mørkerød",
|
||||
"orange": "Oransje",
|
||||
"yellow": "Gul",
|
||||
"green": "Grønn",
|
||||
"blue": "Blå",
|
||||
"purple": "Lilla",
|
||||
"dark_pink": "Mørk rosa",
|
||||
"brown": "Brun",
|
||||
"red": "Rød",
|
||||
"light_orange": "Lys oransje",
|
||||
"light_green": "Lysegrønn",
|
||||
"light_blue": "Lyseblå",
|
||||
"pink": "Rosa"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Meldingen ble sendt, men oppryddingen etterpå mislyktes. Et gammelt utkast kan bli liggende.",
|
||||
"pop_out": "Åpne svaret i et eget vindu",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Lettergrootte",
|
||||
"font_size_default": "Standaard",
|
||||
"background_color": "Achtergrondkleur",
|
||||
"remove_background_color": "Achtergrondkleur verwijderen"
|
||||
"remove_background_color": "Achtergrondkleur verwijderen",
|
||||
"table_size": "Tabel van {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Zwart",
|
||||
"dark_gray": "Donkergrijs",
|
||||
"gray": "Grijs",
|
||||
"dark_red": "Donkerrood",
|
||||
"orange": "Oranje",
|
||||
"yellow": "Geel",
|
||||
"green": "Groen",
|
||||
"blue": "Blauw",
|
||||
"purple": "Paars",
|
||||
"dark_pink": "Donkerroze",
|
||||
"brown": "Bruin",
|
||||
"red": "Rood",
|
||||
"light_orange": "Lichtoranje",
|
||||
"light_green": "Lichtgroen",
|
||||
"light_blue": "Lichtblauw",
|
||||
"pink": "Roze"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Verzonden - maar het opruimen daarna is mislukt, mogelijk blijft een oud concept staan.",
|
||||
"pop_out": "Antwoord in een apart venster openen",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Rozmiar czcionki",
|
||||
"font_size_default": "Domyślny",
|
||||
"background_color": "Kolor tła",
|
||||
"remove_background_color": "Usuń kolor tła"
|
||||
"remove_background_color": "Usuń kolor tła",
|
||||
"table_size": "Tabela {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Czarny",
|
||||
"dark_gray": "Ciemnoszary",
|
||||
"gray": "Szary",
|
||||
"dark_red": "Ciemnoczerwony",
|
||||
"orange": "Pomarańczowy",
|
||||
"yellow": "Żółty",
|
||||
"green": "Zielony",
|
||||
"blue": "Niebieski",
|
||||
"purple": "Fioletowy",
|
||||
"dark_pink": "Ciemnoróżowy",
|
||||
"brown": "Brązowy",
|
||||
"red": "Czerwony",
|
||||
"light_orange": "Jasnopomarańczowy",
|
||||
"light_green": "Jasnozielony",
|
||||
"light_blue": "Jasnoniebieski",
|
||||
"pink": "Różowy"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Wysłano - ale późniejsze porządkowanie nie powiodło się, może pozostać nieaktualna wersja robocza.",
|
||||
"pop_out": "Otwórz odpowiedź w oknie",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Tamanho da fonte",
|
||||
"font_size_default": "Padrão",
|
||||
"background_color": "Cor de fundo",
|
||||
"remove_background_color": "Remover cor de fundo"
|
||||
"remove_background_color": "Remover cor de fundo",
|
||||
"table_size": "Tabela de {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Preto",
|
||||
"dark_gray": "Cinza-escuro",
|
||||
"gray": "Cinza",
|
||||
"dark_red": "Vermelho-escuro",
|
||||
"orange": "Laranja",
|
||||
"yellow": "Amarelo",
|
||||
"green": "Verde",
|
||||
"blue": "Azul",
|
||||
"purple": "Roxo",
|
||||
"dark_pink": "Rosa-escuro",
|
||||
"brown": "Marrom",
|
||||
"red": "Vermelho",
|
||||
"light_orange": "Laranja-claro",
|
||||
"light_green": "Verde-claro",
|
||||
"light_blue": "Azul-claro",
|
||||
"pink": "Rosa"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Enviado - mas a limpeza posterior falhou, um rascunho antigo pode permanecer.",
|
||||
"pop_out": "Abrir a resposta numa janela",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Dimensiune font",
|
||||
"font_size_default": "Implicit",
|
||||
"background_color": "Culoarea fundalului",
|
||||
"remove_background_color": "Elimină culoarea fundalului"
|
||||
"remove_background_color": "Elimină culoarea fundalului",
|
||||
"table_size": "Tabel {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Negru",
|
||||
"dark_gray": "Gri închis",
|
||||
"gray": "Gri",
|
||||
"dark_red": "Roșu închis",
|
||||
"orange": "Portocaliu",
|
||||
"yellow": "Galben",
|
||||
"green": "Verde",
|
||||
"blue": "Albastru",
|
||||
"purple": "Mov",
|
||||
"dark_pink": "Roz închis",
|
||||
"brown": "Maro",
|
||||
"red": "Roșu",
|
||||
"light_orange": "Portocaliu deschis",
|
||||
"light_green": "Verde deschis",
|
||||
"light_blue": "Albastru deschis",
|
||||
"pink": "Roz"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Trimis - dar curățarea ulterioară a eșuat, poate rămâne o ciornă veche.",
|
||||
"pop_out": "Deschide răspunsul într-o fereastră",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Размер шрифта",
|
||||
"font_size_default": "По умолчанию",
|
||||
"background_color": "Цвет фона",
|
||||
"remove_background_color": "Убрать цвет фона"
|
||||
"remove_background_color": "Убрать цвет фона",
|
||||
"table_size": "Таблица {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Чёрный",
|
||||
"dark_gray": "Тёмно-серый",
|
||||
"gray": "Серый",
|
||||
"dark_red": "Тёмно-красный",
|
||||
"orange": "Оранжевый",
|
||||
"yellow": "Жёлтый",
|
||||
"green": "Зелёный",
|
||||
"blue": "Синий",
|
||||
"purple": "Фиолетовый",
|
||||
"dark_pink": "Тёмно-розовый",
|
||||
"brown": "Коричневый",
|
||||
"red": "Красный",
|
||||
"light_orange": "Светло-оранжевый",
|
||||
"light_green": "Светло-зелёный",
|
||||
"light_blue": "Голубой",
|
||||
"pink": "Розовый"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Отправлено - но последующая очистка не удалась, может остаться устаревший черновик.",
|
||||
"pop_out": "Открыть ответ в отдельном окне",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Veľkosť písma",
|
||||
"font_size_default": "Predvolené",
|
||||
"background_color": "Farba pozadia",
|
||||
"remove_background_color": "Odstrániť farbu pozadia"
|
||||
"remove_background_color": "Odstrániť farbu pozadia",
|
||||
"table_size": "Tabuľka {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Čierna",
|
||||
"dark_gray": "Tmavosivá",
|
||||
"gray": "Sivá",
|
||||
"dark_red": "Tmavočervená",
|
||||
"orange": "Oranžová",
|
||||
"yellow": "Žltá",
|
||||
"green": "Zelená",
|
||||
"blue": "Modrá",
|
||||
"purple": "Fialová",
|
||||
"dark_pink": "Tmavoružová",
|
||||
"brown": "Hnedá",
|
||||
"red": "Červená",
|
||||
"light_orange": "Svetlooranžová",
|
||||
"light_green": "Svetlozelená",
|
||||
"light_blue": "Svetlomodrá",
|
||||
"pink": "Ružová"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Odoslané - ale následné upratovanie zlyhalo, môže zostať zastaraný koncept.",
|
||||
"pop_out": "Otvoriť odpoveď v okne",
|
||||
|
||||
+20
-1
@@ -1233,7 +1233,26 @@
|
||||
"font_size": "ขนาดตัวอักษร",
|
||||
"font_size_default": "ค่าเริ่มต้น",
|
||||
"background_color": "สีพื้นหลังข้อความ",
|
||||
"remove_background_color": "ล้างสีพื้นหลัง"
|
||||
"remove_background_color": "ล้างสีพื้นหลัง",
|
||||
"table_size": "ตาราง {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "ดำ",
|
||||
"dark_gray": "เทาเข้ม",
|
||||
"gray": "เทา",
|
||||
"dark_red": "แดงเข้ม",
|
||||
"orange": "ส้ม",
|
||||
"yellow": "เหลือง",
|
||||
"green": "เขียว",
|
||||
"blue": "น้ำเงิน",
|
||||
"purple": "ม่วง",
|
||||
"dark_pink": "ชมพูเข้ม",
|
||||
"brown": "น้ำตาล",
|
||||
"red": "แดง",
|
||||
"light_orange": "ส้มอ่อน",
|
||||
"light_green": "เขียวอ่อน",
|
||||
"light_blue": "ฟ้า",
|
||||
"pink": "ชมพู"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "ส่งแล้ว - แต่การล้างข้อมูลหลังการส่งล้มเหลว อาจมีข้อความร่างค้างอยู่",
|
||||
"pop_out": "เปิดการตอบกลับในหน้าต่างใหม่",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Yazı boyutu",
|
||||
"font_size_default": "Varsayılan",
|
||||
"background_color": "Arka plan rengi",
|
||||
"remove_background_color": "Arka plan rengini kaldır"
|
||||
"remove_background_color": "Arka plan rengini kaldır",
|
||||
"table_size": "{rows} × {cols} tablo",
|
||||
"colors": {
|
||||
"black": "Siyah",
|
||||
"dark_gray": "Koyu gri",
|
||||
"gray": "Gri",
|
||||
"dark_red": "Koyu kırmızı",
|
||||
"orange": "Turuncu",
|
||||
"yellow": "Sarı",
|
||||
"green": "Yeşil",
|
||||
"blue": "Mavi",
|
||||
"purple": "Mor",
|
||||
"dark_pink": "Koyu pembe",
|
||||
"brown": "Kahverengi",
|
||||
"red": "Kırmızı",
|
||||
"light_orange": "Açık turuncu",
|
||||
"light_green": "Açık yeşil",
|
||||
"light_blue": "Açık mavi",
|
||||
"pink": "Pembe"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Gönderildi - ancak sonrasındaki temizleme başarısız oldu, eski bir taslak kalabilir.",
|
||||
"pop_out": "Yanıtı ayrı pencerede aç",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "Розмір шрифту",
|
||||
"font_size_default": "За замовчуванням",
|
||||
"background_color": "Колір фону",
|
||||
"remove_background_color": "Прибрати колір фону"
|
||||
"remove_background_color": "Прибрати колір фону",
|
||||
"table_size": "Таблиця {rows} × {cols}",
|
||||
"colors": {
|
||||
"black": "Чорний",
|
||||
"dark_gray": "Темно-сірий",
|
||||
"gray": "Сірий",
|
||||
"dark_red": "Темно-червоний",
|
||||
"orange": "Помаранчевий",
|
||||
"yellow": "Жовтий",
|
||||
"green": "Зелений",
|
||||
"blue": "Синій",
|
||||
"purple": "Фіолетовий",
|
||||
"dark_pink": "Темно-рожевий",
|
||||
"brown": "Коричневий",
|
||||
"red": "Червоний",
|
||||
"light_orange": "Світло-помаранчевий",
|
||||
"light_green": "Світло-зелений",
|
||||
"light_blue": "Блакитний",
|
||||
"pink": "Рожевий"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "Надіслано - але подальше очищення не вдалося, може залишитися застаріла чернетка.",
|
||||
"pop_out": "Відкрити відповідь в окремому вікні",
|
||||
|
||||
@@ -994,7 +994,26 @@
|
||||
"font_size": "字型大小",
|
||||
"font_size_default": "預設",
|
||||
"background_color": "背景顏色",
|
||||
"remove_background_color": "移除背景顏色"
|
||||
"remove_background_color": "移除背景顏色",
|
||||
"table_size": "{rows} × {cols} 表格",
|
||||
"colors": {
|
||||
"black": "黑色",
|
||||
"dark_gray": "深灰色",
|
||||
"gray": "灰色",
|
||||
"dark_red": "深紅色",
|
||||
"orange": "橘色",
|
||||
"yellow": "黃色",
|
||||
"green": "綠色",
|
||||
"blue": "藍色",
|
||||
"purple": "紫色",
|
||||
"dark_pink": "深粉紅色",
|
||||
"brown": "棕色",
|
||||
"red": "紅色",
|
||||
"light_orange": "淺橘色",
|
||||
"light_green": "淺綠色",
|
||||
"light_blue": "淺藍色",
|
||||
"pink": "粉紅色"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "郵件已傳送,但寄送後清理失敗,可能仍保留舊草稿。",
|
||||
"pop_out": "在獨立視窗中開啟回覆",
|
||||
|
||||
+20
-1
@@ -994,7 +994,26 @@
|
||||
"font_size": "字号",
|
||||
"font_size_default": "默认",
|
||||
"background_color": "背景颜色",
|
||||
"remove_background_color": "移除背景颜色"
|
||||
"remove_background_color": "移除背景颜色",
|
||||
"table_size": "{rows} × {cols} 表格",
|
||||
"colors": {
|
||||
"black": "黑色",
|
||||
"dark_gray": "深灰色",
|
||||
"gray": "灰色",
|
||||
"dark_red": "深红色",
|
||||
"orange": "橙色",
|
||||
"yellow": "黄色",
|
||||
"green": "绿色",
|
||||
"blue": "蓝色",
|
||||
"purple": "紫色",
|
||||
"dark_pink": "深粉色",
|
||||
"brown": "棕色",
|
||||
"red": "红色",
|
||||
"light_orange": "浅橙色",
|
||||
"light_green": "浅绿色",
|
||||
"light_blue": "浅蓝色",
|
||||
"pink": "粉色"
|
||||
}
|
||||
},
|
||||
"send_filing_warning": "已发送,但发送后的清理失败,可能会残留旧草稿。",
|
||||
"pop_out": "在单独窗口中打开回复",
|
||||
|
||||
Reference in New Issue
Block a user