From 6055dac1cbbab9e820fec5acefc35885c44a65ef Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Mon, 2 Nov 2026 20:26:21 +0100 Subject: [PATCH] fix: keep proofreading text from plugins that shouldn't get it, and work it by keyboard #1397 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A security and an accessibility review of the proofreading hook found gaps, fixed here. What leaves the browser: - A checker gets a name for each composer of its own, not the composer's id, so it can't reach the message through the other api.composer calls after proofreading stops. - Checking pauses while a plugin that can sign or encrypt is turned on but still starting, and while the policy couldn't be fetched (the built-in defaults could undo an admin's lock). - No plugin can hold up turning proofreading off (onBeforeSettingChange). - Suggestions can't carry zero-width, bidi or line-separator characters; messages lose bidi overrides; a flood of annotations is cut off early. - The notices name the plugin as installed, not a label it chose, and say that text written before signing or encryption was turned on may already have been checked. - A checker registered during start-up no longer fails its first check. Keyboard and screen readers: - Escape in the issue panel closes the panel, not the message. - Underlines carry aria-invalid, as the browser's own markers do. - The focus goes somewhere sensible when a button or an issue goes away, Tab past the popup's edges goes back to the text, and the shortcuts work in the popup and the panel. - Ctrl+' and Ctrl+; follow the character typed, so other layouts keep their own keys (Ctrl+S on Dvorak), with Ä and Ö on German keyboards. - The popup is described by its message, sits inside a modal composer, fits narrow screens, and the panel fits a phone. --- app/accessibility.css | 1 + app/globals.css | 3 +- .../org/sections/proofreading-section.tsx | 2 +- .../email/__tests__/proofreading-ui.test.tsx | 37 +++++++- .../__tests__/text-check-extension.test.ts | 6 +- components/email/email-composer.tsx | 7 +- .../email/proofreading/issue-actions.tsx | 11 ++- components/email/proofreading/issue-popup.tsx | 53 ++++++++--- .../proofreading/proofreading-notice.tsx | 7 +- .../proofreading/proofreading-status.tsx | 58 +++++++++--- .../proofreading/text-check-extension.ts | 23 ++++- .../email/proofreading/use-proofreading.ts | 42 ++++++++- .../__tests__/proofreading-settings.test.tsx | 2 +- components/settings/proofreading-settings.tsx | 3 +- .../proofreading-proofreader.test.ts | 2 +- .../proofreading-review-fixes.test.ts | 94 +++++++++++++++++++ lib/plugin-sandbox/host-api-checker.ts | 3 + lib/plugin-sandbox/loader.ts | 24 ++++- lib/plugin-sandbox/runtime.tsx | 4 +- lib/proofreading/annotations.ts | 43 ++++++--- lib/proofreading/composer-proofreader.ts | 25 +++-- lib/proofreading/protection.ts | 14 ++- locales/ar/common.json | 10 +- locales/ca/common.json | 10 +- locales/cs/common.json | 10 +- locales/da/common.json | 10 +- locales/de/common.json | 10 +- locales/en/common.json | 10 +- locales/es/common.json | 10 +- locales/et/common.json | 10 +- locales/fa/common.json | 10 +- locales/fr/common.json | 10 +- locales/he/common.json | 10 +- locales/hu/common.json | 10 +- locales/it/common.json | 10 +- locales/ja/common.json | 10 +- locales/ko/common.json | 10 +- locales/lv/common.json | 10 +- locales/mn/common.json | 10 +- locales/nb/common.json | 10 +- locales/nl/common.json | 10 +- locales/pl/common.json | 10 +- locales/pt/common.json | 10 +- locales/ro/common.json | 10 +- locales/ru/common.json | 10 +- locales/sk/common.json | 10 +- locales/th/common.json | 10 +- locales/tr/common.json | 10 +- locales/uk/common.json | 10 +- locales/vi/common.json | 10 +- locales/zh-TW/common.json | 10 +- locales/zh/common.json | 10 +- .../settings-store-plugin-hooks.test.ts | 13 +++ .../settings-store-proofreading.test.ts | 11 ++- stores/settings-store.ts | 13 ++- 55 files changed, 569 insertions(+), 232 deletions(-) create mode 100644 lib/__tests__/proofreading-review-fixes.test.ts diff --git a/app/accessibility.css b/app/accessibility.css index 1fb0d8ad3..60ba4140a 100644 --- a/app/accessibility.css +++ b/app/accessibility.css @@ -285,6 +285,7 @@ html.dark[data-contrast="more"] { text-decoration-thickness: 2px; } .tiptap .text-issue-active { + forced-color-adjust: none; background-color: Highlight; color: HighlightText; } diff --git a/app/globals.css b/app/globals.css index ceb031fa4..15f61878d 100644 --- a/app/globals.css +++ b/app/globals.css @@ -934,7 +934,6 @@ optgroup { text-decoration-thickness: 1.5px; text-underline-offset: 3px; text-decoration-skip-ink: none; - cursor: pointer; } .tiptap .text-issue-spelling { text-decoration-style: wavy; @@ -955,6 +954,8 @@ optgroup { } .tiptap .text-issue-active { background-color: color-mix(in srgb, var(--color-primary) 18%, transparent); + outline: 1px solid var(--color-primary); + outline-offset: 1px; border-radius: 2px; } diff --git a/components/admin/org/sections/proofreading-section.tsx b/components/admin/org/sections/proofreading-section.tsx index bd20d26d1..e9e355cfe 100644 --- a/components/admin/org/sections/proofreading-section.tsx +++ b/components/admin/org/sections/proofreading-section.tsx @@ -132,7 +132,7 @@ export function ProofreadingSection({ draft }: { draft: OrgPolicyDraft }) {
diff --git a/components/email/__tests__/proofreading-ui.test.tsx b/components/email/__tests__/proofreading-ui.test.tsx index 9f6130e02..96d99afb5 100644 --- a/components/email/__tests__/proofreading-ui.test.tsx +++ b/components/email/__tests__/proofreading-ui.test.tsx @@ -83,7 +83,7 @@ describe('ProofreadingStatus', () => { it('lists issues that jump to the text, each read out with its first suggestion', () => { const p = proofreading({ panel: { focusKey: null } }); render(); - const item = screen.getByRole('button', { name: 'list_item' }); + const item = screen.getByRole('button', { name: /^list_item/ }); fireEvent.click(item); expect(p.jumpTo).toHaveBeenCalledWith(expect.objectContaining({ key: issue().key })); }); @@ -94,6 +94,17 @@ describe('ProofreadingStatus', () => { expect(document.activeElement?.getAttribute('data-issue-key')).toBe(issue().key); }); + it('names the control with what it is about, and groups each plain-text issue', () => { + render(); + expect(screen.getByTestId('proofreading-status').getAttribute('aria-label')).toBe('status.issues, status.title'); + expect(screen.getByRole('group', { name: /list_item/ })).toBeInTheDocument(); + }); + + it('does not read out "no issues" when a message opens with none', () => { + render(); + expect(announce).not.toHaveBeenCalled(); + }); + it('in plain text, puts the suggestions in the list', () => { const p = proofreading({ panel: { focusKey: null } }); render(); @@ -105,7 +116,8 @@ describe('ProofreadingStatus', () => { describe('IssuePopup', () => { const props = (over: Partial> = {}) => ({ state: { key: issue().key, anchor: new DOMRect(10, 10, 30, 16), via: 'click' as const }, - issue: issue(), anchor: () => null, onApply: vi.fn(() => true), onDismiss: vi.fn(), onClose: vi.fn(), ...over, + issue: issue(), anchor: () => null, onApply: vi.fn(() => true), onDismiss: vi.fn(), onClose: vi.fn(), + onNavigate: vi.fn(), textRoot: () => null, container: document.body, ...over, }); it('puts in a suggestion, and says so when the text changed meanwhile', () => { @@ -136,6 +148,27 @@ describe('IssuePopup', () => { expect(p.onClose).toHaveBeenCalledWith(true); }); + it('goes back to the text on Tab past its last button or Shift+Tab before its first', () => { + const p = props({ state: { key: issue().key, anchor: null, via: 'keyboard' } }); + render(); + const items = screen.getByTestId('proofreading-popup').querySelectorAll('[data-popup-item]'); + fireEvent.keyDown(items[0], { key: 'Tab', shiftKey: true }); + expect(p.onClose).toHaveBeenCalledWith(true); + vi.mocked(p.onClose).mockClear(); + items[items.length - 1].focus(); + fireEvent.keyDown(items[items.length - 1], { key: 'Tab' }); + expect(p.onClose).toHaveBeenCalledWith(true); + }); + + it('steps to the next issue with the shortcut and is described by its message', () => { + const p = props({ state: { key: issue().key, anchor: null, via: 'keyboard' } }); + render(); + fireEvent.keyDown(document.activeElement!, { key: "'", code: 'Quote', ctrlKey: true }); + expect(p.onNavigate).toHaveBeenCalledWith(1); + const dialog = screen.getByTestId('proofreading-popup'); + expect(document.getElementById(dialog.getAttribute('aria-describedby')!)?.textContent).toContain('Did you mean'); + }); + it('leaves the focus in the text when opened with the mouse', () => { render(); expect(screen.getByTestId('proofreading-popup').contains(document.activeElement)).toBe(false); diff --git a/components/email/__tests__/text-check-extension.test.ts b/components/email/__tests__/text-check-extension.test.ts index 4243f78a2..a51797d57 100644 --- a/components/email/__tests__/text-check-extension.test.ts +++ b/components/email/__tests__/text-check-extension.test.ts @@ -34,6 +34,8 @@ describe('text check underlines', () => { setTextIssues(editor, [range(editor, 'dem'), { ...range(editor, 'Brief', 'stale'), text: 'Briefe' }]); expect(drawnIssues(editor.state).map((r) => r.key)).toEqual(['dem']); expect(editor.view.dom.querySelector('[data-text-issue="dem"]')?.className).toContain('text-issue-grammar'); + // Screen readers hear it as they do the browser's own markers. + expect(editor.view.dom.querySelector('[data-text-issue="dem"]')?.getAttribute('aria-invalid')).toBe('grammar'); }); it('moves with typing before it, keeps still for typing after it, and goes when its text is edited', () => { @@ -103,11 +105,11 @@ describe('text check underlines', () => { const { editor, onOpen } = setup('Ich habe dem Brief'); setTextIssues(editor, [range(editor, 'dem')]); const el = editor.view.dom.querySelector('[data-text-issue="dem"]')!; - const event = new MouseEvent('contextmenu', { bubbles: true, cancelable: true }); + const event = new MouseEvent('contextmenu', { bubbles: true, cancelable: true, clientX: 20, clientY: 20 }); el.dispatchEvent(event); expect(event.defaultPrevented).toBe(true); expect(onOpen).toHaveBeenCalledWith('dem', expect.anything(), 'contextmenu'); - const plain = new MouseEvent('contextmenu', { bubbles: true, cancelable: true }); + const plain = new MouseEvent('contextmenu', { bubbles: true, cancelable: true, clientX: 5, clientY: 5 }); editor.view.dom.querySelector('p')!.dispatchEvent(plain); expect(plain.defaultPrevented).toBe(false); }); diff --git a/components/email/email-composer.tsx b/components/email/email-composer.tsx index 24eafec77..87ae048e9 100644 --- a/components/email/email-composer.tsx +++ b/components/email/email-composer.tsx @@ -4198,8 +4198,8 @@ export function EmailComposer({ // Same for the "@" recipient list in the body: this handler runs in the // capture phase, before the editor could close the list itself. if (isRecipientMentionActive(editorRef.current)) return; - // And for a proofreading popup: Escape closes only it. - if (proofreading.popup) return; + // And for proofreading's popup and panel: Escape closes only them. + if (proofreading.popup || proofreading.panel) return; e.preventDefault(); handleClose(); return; @@ -4766,6 +4766,9 @@ export function EmailComposer({ onApply={proofreading.apply} onDismiss={proofreading.dismiss} onClose={proofreading.closePopup} + onNavigate={proofreading.navigate} + textRoot={() => (pluginEditor.isDestroyed ? null : pluginEditor.view.dom)} + container={(pluginEditor.isDestroyed ? null : pluginEditor.view.dom.closest('[aria-modal="true"]')) ?? document.body} /> ) : null; })()} diff --git a/components/email/proofreading/issue-actions.tsx b/components/email/proofreading/issue-actions.tsx index 38083f046..e2f891909 100644 --- a/components/email/proofreading/issue-actions.tsx +++ b/components/email/proofreading/issue-actions.tsx @@ -28,6 +28,8 @@ interface Props { showWord?: boolean; /** Shown when a suggestion could not be put in (the text changed). */ error?: string | null; + /** id for the message, which the popup is described by. */ + messageId?: string; } export function IssueHeading({ issue }: { issue: PlacedIssue }) { @@ -67,13 +69,13 @@ function Suggestions({ issue, onApply }: Pick) { ); } -export function IssueActions({ issue, onApply, onDismiss, showWord = false, error }: Props) { +export function IssueActions({ issue, onApply, onDismiss, showWord = false, error, messageId }: Props) { const t = useTranslations("proofreading"); const a = issue.annotation; return (
-

+

{showWord && <>{a.text}{": "}} {a.message}

@@ -112,10 +114,11 @@ export function IssueActions({ issue, onApply, onDismiss, showWord = false, erro ); } -/** What a screen reader reads for an issue in the list. */ +/** What a screen reader reads for an issue in the list: the word first, as it shows. */ export function issueListLabel(t: ReturnType, issue: PlacedIssue): string { const a = issue.annotation; - return a.replacements.length + const text = a.replacements.length ? t("list_item", { word: a.text, message: a.message, suggestion: a.replacements[0] }) : t("list_item_no_suggestion", { word: a.text, message: a.message }); + return `${text} (${t(`kind.${issue.kind}`)})`; } diff --git a/components/email/proofreading/issue-popup.tsx b/components/email/proofreading/issue-popup.tsx index 875122bb1..25baf6cd0 100644 --- a/components/email/proofreading/issue-popup.tsx +++ b/components/email/proofreading/issue-popup.tsx @@ -9,7 +9,8 @@ // issue list, the context-menu key), it takes the focus: arrows move between // its buttons, Enter picks, Escape goes back to the text. -import { useEffect, useLayoutEffect, useRef, useState } from "react"; +import { useEffect, useId, useLayoutEffect, useRef, useState } from "react"; +import { issueNavigationKey } from "./text-check-extension"; import { createPortal } from "react-dom"; import { useTranslations } from "next-intl"; import type { DismissAction, PlacedIssue } from "@/lib/proofreading/composer-proofreader"; @@ -27,23 +28,35 @@ interface Props { onApply: (issue: PlacedIssue, replacement: string) => boolean; onDismiss: (issue: PlacedIssue, action: DismissAction) => void; onClose: (refocus: boolean) => void; + /** Next or previous issue (Mod+' and Mod+; work in the popup too). */ + onNavigate: (dir: 1 | -1) => void; + /** The text being written: focus going back there leaves the popup open. */ + textRoot: () => HTMLElement | null; + /** + * Where the popup goes in the page: inside an open modal (Mountain View's + * maximised composer), so screen readers don't hide it with the rest. + */ + container: HTMLElement; } function place(anchor: DOMRect | null, height: number): { top: number; left: number } { const vw = window.innerWidth; - const vh = window.innerHeight; - if (!anchor) return { top: Math.max(8, vh / 2 - height / 2), left: Math.max(8, vw / 2 - WIDTH / 2) }; + // Above an on-screen keyboard, where there is one. + const vh = window.visualViewport?.height ?? window.innerHeight; + const width = Math.min(WIDTH, vw - 16); + if (!anchor) return { top: Math.max(8, vh / 2 - height / 2), left: Math.max(8, vw / 2 - width / 2) }; const rtl = document.documentElement.dir === "rtl"; - let left = rtl ? anchor.right - WIDTH : anchor.left; - left = Math.min(Math.max(8, left), vw - WIDTH - 8); + let left = rtl ? anchor.right - width : anchor.left; + left = Math.min(Math.max(8, left), vw - width - 8); let top = anchor.bottom + GAP; if (top + height > vh - 8 && anchor.top - GAP - height > 8) top = anchor.top - GAP - height; return { top: Math.max(8, top), left }; } -export function IssuePopup({ state, issue, anchor, onApply, onDismiss, onClose }: Props) { +export function IssuePopup({ state, issue, anchor, onApply, onDismiss, onClose, onNavigate, textRoot, container }: Props) { const t = useTranslations("proofreading"); const ref = useRef(null); + const messageId = useId(); const [pos, setPos] = useState<{ top: number; left: number } | null>(null); const [error, setError] = useState(null); const anchorRef = useRef(anchor); @@ -87,6 +100,23 @@ export function IssuePopup({ state, issue, anchor, onApply, onDismiss, onClose } onClose(true); return; } + const dir = issueNavigationKey(e.nativeEvent); + if (dir) { + e.preventDefault(); + onNavigate(dir); + return; + } + if (e.key === "Tab") { + // The popup sits at the end of the page: Tab past its edges goes back + // to the text instead of off the page or somewhere before it. + const items = Array.from(ref.current?.querySelectorAll("[data-popup-item]") ?? []); + const at = items.indexOf(document.activeElement as HTMLElement); + if ((e.shiftKey && at === 0) || (!e.shiftKey && at === items.length - 1)) { + e.preventDefault(); + onClose(true); + } + return; + } if (e.key !== "ArrowDown" && e.key !== "ArrowUp" && e.key !== "ArrowLeft" && e.key !== "ArrowRight" && e.key !== "Home" && e.key !== "End") return; const items = Array.from(ref.current?.querySelectorAll("[data-popup-item]") ?? []); if (!items.length) return; @@ -111,19 +141,20 @@ export function IssuePopup({ state, issue, anchor, onApply, onDismiss, onClose } ref={ref} role="dialog" aria-label={t("popup.label", { kind: t(`kind.${issue.kind}`), word: issue.annotation.text })} + aria-describedby={messageId} data-testid="proofreading-popup" - className="fixed z-50 rounded-md border border-border bg-popover p-3 text-popover-foreground shadow-lg" - style={{ width: WIDTH, maxWidth: "calc(100vw - 16px)", top: pos?.top ?? -9999, left: pos?.left ?? -9999 }} + className="fixed z-50 overflow-y-auto rounded-md border border-border bg-popover p-3 text-popover-foreground shadow-lg" + style={{ width: WIDTH, maxWidth: "calc(100vw - 16px)", maxHeight: "calc(100dvh - 16px)", top: pos?.top ?? -9999, left: pos?.left ?? -9999 }} onKeyDown={onKeyDown} onBlur={(e) => { // Tabbing out of it closes it; a click inside, or going back to the // text (where Escape closes it), doesn't. const next = e.relatedTarget as Element | null; - if (next && !ref.current?.contains(next) && !next.closest?.(".tiptap")) onClose(false); + if (next && !ref.current?.contains(next) && !textRoot()?.contains(next)) onClose(false); }} > - +
, - document.body, + container, ); } diff --git a/components/email/proofreading/proofreading-notice.tsx b/components/email/proofreading/proofreading-notice.tsx index 0f521e731..503c28ce8 100644 --- a/components/email/proofreading/proofreading-notice.tsx +++ b/components/email/proofreading/proofreading-notice.tsx @@ -5,6 +5,7 @@ // organisation turned it on (then worded as their choice). OK hides it for // good on every device (the setting is synced). +import { useId } from "react"; import { useTranslations } from "next-intl"; import { SpellCheck } from "@/components/icons"; import { useSettingsStore } from "@/stores/settings-store"; @@ -12,6 +13,7 @@ import type { ProofreaderStatus } from "@/lib/proofreading/composer-proofreader" export function ProofreadingNotice({ status }: { status: ProofreaderStatus }) { const t = useTranslations("proofreading"); + const textId = useId(); const seen = useSettingsStore((s) => s.proofreadingNoticeSeen); const updateSetting = useSettingsStore((s) => s.updateSetting); if (!status.on) return null; @@ -21,13 +23,14 @@ export function ProofreadingNotice({ status }: { status: ProofreaderStatus }) { return (
); } -function PanelBody({ proofreading, plainTextMode, issues, onDone }: Props & { issues: Proofreading["issues"]; onDone: () => void }) { +function PanelBody({ proofreading, plainTextMode, issues, onDone, onTurnedOn }: Props & { issues: Proofreading["issues"]; onDone: () => void; onTurnedOn: () => void }) { const t = useTranslations("proofreading"); const { status, composerOn, setComposerOn } = proofreading; const updateSetting = useSettingsStore((s) => s.updateSetting); @@ -146,6 +159,8 @@ function PanelBody({ proofreading, plainTextMode, issues, onDone }: Props & { is updateSetting("proofreading", true); updateSetting("proofreadingNoticeSeen", { ...noticeSeen, ...Object.fromEntries(checkers.map((c) => [c.pluginId, true])) }); if (!composerOn) setComposerOn(true); + // The button goes away: the focus goes to the panel's first control. + onTurnedOn(); }; return ( @@ -213,9 +228,11 @@ function PanelBody({ proofreading, plainTextMode, issues, onDone }: Props & { is {issue.annotation.text} {issue.annotation.replacements[0] && ( - <> {issue.annotation.replacements[0]} + <> {issue.annotation.replacements[0]} )} - {issue.annotation.shortMessage ?? issue.annotation.message} + + {t(`kind.${issue.kind}`)} · {issue.annotation.shortMessage ?? issue.annotation.message} + )} @@ -236,14 +253,33 @@ function PanelBody({ proofreading, plainTextMode, issues, onDone }: Props & { is function PlainIssue({ issue, proofreading }: { issue: Proofreading["issues"][number]; proofreading: Proofreading }) { const t = useTranslations("proofreading"); + const ref = useRef(null); + // The card goes away with its issue: the focus moves to the next card, or + // back to the text when it was the last. + const thenFocusNext = (act: () => void) => { + const card = ref.current?.closest("li"); + const next = (card?.nextElementSibling ?? card?.previousElementSibling)?.querySelector("[data-issue-key]"); + act(); + requestAnimationFrame(() => { + if (next?.isConnected) next.focus(); + else { proofreading.setPanel(null); proofreading.closePopup(true); } + }); + }; return (
- { proofreading.apply(i, r); }} onDismiss={proofreading.dismiss} /> + thenFocusNext(() => { proofreading.apply(i, r); })} + onDismiss={(i, action) => thenFocusNext(() => proofreading.dismiss(i, action))} + />
); } diff --git a/components/email/proofreading/text-check-extension.ts b/components/email/proofreading/text-check-extension.ts index fb524a358..1fa08bddf 100644 --- a/components/email/proofreading/text-check-extension.ts +++ b/components/email/proofreading/text-check-extension.ts @@ -51,11 +51,17 @@ export interface TextCheckOptions { } /** - * Next (1) or previous (-1) issue: Mod+' and Mod+;, by the key's place on - * the keyboard (Ä and Ö on a German one), with or without Shift. + * Next (1) or previous (-1) issue: Mod+' and Mod+;. By the character the + * key types, so other layouts keep their own keys (on Dvorak the ;-position + * types "s": Ctrl+S stays Save). Only where those keys type a letter of + * their own (Ä and Ö on a German keyboard) by their place on the keyboard. */ export function issueNavigationKey(event: KeyboardEvent): 1 | -1 | null { if (!(event.ctrlKey || event.metaKey) || event.altKey) return null; + if (event.key === "'") return 1; + if (event.key === ';') return -1; + const ascii = event.key.length === 1 && event.key.charCodeAt(0) < 128; + if (ascii) return null; if (event.code === 'Quote') return 1; if (event.code === 'Semicolon') return -1; return null; @@ -68,6 +74,9 @@ function decorationFor(range: TextIssueRange, active: boolean): Decoration { { class: `text-issue text-issue-${range.kind}${active ? ' text-issue-active' : ''}`, 'data-text-issue': range.key, + // Screen readers announce this as they do the browser's own markers, + // which are off while a checker checks spelling. + 'aria-invalid': range.kind === 'spelling' ? 'spelling' : 'grammar', }, { key: range.key, kind: range.kind, text: range.text } satisfies IssueSpec, ); @@ -157,7 +166,13 @@ export function createTextCheckExtension(options: () => TextCheckOptions) { }, handleDOMEvents: { contextmenu(view, event) { - const el = (event.target as Element | null)?.closest?.('[data-text-issue]'); + let el = (event.target as Element | null)?.closest?.('[data-text-issue]') ?? null; + // From the keyboard (Shift+F10, the Menu key) the event can + // land on the editor: take the issue at the caret. + if (!el && event.clientX === 0 && event.clientY === 0) { + const at = issueAt(view, view.state.selection.from); + if (at) el = view.dom.querySelector(`[data-text-issue="${CSS.escape((at.spec as IssueSpec).key)}"]`); + } const key = el?.getAttribute('data-text-issue'); if (!el || !key) return false; const known = textCheckKey.getState(view.state)?.decos.find().some((d) => (d.spec as IssueSpec).key === key); @@ -166,7 +181,7 @@ export function createTextCheckExtension(options: () => TextCheckOptions) { // plugins' selection menu above it). event.preventDefault(); event.stopPropagation(); - options().onOpen(key, el.getBoundingClientRect(), 'contextmenu'); + options().onOpen(key, el.getBoundingClientRect(), event.clientX === 0 && event.clientY === 0 ? 'keyboard' : 'contextmenu'); return true; }, }, diff --git a/components/email/proofreading/use-proofreading.ts b/components/email/proofreading/use-proofreading.ts index d9b0b0a6d..6811e2aa8 100644 --- a/components/email/proofreading/use-proofreading.ts +++ b/components/email/proofreading/use-proofreading.ts @@ -5,6 +5,8 @@ // textarea, draws their underlines and applies the fixes the user picks. import { useCallback, useEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore, type RefObject } from "react"; +import { useTranslations } from "next-intl"; +import { announce } from "@/lib/announce"; import type { Editor } from "@tiptap/core"; import { TextSelection } from "@tiptap/pm/state"; import { ComposerProofreader, type DismissAction, type PlacedIssue, type ProofreaderStatus } from "@/lib/proofreading/composer-proofreader"; @@ -16,22 +18,39 @@ import { actionsFor, subscribeActions } from "@/lib/plugin-sandbox/actions"; import { all as allActivePlugins, subscribe as subscribeActivePlugins } from "@/lib/plugin-sandbox/registry"; import { replaceTextareaRange, type PlainOwnOptions } from "@/lib/plugin-sandbox/composer-edit"; import { usePolicyStore } from "@/stores/policy-store"; +import { usePluginStore } from "@/stores/plugin-store"; +import type { InstalledPlugin } from "@/lib/plugin-types"; import { useSettingsStore } from "@/stores/settings-store"; import { applyIssueReplacement, drawnIssues, issueNavigationKey, issueRect, selectNextIssue, setActiveIssue, setTextIssues, type IssueOpenVia, type TextCheckOptions, } from "./text-check-extension"; -/** The policy as the gate needs it, read now. */ +/** + * The policy as the gate needs it, read now. Settled only with a policy from + * the server (or the copy this browser kept of one): when the fetch failed + * and nothing was ever fetched, the built-in defaults could undo an admin's + * lock, so nothing is checked. + */ export function currentPolicyView(): PolicyView { const s = usePolicyStore.getState(); return { restrictions: s.policy.restrictions ?? {}, defaults: (s.policy.defaults ?? {}) as Record, - settled: s.loaded && !s.partial && !s.subjectPending, + settled: policySettled(s), }; } +export function policySettled(s: Pick, 'loaded' | 'partial' | 'subjectPending' | 'source'>): boolean { + return s.loaded && !s.partial && !s.subjectPending && s.source !== 'fallback' && s.source !== 'none'; +} + +/** Plugins turned on that aren't running yet (still loading). */ +function startingPlugins(running: InstalledPlugin[]): InstalledPlugin[] { + const up = new Set(running.map((p) => p.id)); + return usePluginStore.getState().plugins.filter((p) => p.enabled && p.status !== 'error' && p.status !== 'disabled' && !up.has(p.id)); +} + // The per-composer switch outlives a remount (pop-out, layout switch). const composerSwitches = new Map(); @@ -91,6 +110,9 @@ export function useProofreading({ composerId, sessionKey, editor, textareaRef, p composerOnRef.current = composerOn; const editorRef = useRef(editor); editorRef.current = editor; + const t = useTranslations("proofreading"); + const tRef = useRef(t); + tRef.current = t; const plainRef = useRef(plainTextMode); plainRef.current = plainTextMode; const hasCheckers = checkers.length > 0; @@ -108,7 +130,10 @@ export function useProofreading({ composerId, sessionKey, editor, textareaRef, p }, language: () => useSettingsStore.getState().writingLanguage, access: (pluginId) => checkerAccess(currentPolicyView(), useSettingsStore.getState(), pluginId), - protection: () => draftProtection(actionsFor(OPTIONS_SURFACE), allActivePlugins().map((e) => e.plugin)), + protection: () => { + const running = allActivePlugins().map((e) => e.plugin); + return draftProtection(actionsFor(OPTIONS_SURFACE), running, startingPlugins(running)); + }, composerOn: () => composerOnRef.current, send: (spec, request) => dispatchCheck(spec, request), action: (spec, payload) => dispatchCheckerAction(spec, payload), @@ -123,6 +148,7 @@ export function useProofreading({ composerId, sessionKey, editor, textareaRef, p subscribeCheckers(() => p.sync()), subscribeActions(() => p.gateChanged()), subscribeActivePlugins(() => p.gateChanged()), + usePluginStore.subscribe(() => p.gateChanged()), usePolicyStore.subscribe(() => p.gateChanged()), useSettingsStore.subscribe((s, prev) => { if (s.writingLanguage !== prev.writingLanguage) p.edited("now"); @@ -246,7 +272,10 @@ export function useProofreading({ composerId, sessionKey, editor, textareaRef, p /** Go to the next or previous issue: in the text (rich) or in the list (plain). */ const navigate = useCallback((dir: 1 | -1) => { const list = visibleRef.current; - if (!list.length) return false; + if (!list.length) { + if (composerOnRef.current) announce(tRef.current("panel.no_issues"), { key: "proofreading-count" }); + return false; + } const ed = editorRef.current; if (ed && !plainRef.current) { const key = selectNextIssue(ed, dir); @@ -309,8 +338,11 @@ export function useProofreading({ composerId, sessionKey, editor, textareaRef, p }, [plainTextMode, textareaRef, proofreader, navigate]); // An issue that went away (fixed, typed over) closes its popup. + // With the focus in it, the focus goes back to the text. useEffect(() => { - if (popup && !visibleIssues.some((i) => i.key === popup.key)) closePopup(false); + if (!popup || visibleIssues.some((i) => i.key === popup.key)) return; + const focusInside = !!(document.activeElement as Element | null)?.closest?.('[data-testid="proofreading-popup"]'); + closePopup(focusInside); }, [popup, visibleIssues, closePopup]); const apply = useCallback((issue: PlacedIssue, replacement: string): boolean => { diff --git a/components/settings/__tests__/proofreading-settings.test.tsx b/components/settings/__tests__/proofreading-settings.test.tsx index fdd2c76c5..a65b58fbc 100644 --- a/components/settings/__tests__/proofreading-settings.test.tsx +++ b/components/settings/__tests__/proofreading-settings.test.tsx @@ -14,7 +14,7 @@ vi.mock('../settings-section', async (importOriginal) => ({ })); const policy = (patch: Partial) => - usePolicyStore.setState({ policy: { ...DEFAULT_POLICY, ...patch }, loaded: true, partial: false, subjectPending: false }); + usePolicyStore.setState({ policy: { ...DEFAULT_POLICY, ...patch }, loaded: true, partial: false, subjectPending: false, source: 'server' }); const register = (pluginId: string, pluginName: string) => setPluginChecker(pluginId, pluginName, { id: 'check', label: pluginName, kinds: ['spelling'], debounceMs: 800, languages: ['de-DE'], mode: 'typing', warnBeforeSend: false, diff --git a/components/settings/proofreading-settings.tsx b/components/settings/proofreading-settings.tsx index ecb98d0c5..4132f63c0 100644 --- a/components/settings/proofreading-settings.tsx +++ b/components/settings/proofreading-settings.tsx @@ -12,6 +12,7 @@ import { usePolicyStore } from "@/stores/policy-store"; import { allCheckers, subscribeCheckers } from "@/lib/plugin-sandbox/checkers"; import { checkerAccess, proofreadingAccess, PROOFREADING_KEY, type PolicyView } from "@/lib/proofreading/gate"; import { writingLanguageOptions } from "@/lib/proofreading/languages"; +import { policySettled } from "@/components/email/proofreading/use-proofreading"; import { SettingsSection, SettingItem, Select, ToggleSwitch } from "./settings-section"; export function ProofreadingSettings() { @@ -23,7 +24,7 @@ export function ProofreadingSettings() { const writingLanguage = useSettingsStore((s) => s.writingLanguage); const updateSetting = useSettingsStore((s) => s.updateSetting); const policy = usePolicyStore((s) => s.policy); - const settled = usePolicyStore((s) => s.loaded && !s.partial && !s.subjectPending); + const settled = usePolicyStore(policySettled); const view: PolicyView = { restrictions: policy.restrictions ?? {}, defaults: (policy.defaults ?? {}) as Record, settled }; const user = { proofreading, proofreadingPlugins }; const main = proofreadingAccess(view, user); diff --git a/lib/__tests__/proofreading-proofreader.test.ts b/lib/__tests__/proofreading-proofreader.test.ts index d77d16372..88024b626 100644 --- a/lib/__tests__/proofreading-proofreader.test.ts +++ b/lib/__tests__/proofreading-proofreader.test.ts @@ -160,7 +160,7 @@ describe('ComposerProofreader: issues', () => { r.p.dismiss(first, 'disable-rule'); await settle(); expect(r.p.issues().map((i) => i.annotation.text)).toEqual(['dem']); - expect(r.actions).toEqual([{ composerId: 'c1', action: 'disable-rule', annotation: expect.objectContaining({ text: 'Ich', ruleId: 'R1' }) }]); + expect(r.actions).toEqual([{ composerId: expect.stringMatching(/^chk-/), action: 'disable-rule', annotation: expect.objectContaining({ text: 'Ich', ruleId: 'R1' }) }]); }); it('turns the browser spell check off only while a spelling checker works here', async () => { diff --git a/lib/__tests__/proofreading-review-fixes.test.ts b/lib/__tests__/proofreading-review-fixes.test.ts new file mode 100644 index 000000000..2b583eadf --- /dev/null +++ b/lib/__tests__/proofreading-review-fixes.test.ts @@ -0,0 +1,94 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { validateAnnotations } from '@/lib/proofreading/annotations'; +import { ComposerProofreader, OPEN_GRACE_MS } from '@/lib/proofreading/composer-proofreader'; +import { draftProtection } from '@/lib/proofreading/protection'; +import { issueNavigationKey } from '@/components/email/proofreading/text-check-extension'; +import type { CheckRequest } from '@/lib/proofreading/check-session'; +import type { CheckerSpec } from '@/lib/plugin-sandbox/checkers'; +import type { InstalledPlugin } from '@/lib/plugin-types'; + +// What the security and accessibility reviews of #1397 found, each fixed. + +const SEGS = new Map([['s1', 'Ich habe dem Brief geschrieben.']]); +const ann = (replacements: string[], message = 'Use den') => ({ segmentId: 's1', offset: 9, length: 3, kind: 'grammar', message, replacements }); + +describe('what a checker may put into the message', () => { + it('only text a user could type: no zero-width, bidi, line or paragraph separators, no C1 controls', () => { + const bad = ['de\u200Bn', 'd\u202Een', 'den\u2028', 'den\u2029', 'den\u0085', 'de\uFEFFn', 'den\u2066']; + const [a] = validateAnnotations([ann([...bad, 'den'])], SEGS, ['grammar']).annotations; + expect(a.replacements).toEqual(['den']); + }); + + it('shows no bidi overrides in the message', () => { + const [a] = validateAnnotations([ann(['den'], 'Use \u202Enot\u202C den')], SEGS, ['grammar']).annotations; + expect(a.message).toBe('Use not den'); + }); + + it('never cuts a long message in the middle of a character', () => { + const [a] = validateAnnotations([ann(['den'], `${'x'.repeat(498)}😀😀`)], SEGS, ['grammar']).annotations; + expect(a.message.endsWith('…')).toBe(true); + const beforeEllipsis = a.message.charCodeAt(a.message.length - 2); + expect(beforeEllipsis >= 0xd800 && beforeEllipsis <= 0xdbff).toBe(false); + }); + + it('stops looking at a flood of annotations once it has enough', () => { + const many = Array.from({ length: 5000 }, () => ann(['den'])); + const { annotations, dropped } = validateAnnotations(many, SEGS, ['grammar'], 10); + expect(annotations).toHaveLength(10); + expect(dropped).toBe(4990); + }); +}); + +describe('a checker never learns the composer id', () => { + beforeEach(() => { vi.useFakeTimers(); }); + afterEach(() => { vi.useRealTimers(); }); + + it('gets a name for the composer of its own, the same in requests and actions', async () => { + const sent: CheckRequest[] = []; + const actions: { composerId: string }[] = []; + const spec: CheckerSpec = { + pluginId: 'lt', pluginName: 'LanguageTool', id: 'c', label: 'Checker', kinds: ['grammar'], debounceMs: 300, + languages: [], mode: 'typing', warnBeforeSend: false, epoch: 1, order: 0, + }; + const p = new ComposerProofreader({ + composerId: 'real-composer-id', checkers: () => [spec], + readSegments: () => [{ id: 's1', text: 'Ich habe dem Brief geschrieben.', start: 1 }], language: () => 'auto', + access: () => ({ on: true, forced: false, lockedOff: false, hidden: false }), + protection: () => ({ protected: false }), composerOn: () => true, + send: async (_s, r) => { sent.push(r); return [ann(['den'])]; }, + action: async (_s, payload) => { actions.push(payload as { composerId: string }); }, + onChange: () => {}, openedAt: Date.now() - OPEN_GRACE_MS, + }); + await vi.advanceTimersByTimeAsync(10); + p.dismiss(p.issues()[0], 'ignore'); + expect(sent[0].composerId).not.toBe('real-composer-id'); + expect(sent[0].composerId).toMatch(/^chk-/); + expect(actions[0].composerId).toBe(sent[0].composerId); + // Its notices name the plugin as installed, not the label it chose. + expect(p.status().activePlugins).toEqual([{ pluginId: 'lt', label: 'LanguageTool' }]); + p.dispose(); + }); +}); + +describe('protection while plugins start', () => { + const plugin = (id: string, permissions: string[]) => ({ id, name: id, permissions } as unknown as InstalledPlugin); + it('pauses while a plugin that can sign is turned on but not running yet', () => { + expect(draftProtection([], [], [plugin('smime', ['crypto:full'])])).toMatchObject({ protected: true, reason: 'unknown' }); + expect(draftProtection([], [], [plugin('gravatar', ['http:fetch'])])).toEqual({ protected: false }); + }); +}); + +describe('next and previous issue keys', () => { + const key = (init: KeyboardEventInit) => new KeyboardEvent('keydown', { ctrlKey: true, ...init }); + it('follow the character, so other layouts keep their own Ctrl keys', () => { + expect(issueNavigationKey(key({ key: "'", code: 'Quote' }))).toBe(1); + expect(issueNavigationKey(key({ key: ';', code: 'Semicolon' }))).toBe(-1); + // Dvorak: the ;-position types "s", the '-position "-": Ctrl+S and Ctrl+- stay the browser's. + expect(issueNavigationKey(key({ key: 's', code: 'Semicolon' }))).toBeNull(); + expect(issueNavigationKey(key({ key: '-', code: 'Quote' }))).toBeNull(); + // German: Ä and Ö, where the US keyboard has ' and ;. + expect(issueNavigationKey(key({ key: 'ä', code: 'Quote' }))).toBe(1); + expect(issueNavigationKey(key({ key: 'ö', code: 'Semicolon' }))).toBe(-1); + expect(issueNavigationKey(new KeyboardEvent('keydown', { key: "'" }))).toBeNull(); + }); +}); diff --git a/lib/plugin-sandbox/host-api-checker.ts b/lib/plugin-sandbox/host-api-checker.ts index ae2bc49cf..9143b3d49 100644 --- a/lib/plugin-sandbox/host-api-checker.ts +++ b/lib/plugin-sandbox/host-api-checker.ts @@ -19,9 +19,11 @@ import { removePluginChecker, setPluginChecker, updatePluginChecker, type CheckerSpec, } from './checkers'; import { readId, withinRate } from './host-api-input'; +import { MAX_ANNOTATIONS } from '@/lib/proofreading/annotations'; import { pluginHasPermission } from './permissions'; const CALLS_PER_MINUTE = 60; +const MAX_PUSHED = 2 * MAX_ANNOTATIONS; function rate(method: string, plugin: InstalledPlugin): void { if (!withinRate('composer-check', plugin.id, CALLS_PER_MINUTE)) throw new Error(`${method}: at most ${CALLS_PER_MINUTE} calls a minute`); @@ -91,6 +93,7 @@ export function doSetAnnotations(plugin: InstalledPlugin, composerId: unknown, c const spec = checkerOf(method, plugin, checkerId); const rev = readId(revision, method, 'revision'); if (!Array.isArray(annotations)) throw new Error(`${method}: annotations must be a list`); + if (annotations.length > MAX_PUSHED) throw new Error(`${method}: at most ${MAX_PUSHED} annotations`); const target = checkTarget(id); if (!target) throw new Error(`${method}: that composer is not open any more`); rate(method, plugin); diff --git a/lib/plugin-sandbox/loader.ts b/lib/plugin-sandbox/loader.ts index 2b546e97f..eaaceff4b 100644 --- a/lib/plugin-sandbox/loader.ts +++ b/lib/plugin-sandbox/loader.ts @@ -20,7 +20,7 @@ import { verifyBundle } from './bundle-integrity'; import { downloadManagedBundle } from './bundle-fetch'; import { createBackgroundInstance, forEachInstance, snapshotRegionalSettings, type InitDoneInfo, type RegionalSnapshot } from './host-bridge'; import { resolvePluginTier } from './tier'; -import { register as registerActive, deregister as deregisterActive, all as allActiveEntries, get as getActive, type SlotOffer } from './registry'; +import { register as registerActive, deregister as deregisterActive, all as allActiveEntries, get as getActive, subscribe as subscribeActive, type SlotOffer } from './registry'; import { cancelPluginDialogs } from './host-api'; import { hasDialogFor, whenNoDialogFor } from './host-dialog'; import { registerShortcuts } from './shortcuts'; @@ -379,15 +379,29 @@ setActionDispatcher(async (pluginId, surface, actionId, context) => { // A slow or failing checker is not a broken plugin: its server may be down. // Its errors stay with the composer that asked (shown as "checking // unavailable") and don't count towards turning the plugin off. -function backgroundOf(pluginId: string) { - const entry = getActive(pluginId); +// A plugin registers its checker in activate(), a moment before the loader +// counts it as running: wait for that rather than fail the first check. +async function backgroundOf(pluginId: string) { + let entry = getActive(pluginId); + if (!entry) { + entry = await new Promise>((resolve) => { + const timer = setTimeout(() => { unsubscribe(); resolve(getActive(pluginId)); }, 10_000); + const unsubscribe = subscribeActive(() => { + const found = getActive(pluginId); + if (!found) return; + clearTimeout(timer); + unsubscribe(); + resolve(found); + }); + }); + } if (!entry) throw new Error('plugin is not running'); return entry.background; } setCheckerDispatcher({ - check: (pluginId, checkerId, request) => backgroundOf(pluginId).invokeHook(`checker:${checkerId}`, [request]), - action: (pluginId, checkerId, payload) => backgroundOf(pluginId).invokeHook(`checker-action:${checkerId}`, [payload]), + check: async (pluginId, checkerId, request) => (await backgroundOf(pluginId)).invokeHook(`checker:${checkerId}`, [request]), + action: async (pluginId, checkerId, payload) => (await backgroundOf(pluginId)).invokeHook(`checker-action:${checkerId}`, [payload]), }); // ─── Unload ─────────────────────────────────────────────────── diff --git a/lib/plugin-sandbox/runtime.tsx b/lib/plugin-sandbox/runtime.tsx index fdf6dc883..73ff16600 100644 --- a/lib/plugin-sandbox/runtime.tsx +++ b/lib/plugin-sandbox/runtime.tsx @@ -726,10 +726,10 @@ function buildPluginApi(manifest: PluginManifest) { }, }; }, - /** The whole text of a composer the plugin was handed, request-shaped, for an on-demand check. Needs compose:check. */ + /** The whole text of a composer the plugin was handed, request-shaped, for an on-demand check. Needs compose:check, or compose:read for an on-demand checker. */ getCheckText: (composerId: string, checkerId: string) => callApi('composer.getCheckText', [composerId, checkerId]) as Promise, - /** Results for a request from getCheckText; resolves to how many were kept. Needs compose:check. */ + /** Results for a request from getCheckText; resolves to how many were kept. Needs compose:check, or compose:read for an on-demand checker. */ setAnnotations: (composerId: string, checkerId: string, revision: string, annotations: CheckAnnotation[]) => callApi('composer.setAnnotations', [composerId, checkerId, revision, annotations]) as Promise, }, diff --git a/lib/proofreading/annotations.ts b/lib/proofreading/annotations.ts index 02cd67b25..9e84338c3 100644 --- a/lib/proofreading/annotations.ts +++ b/lib/proofreading/annotations.ts @@ -36,15 +36,32 @@ export interface Annotation { text: string; } -/** The text without control characters other than tab and line breaks. */ +const isHigh = (c: number) => c >= 0xd800 && c <= 0xdbff; +const isLow = (c: number) => c >= 0xdc00 && c <= 0xdfff; + +/** + * Text shown in the popup: no control characters, and no bidi overrides, + * which could make a message read differently from what it says. + */ function stripControls(s: string): string { - let out = ''; - for (const ch of s) { - const c = ch.charCodeAt(0); - if ((c < 32 && c !== 9 && c !== 10 && c !== 13) || c === 127) continue; - out += ch; - } - return out; + return s.replace(/[\p{Cc}\p{Bidi_Control}]/gu, (ch) => (ch === '\t' || ch === '\n' ? ch : '')); +} + +/** + * Text that goes into the message: what a user could type. No control or + * format characters (zero-width, bidi, a watermark nobody sees), no line or + * paragraph separators, no object placeholder. + */ +function insertable(s: string): boolean { + return !/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(s) && !s.includes(OBJECT_CHAR); +} + +/** The first `max` characters, never half a character. */ +function cut(s: string, max: number): string { + if (s.length <= max) return s; + let end = max - 1; + if (isHigh(s.charCodeAt(end - 1))) end--; + return `${s.slice(0, end)}…`; } const isKind = (v: unknown): v is CheckKind => typeof v === 'string' && (CHECK_KINDS as readonly string[]).includes(v); @@ -53,11 +70,9 @@ function str(v: unknown, max: number): string | undefined { if (typeof v !== 'string') return undefined; const s = stripControls(v).trim(); if (!s) return undefined; - return s.length > max ? `${s.slice(0, max - 1)}…` : s; + return cut(s, max); } -const isHigh = (c: number) => c >= 0xd800 && c <= 0xdbff; -const isLow = (c: number) => c >= 0xdc00 && c <= 0xdfff; /** * The annotations of a checker's answer that may be drawn. `segments` maps @@ -74,7 +89,7 @@ export function validateAnnotations( let dropped = 0; if (!Array.isArray(raw)) return { annotations: out, dropped }; for (const item of raw) { - if (out.length >= max) { dropped++; continue; } + if (out.length >= max) { dropped = raw.length - out.length; break; } const a = item as Record | null; if (!a || typeof a !== 'object') { dropped++; continue; } const text = typeof a.segmentId === 'string' ? segments.get(a.segmentId) : undefined; @@ -93,8 +108,8 @@ export function validateAnnotations( if (Array.isArray(a.replacements)) { for (const r of a.replacements) { if (replacements.length >= MAX_REPLACEMENTS) break; - // A replacement is put in the text as typed: no line breaks, no objects. - if (typeof r !== 'string' || r.length > MAX_REPLACEMENT || stripControls(r) !== r || /[\n\r]/.test(r) || r.includes(OBJECT_CHAR)) continue; + // A replacement is put in the text as typed. + if (typeof r !== 'string' || r.length > MAX_REPLACEMENT || !insertable(r)) continue; if (r === covered || replacements.includes(r)) continue; replacements.push(r); } diff --git a/lib/proofreading/composer-proofreader.ts b/lib/proofreading/composer-proofreader.ts index 8612ff261..048e0539f 100644 --- a/lib/proofreading/composer-proofreader.ts +++ b/lib/proofreading/composer-proofreader.ts @@ -21,6 +21,10 @@ import type { CheckerSpec } from '@/lib/plugin-sandbox/checkers'; export const OPEN_GRACE_MS = 1000; +function newToken(): string { + return `chk-${typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2)}`; +} + export interface PlacedIssue { key: string; checkerKey: string; @@ -57,7 +61,11 @@ export interface ProofreaderStatus { count: number; /** Labels of the checkers that are on, for the notice. */ activeLabels: string[]; - /** The checkers that are on, by plugin. */ + /** + * The checkers that are on, by plugin, with the plugin's name from its + * manifest (the one shown at install), not a label the plugin can change: + * the notices say where the text goes. + */ activePlugins: { pluginId: string; label: string }[]; /** The checkers the user could turn on (not locked off). */ availablePlugins: { pluginId: string; label: string }[]; @@ -91,7 +99,7 @@ interface Dismissal { export class ComposerProofreader { private readonly deps: ProofreaderDeps; - private readonly sessions = new Map(); + private readonly sessions = new Map(); private readonly dismissed: Dismissal[] = []; private readonly wasAllowed = new Map(); private readonly openedAt: number; @@ -130,9 +138,12 @@ export class ComposerProofreader { if (recheck && spec.mode === 'typing' && this.allowed(spec)) entry.session.schedule('now'); continue; } - const holder = { spec, session: null as unknown as CheckSession }; + // The checker gets a name for this composer of its own, never the + // composer's id: with that id a plugin could reach the composer through + // the other api.composer calls, past proofreading's rules. + const holder = { spec, session: null as unknown as CheckSession, token: newToken() }; holder.session = new CheckSession({ - composerId: this.deps.composerId, + composerId: holder.token, spec: () => holder.spec, readSegments: this.deps.readSegments, language: this.deps.language, @@ -242,7 +253,7 @@ export class ComposerProofreader { this.dismissed.push({ checkerKey: issue.checkerKey, action, segmentId: a.segmentId, offset: a.offset, text: a.text, ruleId: a.ruleId }); this.deps.onChange(); const { text, ...rest } = a; - void this.deps.action(entry.spec, { composerId: this.deps.composerId, annotation: { ...rest, text }, action }).catch(() => {}); + void this.deps.action(entry.spec, { composerId: entry.token, annotation: { ...rest, text }, action }).catch(() => {}); } status(): ProofreaderStatus { @@ -262,14 +273,14 @@ export class ComposerProofreader { if (!access.lockedOff) lockedOff = false; if (access.lockedOff) continue; available = true; - availablePlugins.push({ pluginId: spec.pluginId, label: spec.label }); + if (!availablePlugins.some((x) => x.pluginId === spec.pluginId)) availablePlugins.push({ pluginId: spec.pluginId, label: spec.pluginName }); if (access.forced) forced = true; if (access.on) userOn = true; const isOn = access.on && (access.forced || this.deps.composerOn()); if (!isOn) continue; on = true; activeLabels.push(spec.label); - activePlugins.push({ pluginId: spec.pluginId, label: spec.label }); + if (!activePlugins.some((x) => x.pluginId === spec.pluginId)) activePlugins.push({ pluginId: spec.pluginId, label: spec.pluginName }); if (protection.protected) continue; if (session.status === 'checking') checking = true; if ((session.status === 'unavailable' || session.status === 'paused') && !trouble) { diff --git a/lib/proofreading/protection.ts b/lib/proofreading/protection.ts index 5cc675d7e..94c1eddb0 100644 --- a/lib/proofreading/protection.ts +++ b/lib/proofreading/protection.ts @@ -14,6 +14,9 @@ // // The plugins' choice isn't per composer (S/MIME keeps one intent for the // next message), so one answer covers every open composer. +// +// A crypto:full plugin that is turned on but still starting can't have said +// anything yet (S/MIME may encrypt by default): it pauses checking too. import type { RegisteredAction } from '@/lib/plugin-sandbox/actions'; import type { InstalledPlugin } from '@/lib/plugin-types'; @@ -32,9 +35,16 @@ function canSign(plugin: InstalledPlugin): boolean { /** * `options`: the actions on the composer's Options menu. `running`: the - * plugins currently loaded. + * plugins currently loaded. `starting`: plugins turned on that aren't + * running yet. */ -export function draftProtection(options: readonly RegisteredAction[], running: readonly InstalledPlugin[]): Protection { +export function draftProtection( + options: readonly RegisteredAction[], + running: readonly InstalledPlugin[], + starting: readonly InstalledPlugin[] = [], +): Protection { + const loading = starting.find(canSign); + if (loading) return { protected: true, reason: 'unknown', pluginName: loading.name }; const signers = new Map(running.filter(canSign).map((p) => [p.id, p])); const speaks = new Set(); for (const a of options) { diff --git a/locales/ar/common.json b/locales/ar/common.json index 67bdcf7b9..0467c640f 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "تدقيق ما تكتبه", - "body": "يقرأ ما تكتبه في محرر الرسائل أثناء الكتابة وقد يرسله إلى الخدمة التي يستخدمها. فقط عندما يكون التدقيق مفعّلًا، ولا يحدث أبدًا لرسالة توقّعها أو تشفّرها." + "body": "يقرأ ما تكتبه في محرر الرسائل أثناء الكتابة وقد يرسله إلى الخدمة التي يستخدمها. فقط عندما يكون التدقيق مفعّلًا، ولا شيء ما دامت الرسالة مضبوطة للتوقيع أو التشفير. وقد يكون النص الذي كتبته قبل تفعيل ذلك قد دُقِّق بالفعل." }, "email_forward": { "title": "إعادة توجيه بريدك", @@ -6394,7 +6394,7 @@ "title": "التدقيق", "required": "تشترطه مؤسستك", "check_message": "تدقيق هذه الرسالة", - "notice": "أثناء الكتابة، يُرسَل النص الذي تكتبه إلى {checkers} لتدقيقه. يُستثنى توقيعك والرسالة المقتبسة، ولا يُرسَل أي شيء لرسالة توقّعها أو تشفّرها.", + "notice": "أثناء الكتابة، يُرسَل النص الذي تكتبه إلى {checkers} لتدقيقه. يُستثنى توقيعك والرسالة المقتبسة. لا يُرسَل أي شيء ما دامت الرسالة مضبوطة للتوقيع أو التشفير؛ وقد يكون ما كتبته قبل تفعيل ذلك قد دُقِّق بالفعل.", "turn_on": "تفعيل التدقيق", "paused_protected": "لا يُدقَّق أي شيء ما دامت هذه الرسالة مضبوطة للتوقيع أو التشفير.", "paused_unknown": "يمكن لـ {plugin} توقيع الرسائل أو تشفيرها دون أن يظهر ذلك هنا، لذا لا يُدقَّق أي شيء ما دام مفعّلًا.", @@ -6405,8 +6405,8 @@ "shortcuts": "المشكلة التالية: {next}. السابقة: {previous}." }, "notice": { - "on": "التدقيق مفعّل. أثناء الكتابة، يُرسَل النص الذي تكتبه إلى {checkers} لتدقيقه، دون توقيعك أو الرسالة المقتبسة. لا يُرسَل أي شيء لرسالة توقّعها أو تشفّرها.", - "forced": "فعّلت مؤسستك التدقيق. أثناء الكتابة، يُرسَل النص الذي تكتبه إلى {checkers} لتدقيقه، دون توقيعك أو الرسالة المقتبسة. لا يُرسَل أي شيء لرسالة توقّعها أو تشفّرها.", + "on": "التدقيق مفعّل. أثناء الكتابة، يُرسَل النص الذي تكتبه إلى {checkers} لتدقيقه، دون توقيعك أو الرسالة المقتبسة. لا يُرسَل أي شيء ما دامت الرسالة مضبوطة للتوقيع أو التشفير؛ وقد يكون ما كتبته قبل ذلك قد دُقِّق بالفعل.", + "forced": "فعّلت مؤسستك التدقيق. أثناء الكتابة، يُرسَل النص الذي تكتبه إلى {checkers} لتدقيقه، دون توقيعك أو الرسالة المقتبسة. لا يُرسَل أي شيء ما دامت الرسالة مضبوطة للتوقيع أو التشفير؛ وقد يكون ما كتبته قبل ذلك قد دُقِّق بالفعل.", "ok": "حسنًا" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "التدقيق", "description": "وضع خط تحت الأخطاء الإملائية والنحوية أثناء الكتابة، مع اقتراحات لتصحيحها.", "enable": "تدقيق الإملاء والقواعد أثناء الكتابة", - "enable_description": "يُرسَل النص الذي تكتبه إلى إضافة التدقيق والخدمة التي تستخدمها. يُستثنى توقيعك والرسائل المقتبسة، ولا تُدقَّق الرسائل الموقّعة أو المشفّرة أبدًا.", + "enable_description": "يُرسَل النص الذي تكتبه إلى إضافة التدقيق والخدمة التي تستخدمها، دون توقيعك أو الرسائل المقتبسة. لا يُرسَل أي شيء ما دامت الرسالة مضبوطة للتوقيع أو التشفير.", "no_checker": "لا توجد إضافة تدقيق مثبّتة. اطلب من المسؤول إضافة واحدة.", "plugin_description": "يرسل ما تكتبه إلى {plugin}.", "writing_language": "لغة الكتابة", diff --git a/locales/ca/common.json b/locales/ca/common.json index a267398e0..e4436172f 100644 --- a/locales/ca/common.json +++ b/locales/ca/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Revisar el que escriviu", - "body": "Llegeix el que escriviu a l'editor mentre teclegeu i ho pot enviar al servei que fa servir. Només quan la revisió està activada, i mai per a un missatge que signeu o xifreu." + "body": "Llegeix el que escriviu a l'editor mentre teclegeu i ho pot enviar al servei que fa servir. Només quan la revisió està activada, i res mentre un missatge estigui configurat per signar-se o xifrar-se. El text que heu escrit abans d'activar-ho potser ja s'ha revisat." }, "email_forward": { "title": "Reenviar el vostre correu", @@ -6394,7 +6394,7 @@ "title": "Revisió", "required": "Exigit per la vostra organització", "check_message": "Revisa aquest missatge", - "notice": "Mentre escriviu, el text s'envia a {checkers} perquè el revisi. La vostra signatura i el missatge citat en queden fora, i no s'envia res d'un missatge que signeu o xifreu.", + "notice": "Mentre escriviu, el text s'envia a {checkers} perquè el revisi. La vostra signatura i el missatge citat en queden fora. No s'envia res mentre un missatge estigui configurat per signar-se o xifrar-se; el que heu escrit abans d'activar-ho potser ja s'ha revisat.", "turn_on": "Activa la revisió", "paused_protected": "No es revisa res mentre aquest missatge estigui configurat per signar-se o xifrar-se.", "paused_unknown": "{plugin} pot signar o xifrar missatges sense mostrar-ho aquí, així que no es revisa res mentre estigui activat.", @@ -6405,8 +6405,8 @@ "shortcuts": "Problema següent: {next}. Anterior: {previous}." }, "notice": { - "on": "La revisió està activada. Mentre escriviu, el text s'envia a {checkers} perquè el revisi, sense la vostra signatura ni el missatge citat. No s'envia res d'un missatge que signeu o xifreu.", - "forced": "La vostra organització ha activat la revisió. Mentre escriviu, el text s'envia a {checkers} perquè el revisi, sense la vostra signatura ni el missatge citat. No s'envia res d'un missatge que signeu o xifreu.", + "on": "La revisió està activada. Mentre escriviu, el text s'envia a {checkers} perquè el revisi, sense la vostra signatura ni el missatge citat. No s'envia res mentre un missatge estigui configurat per signar-se o xifrar-se; el que heu escrit abans potser ja s'ha revisat.", + "forced": "La vostra organització ha activat la revisió. Mentre escriviu, el text s'envia a {checkers} perquè el revisi, sense la vostra signatura ni el missatge citat. No s'envia res mentre un missatge estigui configurat per signar-se o xifrar-se; el que heu escrit abans potser ja s'ha revisat.", "ok": "D'acord" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Revisió", "description": "Subratlla els errors d'ortografia i de gramàtica mentre escriviu, amb suggeriments per corregir-los.", "enable": "Revisa l'ortografia i la gramàtica mentre escric", - "enable_description": "El text que escriviu s'envia al complement de revisió i al servei que fa servir. La vostra signatura i els missatges citats en queden fora, i els missatges signats o xifrats no es revisen mai.", + "enable_description": "El text que escriviu s'envia al complement de revisió i al servei que fa servir, sense la vostra signatura ni els missatges citats. No s'envia res mentre un missatge estigui configurat per signar-se o xifrar-se.", "no_checker": "No hi ha cap complement de revisió instal·lat. Demaneu a l'administrador que n'afegeixi un.", "plugin_description": "Envia el que escriviu a {plugin}.", "writing_language": "Llengua d'escriptura", diff --git a/locales/cs/common.json b/locales/cs/common.json index 3bedbdf30..9e330cab1 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Kontrolovat, co píšete", - "body": "Čte, co píšete v editoru, během psaní a může to posílat službě, kterou používá. Jen když je kontrola zapnutá, a nikdy u zprávy, kterou podepisujete nebo šifrujete." + "body": "Čte, co píšete v editoru, během psaní a může to posílat službě, kterou používá. Jen když je kontrola zapnutá, a nic, dokud je zpráva nastavená k podepsání nebo zašifrování. Text, který jste napsali, než jste to zapnuli, už mohl být zkontrolován." }, "email_forward": { "title": "Přeposílat vaši poštu", @@ -6394,7 +6394,7 @@ "title": "Kontrola textu", "required": "Vyžaduje vaše organizace", "check_message": "Zkontrolovat tuto zprávu", - "notice": "Během psaní se váš text posílá ke kontrole službě {checkers}. Váš podpis a citovaná zpráva se vynechávají a u zprávy, kterou podepisujete nebo šifrujete, se neposílá nic.", + "notice": "Během psaní se váš text posílá ke kontrole službě {checkers}. Váš podpis a citovaná zpráva se vynechávají. Dokud je zpráva nastavená k podepsání nebo zašifrování, neposílá se nic; co jste napsali předtím, než jste to zapnuli, už mohlo být zkontrolováno.", "turn_on": "Zapnout kontrolu textu", "paused_protected": "Dokud je tato zpráva nastavená k podepsání nebo zašifrování, nic se nekontroluje.", "paused_unknown": "{plugin} může podepisovat nebo šifrovat zprávy, aniž by se to zde zobrazilo, proto se nic nekontroluje, dokud je zapnutý.", @@ -6405,8 +6405,8 @@ "shortcuts": "Další problém: {next}. Předchozí: {previous}." }, "notice": { - "on": "Kontrola textu je zapnutá. Během psaní se váš text posílá ke kontrole službě {checkers}, bez vašeho podpisu a citované zprávy. U zprávy, kterou podepisujete nebo šifrujete, se neposílá nic.", - "forced": "Vaše organizace zapnula kontrolu textu. Během psaní se váš text posílá ke kontrole službě {checkers}, bez vašeho podpisu a citované zprávy. U zprávy, kterou podepisujete nebo šifrujete, se neposílá nic.", + "on": "Kontrola textu je zapnutá. Během psaní se váš text posílá ke kontrole službě {checkers}, bez vašeho podpisu a citované zprávy. Dokud je zpráva nastavená k podepsání nebo zašifrování, neposílá se nic; co jste napsali předtím, už mohlo být zkontrolováno.", + "forced": "Vaše organizace zapnula kontrolu textu. Během psaní se váš text posílá ke kontrole službě {checkers}, bez vašeho podpisu a citované zprávy. Dokud je zpráva nastavená k podepsání nebo zašifrování, neposílá se nic; co jste napsali předtím, už mohlo být zkontrolováno.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Kontrola textu", "description": "Podtrhává pravopisné a gramatické chyby během psaní a nabízí návrhy na opravu.", "enable": "Kontrolovat pravopis a gramatiku během psaní", - "enable_description": "Text, který píšete, se posílá doplňku pro kontrolu textu a službě, kterou používá. Váš podpis a citované zprávy se vynechávají a podepsané nebo šifrované zprávy se nikdy nekontrolují.", + "enable_description": "Text, který píšete, se posílá doplňku pro kontrolu textu a službě, kterou používá, bez vašeho podpisu a citovaných zpráv. Dokud je zpráva nastavená k podepsání nebo zašifrování, neposílá se nic.", "no_checker": "Není nainstalovaný žádný doplněk pro kontrolu textu. Požádejte správce, aby nějaký přidal.", "plugin_description": "Posílá, co píšete, do {plugin}.", "writing_language": "Jazyk psaní", diff --git a/locales/da/common.json b/locales/da/common.json index ec83555c0..6150cfa41 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Tjekke det, du skriver", - "body": "Læser det, du skriver i editoren, mens du skriver, og kan sende det til den tjeneste, den bruger. Kun når korrekturen er slået til, og aldrig for en besked, du signerer eller krypterer." + "body": "Læser det, du skriver i editoren, mens du skriver, og kan sende det til den tjeneste, den bruger. Kun når korrekturen er slået til, og intet, så længe en besked er sat til at blive signeret eller krypteret. Tekst, du skrev, før du slog det til, kan allerede være tjekket." }, "email_forward": { "title": "Videresende din post", @@ -6394,7 +6394,7 @@ "title": "Korrektur", "required": "Krævet af din organisation", "check_message": "Tjek denne besked", - "notice": "Mens du skriver, sendes teksten til {checkers} for at blive tjekket. Din signatur og den citerede besked udelades, og der sendes intet for en besked, du signerer eller krypterer.", + "notice": "Mens du skriver, sendes teksten til {checkers} for at blive tjekket. Din signatur og den citerede besked udelades. Der sendes intet, så længe en besked er sat til at blive signeret eller krypteret; det, du skrev, før du slog det til, kan allerede være tjekket.", "turn_on": "Slå korrektur til", "paused_protected": "Intet bliver tjekket, så længe denne besked er sat til at blive signeret eller krypteret.", "paused_unknown": "{plugin} kan signere eller kryptere beskeder uden at vise det her, så intet bliver tjekket, mens den er slået til.", @@ -6405,8 +6405,8 @@ "shortcuts": "Næste problem: {next}. Forrige: {previous}." }, "notice": { - "on": "Korrektur er slået til. Mens du skriver, sendes teksten til {checkers} for at blive tjekket, uden din signatur og den citerede besked. Der sendes intet for en besked, du signerer eller krypterer.", - "forced": "Din organisation har slået korrektur til. Mens du skriver, sendes teksten til {checkers} for at blive tjekket, uden din signatur og den citerede besked. Der sendes intet for en besked, du signerer eller krypterer.", + "on": "Korrektur er slået til. Mens du skriver, sendes teksten til {checkers} for at blive tjekket, uden din signatur og den citerede besked. Der sendes intet, så længe en besked er sat til at blive signeret eller krypteret; det, du skrev før, kan allerede være tjekket.", + "forced": "Din organisation har slået korrektur til. Mens du skriver, sendes teksten til {checkers} for at blive tjekket, uden din signatur og den citerede besked. Der sendes intet, så længe en besked er sat til at blive signeret eller krypteret; det, du skrev før, kan allerede være tjekket.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Korrektur", "description": "Understreg stave- og grammatikfejl, mens du skriver, med forslag til at rette dem.", "enable": "Tjek stavning og grammatik, mens jeg skriver", - "enable_description": "Teksten, du skriver, sendes til korrekturtilføjelsen og den tjeneste, den bruger. Din signatur og citerede beskeder udelades, og signerede eller krypterede beskeder bliver aldrig tjekket.", + "enable_description": "Teksten, du skriver, sendes til korrekturtilføjelsen og den tjeneste, den bruger, uden din signatur og citerede beskeder. Der sendes intet, så længe en besked er sat til at blive signeret eller krypteret.", "no_checker": "Der er ingen korrekturtilføjelse installeret. Bed din administrator om at tilføje en.", "plugin_description": "Sender det, du skriver, til {plugin}.", "writing_language": "Skrivesprog", diff --git a/locales/de/common.json b/locales/de/common.json index 36a952851..d325e7ff7 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Prüfen, was Sie schreiben", - "body": "Liest, was Sie im Editor schreiben, während Sie tippen, und kann es an den Dienst senden, den es nutzt. Nur wenn die Korrekturhilfe an ist, und nie bei einer Nachricht, die Sie signieren oder verschlüsseln." + "body": "Liest, was Sie im Editor schreiben, während Sie tippen, und kann es an den Dienst senden, den es nutzt. Nur wenn die Korrekturhilfe an ist, und nichts, solange eine Nachricht signiert oder verschlüsselt werden soll. Text, den Sie geschrieben haben, bevor Sie das eingeschaltet haben, wurde möglicherweise schon geprüft." }, "email_forward": { "title": "E-Mails weiterleiten", @@ -6394,7 +6394,7 @@ "title": "Korrekturhilfe", "required": "Von Ihrer Organisation vorgeschrieben", "check_message": "Diese Nachricht prüfen", - "notice": "Während Sie tippen, geht Ihr Text zur Prüfung an {checkers}. Ihre Signatur und die zitierte Nachricht bleiben außen vor, und bei einer Nachricht, die Sie signieren oder verschlüsseln, wird nichts gesendet.", + "notice": "Während Sie tippen, geht Ihr Text zur Prüfung an {checkers}. Ihre Signatur und die zitierte Nachricht bleiben außen vor. Solange eine Nachricht signiert oder verschlüsselt werden soll, wird nichts gesendet. Was Sie geschrieben haben, bevor Sie das eingeschaltet haben, wurde möglicherweise schon geprüft.", "turn_on": "Korrekturhilfe einschalten", "paused_protected": "Solange diese Nachricht signiert oder verschlüsselt werden soll, wird nichts geprüft.", "paused_unknown": "{plugin} kann Nachrichten signieren oder verschlüsseln, ohne dass es hier angezeigt wird. Solange es aktiv ist, wird deshalb nichts geprüft.", @@ -6405,8 +6405,8 @@ "shortcuts": "Nächster Hinweis: {next}. Vorheriger: {previous}." }, "notice": { - "on": "Die Korrekturhilfe ist an. Während Sie tippen, geht Ihr Text zur Prüfung an {checkers}, ohne Ihre Signatur und die zitierte Nachricht. Bei einer Nachricht, die Sie signieren oder verschlüsseln, wird nichts gesendet.", - "forced": "Ihre Organisation hat die Korrekturhilfe eingeschaltet. Während Sie tippen, geht Ihr Text zur Prüfung an {checkers}, ohne Ihre Signatur und die zitierte Nachricht. Bei einer Nachricht, die Sie signieren oder verschlüsseln, wird nichts gesendet.", + "on": "Die Korrekturhilfe ist an. Während Sie tippen, geht Ihr Text zur Prüfung an {checkers}, ohne Ihre Signatur und die zitierte Nachricht. Solange eine Nachricht signiert oder verschlüsselt werden soll, wird nichts gesendet. Was Sie vorher geschrieben haben, wurde möglicherweise schon geprüft.", + "forced": "Ihre Organisation hat die Korrekturhilfe eingeschaltet. Während Sie tippen, geht Ihr Text zur Prüfung an {checkers}, ohne Ihre Signatur und die zitierte Nachricht. Solange eine Nachricht signiert oder verschlüsselt werden soll, wird nichts gesendet. Was Sie vorher geschrieben haben, wurde möglicherweise schon geprüft.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Korrekturhilfe", "description": "Rechtschreib- und Grammatikfehler beim Schreiben unterstreichen, mit Vorschlägen zur Korrektur.", "enable": "Rechtschreibung und Grammatik beim Tippen prüfen", - "enable_description": "Ihr Text geht an die Erweiterung für die Korrekturhilfe und an den Dienst, den sie nutzt. Ihre Signatur und zitierte Nachrichten bleiben außen vor, und signierte oder verschlüsselte Nachrichten werden nie geprüft.", + "enable_description": "Ihr Text geht an die Erweiterung für die Korrekturhilfe und an den Dienst, den sie nutzt, ohne Ihre Signatur und zitierte Nachrichten. Solange eine Nachricht signiert oder verschlüsselt werden soll, wird nichts gesendet.", "no_checker": "Es ist keine Erweiterung für die Korrekturhilfe installiert. Bitten Sie Ihre Administration, eine hinzuzufügen.", "plugin_description": "Sendet Ihren Text an {plugin}.", "writing_language": "Schreibsprache", diff --git a/locales/en/common.json b/locales/en/common.json index 945500fb4..3079bd05e 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -6067,7 +6067,7 @@ }, "compose_check": { "title": "Check what you write", - "body": "Reads what you write in the composer while you type and may send it to the service it uses. Only where proofreading is on, and never for a message you sign or encrypt." + "body": "Reads what you write in the composer while you type and may send it to the service it uses. Only where proofreading is on, and nothing while a message is set to be signed or encrypted. Text you wrote before you turned that on may already have been checked." }, "email_forward": { "title": "Forward your mail", @@ -6394,7 +6394,7 @@ "title": "Proofreading", "required": "Required by your organisation", "check_message": "Check this message", - "notice": "While you type, the text you write goes to {checkers} to be checked. Your signature and the quoted message are left out, and nothing is sent for a message you sign or encrypt.", + "notice": "While you type, the text you write goes to {checkers} to be checked. Your signature and the quoted message are left out. Nothing is sent while a message is set to be signed or encrypted; what you wrote before turning that on may already have been checked.", "turn_on": "Turn on proofreading", "paused_protected": "Nothing is checked while this message is set to be signed or encrypted.", "paused_unknown": "{plugin} can sign or encrypt messages without showing it here, so nothing is checked while it is on.", @@ -6405,8 +6405,8 @@ "shortcuts": "Next issue: {next}. Previous: {previous}." }, "notice": { - "on": "Proofreading is on. While you type, the text you write goes to {checkers} to be checked, without your signature or the quoted message. Nothing is sent for a message you sign or encrypt.", - "forced": "Your organisation turned on proofreading. While you type, the text you write goes to {checkers} to be checked, without your signature or the quoted message. Nothing is sent for a message you sign or encrypt.", + "on": "Proofreading is on. While you type, the text you write goes to {checkers} to be checked, without your signature or the quoted message. Nothing is sent while a message is set to be signed or encrypted; what you wrote before that may already have been checked.", + "forced": "Your organisation turned on proofreading. While you type, the text you write goes to {checkers} to be checked, without your signature or the quoted message. Nothing is sent while a message is set to be signed or encrypted; what you wrote before that may already have been checked.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Proofreading", "description": "Underline spelling and grammar mistakes while you write, with suggestions to fix them.", "enable": "Check spelling and grammar while I type", - "enable_description": "The text you write goes to the proofreading add-on and the service it uses. Your signature and quoted messages are left out, and signed or encrypted messages are never checked.", + "enable_description": "The text you write goes to the proofreading add-on and the service it uses, without your signature or quoted messages. Nothing is sent while a message is set to be signed or encrypted.", "no_checker": "No proofreading add-on is installed. Ask your administrator to add one.", "plugin_description": "Sends what you write to {plugin}.", "writing_language": "Writing language", diff --git a/locales/es/common.json b/locales/es/common.json index 8c3ab8731..203dbd91a 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Revisar lo que escribe", - "body": "Lee lo que escribe en el editor mientras teclea y puede enviarlo al servicio que usa. Solo cuando la revisión está activada, y nunca en un mensaje que firme o cifre." + "body": "Lee lo que escribe en el editor mientras teclea y puede enviarlo al servicio que usa. Solo cuando la revisión está activada, y nada mientras un mensaje esté configurado para firmarse o cifrarse. El texto que escribió antes de activarlo puede que ya se haya revisado." }, "email_forward": { "title": "Reenviar tu correo", @@ -6394,7 +6394,7 @@ "title": "Revisión", "required": "Exigido por tu organización", "check_message": "Revisar este mensaje", - "notice": "Mientras escribes, el texto se envía a {checkers} para revisarlo. Tu firma y el mensaje citado quedan fuera, y no se envía nada de un mensaje que firmes o cifres.", + "notice": "Mientras escribes, el texto se envía a {checkers} para revisarlo. Tu firma y el mensaje citado quedan fuera. No se envía nada mientras un mensaje esté configurado para firmarse o cifrarse; lo que escribiste antes de activarlo puede que ya se haya revisado.", "turn_on": "Activar la revisión", "paused_protected": "No se revisa nada mientras este mensaje esté configurado para firmarse o cifrarse.", "paused_unknown": "{plugin} puede firmar o cifrar mensajes sin mostrarlo aquí, así que no se revisa nada mientras esté activo.", @@ -6405,8 +6405,8 @@ "shortcuts": "Siguiente problema: {next}. Anterior: {previous}." }, "notice": { - "on": "La revisión está activada. Mientras escribes, el texto se envía a {checkers} para revisarlo, sin tu firma ni el mensaje citado. No se envía nada de un mensaje que firmes o cifres.", - "forced": "Tu organización ha activado la revisión. Mientras escribes, el texto se envía a {checkers} para revisarlo, sin tu firma ni el mensaje citado. No se envía nada de un mensaje que firmes o cifres.", + "on": "La revisión está activada. Mientras escribes, el texto se envía a {checkers} para revisarlo, sin tu firma ni el mensaje citado. No se envía nada mientras un mensaje esté configurado para firmarse o cifrarse; lo que escribiste antes puede que ya se haya revisado.", + "forced": "Tu organización ha activado la revisión. Mientras escribes, el texto se envía a {checkers} para revisarlo, sin tu firma ni el mensaje citado. No se envía nada mientras un mensaje esté configurado para firmarse o cifrarse; lo que escribiste antes puede que ya se haya revisado.", "ok": "Aceptar" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Revisión", "description": "Subraya los errores de ortografía y gramática mientras escribes, con sugerencias para corregirlos.", "enable": "Revisar la ortografía y la gramática mientras escribo", - "enable_description": "El texto que escribes se envía al complemento de revisión y al servicio que usa. Tu firma y los mensajes citados quedan fuera, y los mensajes firmados o cifrados nunca se revisan.", + "enable_description": "El texto que escribes se envía al complemento de revisión y al servicio que usa, sin tu firma ni los mensajes citados. No se envía nada mientras un mensaje esté configurado para firmarse o cifrarse.", "no_checker": "No hay ningún complemento de revisión instalado. Pide a tu administrador que añada uno.", "plugin_description": "Envía lo que escribes a {plugin}.", "writing_language": "Idioma de escritura", diff --git a/locales/et/common.json b/locales/et/common.json index 1dc684789..6ea83f556 100644 --- a/locales/et/common.json +++ b/locales/et/common.json @@ -6067,7 +6067,7 @@ }, "compose_check": { "title": "Kontrollida, mida kirjutate", - "body": "Loeb kirjutamise ajal, mida koostajas kirjutate, ja võib selle saata teenusele, mida kasutab. Ainult siis, kui õigekirja kontroll on sees, ja mitte kunagi sõnumi puhul, mille allkirjastate või krüpteerite." + "body": "Loeb kirjutamise ajal, mida koostajas kirjutate, ja võib selle saata teenusele, mida kasutab. Ainult siis, kui õigekirja kontroll on sees, ja mitte midagi, kuni sõnum on määratud allkirjastamiseks või krüpteerimiseks. Tekst, mille kirjutasite enne selle sisselülitamist, võib olla juba kontrollitud." }, "email_forward": { "title": "Sinu kirjade edasisaatmine", @@ -6394,7 +6394,7 @@ "title": "Õigekirja kontroll", "required": "Sinu organisatsiooni nõue", "check_message": "Kontrolli seda sõnumit", - "notice": "Kirjutamise ajal läheb sinu tekst kontrollimiseks teenusele {checkers}. Sinu allkiri ja tsiteeritud sõnum jäetakse välja ning sõnumi kohta, mille allkirjastad või krüpteerid, ei saadeta midagi.", + "notice": "Kirjutamise ajal läheb sinu tekst kontrollimiseks teenusele {checkers}. Sinu allkiri ja tsiteeritud sõnum jäetakse välja. Midagi ei saadeta, kuni sõnum on määratud allkirjastamiseks või krüpteerimiseks; see, mille kirjutasid enne selle sisselülitamist, võib olla juba kontrollitud.", "turn_on": "Lülita õigekirja kontroll sisse", "paused_protected": "Midagi ei kontrollita, kuni see sõnum on määratud allkirjastamiseks või krüpteerimiseks.", "paused_unknown": "{plugin} võib sõnumeid allkirjastada või krüpteerida ilma seda siin näitamata, seega ei kontrollita midagi, kuni see on sees.", @@ -6405,8 +6405,8 @@ "shortcuts": "Järgmine probleem: {next}. Eelmine: {previous}." }, "notice": { - "on": "Õigekirja kontroll on sees. Kirjutamise ajal läheb sinu tekst kontrollimiseks teenusele {checkers}, ilma sinu allkirja ja tsiteeritud sõnumita. Sõnumi kohta, mille allkirjastad või krüpteerid, ei saadeta midagi.", - "forced": "Sinu organisatsioon lülitas õigekirja kontrolli sisse. Kirjutamise ajal läheb sinu tekst kontrollimiseks teenusele {checkers}, ilma sinu allkirja ja tsiteeritud sõnumita. Sõnumi kohta, mille allkirjastad või krüpteerid, ei saadeta midagi.", + "on": "Õigekirja kontroll on sees. Kirjutamise ajal läheb sinu tekst kontrollimiseks teenusele {checkers}, ilma sinu allkirja ja tsiteeritud sõnumita. Midagi ei saadeta, kuni sõnum on määratud allkirjastamiseks või krüpteerimiseks; see, mille kirjutasid enne seda, võib olla juba kontrollitud.", + "forced": "Sinu organisatsioon lülitas õigekirja kontrolli sisse. Kirjutamise ajal läheb sinu tekst kontrollimiseks teenusele {checkers}, ilma sinu allkirja ja tsiteeritud sõnumita. Midagi ei saadeta, kuni sõnum on määratud allkirjastamiseks või krüpteerimiseks; see, mille kirjutasid enne seda, võib olla juba kontrollitud.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Õigekirja kontroll", "description": "Joonib kirjutamise ajal alla õigekirja- ja grammatikavead ning pakub parandusi.", "enable": "Kontrolli õigekirja ja grammatikat kirjutamise ajal", - "enable_description": "Sinu kirjutatud tekst läheb õigekirja kontrolli lisale ja teenusele, mida see kasutab. Sinu allkiri ja tsiteeritud sõnumid jäetakse välja ning allkirjastatud või krüpteeritud sõnumeid ei kontrollita kunagi.", + "enable_description": "Sinu kirjutatud tekst läheb õigekirja kontrolli lisale ja teenusele, mida see kasutab, ilma sinu allkirja ja tsiteeritud sõnumiteta. Midagi ei saadeta, kuni sõnum on määratud allkirjastamiseks või krüpteerimiseks.", "no_checker": "Õigekirja kontrolli lisa pole paigaldatud. Palu administraatoril see lisada.", "plugin_description": "Saadab sinu kirjutatu teenusele {plugin}.", "writing_language": "Kirjutamise keel", diff --git a/locales/fa/common.json b/locales/fa/common.json index 033ae2ee8..1b8cec680 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "بررسی آنچه می‌نویسید", - "body": "آنچه را در ویرایشگر می‌نویسید هنگام تایپ می‌خواند و ممکن است آن را به سرویسی که استفاده می‌کند بفرستد. فقط وقتی بررسی متن روشن است، و هرگز برای پیامی که امضا یا رمزگذاری می‌کنید." + "body": "آنچه را در ویرایشگر می‌نویسید هنگام تایپ می‌خواند و ممکن است آن را به سرویسی که استفاده می‌کند بفرستد. فقط وقتی بررسی متن روشن است، و تا وقتی پیامی برای امضا یا رمزگذاری تنظیم شده چیزی نمی‌فرستد. متنی که پیش از روشن کردن این گزینه نوشته‌اید ممکن است قبلاً بررسی شده باشد." }, "email_forward": { "title": "هدایت ایمیل‌های شما", @@ -6394,7 +6394,7 @@ "title": "بررسی متن", "required": "سازمان شما آن را الزامی کرده است", "check_message": "بررسی این پیام", - "notice": "هنگام تایپ، متنی که می‌نویسید برای بررسی به {checkers} فرستاده می‌شود. امضای شما و پیام نقل‌قول‌شده کنار گذاشته می‌شوند و برای پیامی که امضا یا رمزگذاری می‌کنید چیزی فرستاده نمی‌شود.", + "notice": "هنگام تایپ، متنی که می‌نویسید برای بررسی به {checkers} فرستاده می‌شود. امضای شما و پیام نقل‌قول‌شده کنار گذاشته می‌شوند. تا وقتی پیامی برای امضا یا رمزگذاری تنظیم شده، چیزی فرستاده نمی‌شود؛ آنچه پیش از روشن کردن این گزینه نوشته‌اید ممکن است قبلاً بررسی شده باشد.", "turn_on": "روشن کردن بررسی متن", "paused_protected": "تا وقتی این پیام برای امضا یا رمزگذاری تنظیم شده، چیزی بررسی نمی‌شود.", "paused_unknown": "{plugin} می‌تواند پیام‌ها را بدون نمایش در اینجا امضا یا رمزگذاری کند، پس تا وقتی روشن است چیزی بررسی نمی‌شود.", @@ -6405,8 +6405,8 @@ "shortcuts": "مشکل بعدی: {next}. قبلی: {previous}." }, "notice": { - "on": "بررسی متن روشن است. هنگام تایپ، متنی که می‌نویسید بدون امضا و پیام نقل‌قول‌شده برای بررسی به {checkers} فرستاده می‌شود. برای پیامی که امضا یا رمزگذاری می‌کنید چیزی فرستاده نمی‌شود.", - "forced": "سازمان شما بررسی متن را روشن کرده است. هنگام تایپ، متنی که می‌نویسید بدون امضا و پیام نقل‌قول‌شده برای بررسی به {checkers} فرستاده می‌شود. برای پیامی که امضا یا رمزگذاری می‌کنید چیزی فرستاده نمی‌شود.", + "on": "بررسی متن روشن است. هنگام تایپ، متنی که می‌نویسید بدون امضا و پیام نقل‌قول‌شده برای بررسی به {checkers} فرستاده می‌شود. تا وقتی پیامی برای امضا یا رمزگذاری تنظیم شده، چیزی فرستاده نمی‌شود؛ آنچه پیش از آن نوشته‌اید ممکن است قبلاً بررسی شده باشد.", + "forced": "سازمان شما بررسی متن را روشن کرده است. هنگام تایپ، متنی که می‌نویسید بدون امضا و پیام نقل‌قول‌شده برای بررسی به {checkers} فرستاده می‌شود. تا وقتی پیامی برای امضا یا رمزگذاری تنظیم شده، چیزی فرستاده نمی‌شود؛ آنچه پیش از آن نوشته‌اید ممکن است قبلاً بررسی شده باشد.", "ok": "باشه" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "بررسی متن", "description": "هنگام نوشتن زیر خطاهای املایی و دستوری خط می‌کشد و برای اصلاح آن‌ها پیشنهاد می‌دهد.", "enable": "بررسی املا و دستور زبان هنگام تایپ", - "enable_description": "متنی که می‌نویسید به افزونه‌ی بررسی متن و سرویسی که استفاده می‌کند فرستاده می‌شود. امضای شما و پیام‌های نقل‌قول‌شده کنار گذاشته می‌شوند و پیام‌های امضاشده یا رمزگذاری‌شده هرگز بررسی نمی‌شوند.", + "enable_description": "متنی که می‌نویسید بدون امضا و پیام‌های نقل‌قول‌شده به افزونه‌ی بررسی متن و سرویسی که استفاده می‌کند فرستاده می‌شود. تا وقتی پیامی برای امضا یا رمزگذاری تنظیم شده، چیزی فرستاده نمی‌شود.", "no_checker": "هیچ افزونه‌ی بررسی متنی نصب نشده است. از مدیر سیستم بخواهید یکی اضافه کند.", "plugin_description": "آنچه می‌نویسید را به {plugin} می‌فرستد.", "writing_language": "زبان نوشتن", diff --git a/locales/fr/common.json b/locales/fr/common.json index 3e6a41c3a..a56449a3c 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Vérifier ce que vous écrivez", - "body": "Lit ce que vous écrivez dans l'éditeur pendant la saisie et peut l'envoyer au service qu'il utilise. Seulement quand la correction est activée, et jamais pour un message que vous signez ou chiffrez." + "body": "Lit ce que vous écrivez dans l'éditeur pendant la saisie et peut l'envoyer au service qu'il utilise. Seulement quand la correction est activée, et rien tant qu'un message doit être signé ou chiffré. Le texte écrit avant d'activer cette option a peut-être déjà été vérifié." }, "email_forward": { "title": "Transférer vos e-mails", @@ -6394,7 +6394,7 @@ "title": "Correction", "required": "Exigé par votre organisation", "check_message": "Vérifier ce message", - "notice": "Pendant la saisie, le texte que vous écrivez est envoyé à {checkers} pour être vérifié. Votre signature et le message cité sont exclus, et rien n'est envoyé pour un message que vous signez ou chiffrez.", + "notice": "Pendant la saisie, le texte que vous écrivez est envoyé à {checkers} pour être vérifié. Votre signature et le message cité sont exclus. Rien n'est envoyé tant qu'un message doit être signé ou chiffré ; ce que vous avez écrit avant d'activer cette option a peut-être déjà été vérifié.", "turn_on": "Activer la correction", "paused_protected": "Rien n'est vérifié tant que ce message doit être signé ou chiffré.", "paused_unknown": "{plugin} peut signer ou chiffrer des messages sans que cela s'affiche ici, donc rien n'est vérifié tant qu'il est actif.", @@ -6405,8 +6405,8 @@ "shortcuts": "Problème suivant : {next}. Précédent : {previous}." }, "notice": { - "on": "La correction est activée. Pendant la saisie, le texte que vous écrivez est envoyé à {checkers} pour être vérifié, sans votre signature ni le message cité. Rien n'est envoyé pour un message que vous signez ou chiffrez.", - "forced": "Votre organisation a activé la correction. Pendant la saisie, le texte que vous écrivez est envoyé à {checkers} pour être vérifié, sans votre signature ni le message cité. Rien n'est envoyé pour un message que vous signez ou chiffrez.", + "on": "La correction est activée. Pendant la saisie, le texte que vous écrivez est envoyé à {checkers} pour être vérifié, sans votre signature ni le message cité. Rien n'est envoyé tant qu'un message doit être signé ou chiffré ; ce que vous avez écrit avant a peut-être déjà été vérifié.", + "forced": "Votre organisation a activé la correction. Pendant la saisie, le texte que vous écrivez est envoyé à {checkers} pour être vérifié, sans votre signature ni le message cité. Rien n'est envoyé tant qu'un message doit être signé ou chiffré ; ce que vous avez écrit avant a peut-être déjà été vérifié.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Correction", "description": "Souligner les fautes d'orthographe et de grammaire pendant que vous écrivez, avec des suggestions pour les corriger.", "enable": "Vérifier l'orthographe et la grammaire pendant la saisie", - "enable_description": "Le texte que vous écrivez est envoyé à l'extension de correction et au service qu'elle utilise. Votre signature et les messages cités sont exclus, et les messages signés ou chiffrés ne sont jamais vérifiés.", + "enable_description": "Le texte que vous écrivez est envoyé à l'extension de correction et au service qu'elle utilise, sans votre signature ni les messages cités. Rien n'est envoyé tant qu'un message doit être signé ou chiffré.", "no_checker": "Aucune extension de correction n'est installée. Demandez à votre administrateur d'en ajouter une.", "plugin_description": "Envoie ce que vous écrivez à {plugin}.", "writing_language": "Langue de rédaction", diff --git a/locales/he/common.json b/locales/he/common.json index 78b2f949e..37200c853 100644 --- a/locales/he/common.json +++ b/locales/he/common.json @@ -6040,7 +6040,7 @@ }, "compose_check": { "title": "בדיקת מה שאתם כותבים", - "body": "קורא את מה שאתם כותבים בעורך בזמן ההקלדה ועשוי לשלוח אותו לשירות שבו הוא משתמש. רק כשהבדיקה מופעלת, ולעולם לא בהודעה שאתם חותמים או מצפינים." + "body": "קורא את מה שאתם כותבים בעורך בזמן ההקלדה ועשוי לשלוח אותו לשירות שבו הוא משתמש. רק כשהבדיקה מופעלת, ושום דבר כל עוד הודעה מוגדרת לחתימה או להצפנה. טקסט שכתבתם לפני שהפעלתם את זה אולי כבר נבדק." }, "email_forward": { "title": "העברת הדואר שלכם", @@ -6395,7 +6395,7 @@ "title": "הגהה", "required": "נדרש על ידי הארגון שלך", "check_message": "בדיקת ההודעה הזו", - "notice": "בזמן ההקלדה, הטקסט שכתבת נשלח ל-{checkers} לבדיקה. החתימה שלך וההודעה המצוטטת לא נשלחות, ושום דבר לא נשלח מהודעה שנחתמת או מוצפנת.", + "notice": "בזמן ההקלדה, הטקסט שכתבת נשלח ל-{checkers} לבדיקה. החתימה שלך וההודעה המצוטטת לא נשלחות. שום דבר לא נשלח כל עוד הודעה מוגדרת לחתימה או להצפנה; מה שכתבת לפני שהפעלת את זה אולי כבר נבדק.", "turn_on": "הפעלת הגהה", "paused_protected": "שום דבר לא נבדק כל עוד ההודעה הזו מוגדרת לחתימה או להצפנה.", "paused_unknown": "{plugin} יכול לחתום על הודעות או להצפין אותן בלי שזה יוצג כאן, לכן שום דבר לא נבדק כל עוד הוא פעיל.", @@ -6406,8 +6406,8 @@ "shortcuts": "הבעיה הבאה: {next}. הקודמת: {previous}." }, "notice": { - "on": "ההגהה מופעלת. בזמן ההקלדה, הטקסט שכתבת נשלח ל-{checkers} לבדיקה, בלי החתימה שלך וההודעה המצוטטת. שום דבר לא נשלח מהודעה שנחתמת או מוצפנת.", - "forced": "הארגון שלך הפעיל הגהה. בזמן ההקלדה, הטקסט שכתבת נשלח ל-{checkers} לבדיקה, בלי החתימה שלך וההודעה המצוטטת. שום דבר לא נשלח מהודעה שנחתמת או מוצפנת.", + "on": "ההגהה מופעלת. בזמן ההקלדה, הטקסט שכתבת נשלח ל-{checkers} לבדיקה, בלי החתימה שלך וההודעה המצוטטת. שום דבר לא נשלח כל עוד הודעה מוגדרת לחתימה או להצפנה; מה שכתבת לפני כן אולי כבר נבדק.", + "forced": "הארגון שלך הפעיל הגהה. בזמן ההקלדה, הטקסט שכתבת נשלח ל-{checkers} לבדיקה, בלי החתימה שלך וההודעה המצוטטת. שום דבר לא נשלח כל עוד הודעה מוגדרת לחתימה או להצפנה; מה שכתבת לפני כן אולי כבר נבדק.", "ok": "אישור" }, "send_warning": { @@ -6420,7 +6420,7 @@ "title": "הגהה", "description": "סימון שגיאות איות ודקדוק בקו תחתון בזמן הכתיבה, עם הצעות לתיקון.", "enable": "בדיקת איות ודקדוק בזמן ההקלדה", - "enable_description": "הטקסט שכתבת נשלח לתוסף ההגהה ולשירות שהוא משתמש בו. החתימה שלך וההודעות המצוטטות לא נשלחות, והודעות חתומות או מוצפנות אף פעם לא נבדקות.", + "enable_description": "הטקסט שכתבת נשלח לתוסף ההגהה ולשירות שהוא משתמש בו, בלי החתימה שלך וההודעות המצוטטות. שום דבר לא נשלח כל עוד הודעה מוגדרת לחתימה או להצפנה.", "no_checker": "לא מותקן תוסף הגהה. אפשר לבקש ממנהל המערכת להוסיף אחד.", "plugin_description": "שולח את מה שכתבת אל {plugin}.", "writing_language": "שפת הכתיבה", diff --git a/locales/hu/common.json b/locales/hu/common.json index fc8d8f2a7..db599142a 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Ellenőrizni, amit ír", - "body": "Gépelés közben olvassa, amit a szerkesztőben ír, és elküldheti az általa használt szolgáltatásnak. Csak ha a helyesírás-ellenőrzés be van kapcsolva, és soha nem olyan üzenetnél, amelyet aláír vagy titkosít." + "body": "Gépelés közben olvassa, amit a szerkesztőben ír, és elküldheti az általa használt szolgáltatásnak. Csak ha a helyesírás-ellenőrzés be van kapcsolva, és semmit, amíg egy üzenet aláírásra vagy titkosításra van beállítva. Amit a bekapcsolása előtt írt, azt már ellenőrizhették." }, "email_forward": { "title": "Leveleid továbbítása", @@ -6394,7 +6394,7 @@ "title": "Helyesírás-ellenőrzés", "required": "A szervezeted előírja", "check_message": "Üzenet ellenőrzése", - "notice": "Gépelés közben a beírt szöveg ellenőrzésre kerül ide: {checkers}. Az aláírásod és az idézett üzenet kimarad, és az aláírt vagy titkosított üzenetekből semmi nem kerül elküldésre.", + "notice": "Gépelés közben a beírt szöveg ellenőrzésre kerül ide: {checkers}. Az aláírásod és az idézett üzenet kimarad. Amíg egy üzenet aláírásra vagy titkosításra van beállítva, semmi nem megy el; amit a bekapcsolása előtt írtál, azt már ellenőrizhették.", "turn_on": "Helyesírás-ellenőrzés bekapcsolása", "paused_protected": "Amíg ez az üzenet aláírásra vagy titkosításra van beállítva, semmi nem kerül ellenőrzésre.", "paused_unknown": "{plugin} úgy is aláírhat vagy titkosíthat üzeneteket, hogy az itt nem látszik, ezért amíg be van kapcsolva, semmi nem kerül ellenőrzésre.", @@ -6405,8 +6405,8 @@ "shortcuts": "Következő probléma: {next}. Előző: {previous}." }, "notice": { - "on": "A helyesírás-ellenőrzés be van kapcsolva. Gépelés közben a beírt szöveg ellenőrzésre kerül ide: {checkers}, az aláírásod és az idézett üzenet nélkül. Az aláírt vagy titkosított üzenetekből semmi nem kerül elküldésre.", - "forced": "A szervezeted bekapcsolta a helyesírás-ellenőrzést. Gépelés közben a beírt szöveg ellenőrzésre kerül ide: {checkers}, az aláírásod és az idézett üzenet nélkül. Az aláírt vagy titkosított üzenetekből semmi nem kerül elküldésre.", + "on": "A helyesírás-ellenőrzés be van kapcsolva. Gépelés közben a beírt szöveg ellenőrzésre kerül ide: {checkers}, az aláírásod és az idézett üzenet nélkül. Amíg egy üzenet aláírásra vagy titkosításra van beállítva, semmi nem megy el; amit előtte írtál, azt már ellenőrizhették.", + "forced": "A szervezeted bekapcsolta a helyesírás-ellenőrzést. Gépelés közben a beírt szöveg ellenőrzésre kerül ide: {checkers}, az aláírásod és az idézett üzenet nélkül. Amíg egy üzenet aláírásra vagy titkosításra van beállítva, semmi nem megy el; amit előtte írtál, azt már ellenőrizhették.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Helyesírás-ellenőrzés", "description": "Írás közben aláhúzza a helyesírási és nyelvtani hibákat, és javaslatot ad a javításukra.", "enable": "Helyesírás és nyelvtan ellenőrzése gépelés közben", - "enable_description": "A beírt szöveg a helyesírás-ellenőrző bővítményhez és az általa használt szolgáltatáshoz kerül. Az aláírásod és az idézett üzenetek kimaradnak, az aláírt vagy titkosított üzeneteket pedig soha nem ellenőrzi.", + "enable_description": "A beírt szöveg a helyesírás-ellenőrző bővítményhez és az általa használt szolgáltatáshoz kerül, az aláírásod és az idézett üzenetek nélkül. Amíg egy üzenet aláírásra vagy titkosításra van beállítva, semmi nem megy el.", "no_checker": "Nincs telepítve helyesírás-ellenőrző bővítmény. Kérd meg a rendszergazdát, hogy adjon hozzá egyet.", "plugin_description": "Elküldi, amit írsz, ide: {plugin}.", "writing_language": "Írás nyelve", diff --git a/locales/it/common.json b/locales/it/common.json index 0c8a356a4..aa99076ae 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Controllare ciò che scrivi", - "body": "Legge ciò che scrivi nell'editor mentre digiti e può inviarlo al servizio che usa. Solo quando la correzione è attiva, e mai per un messaggio che firmi o cifri." + "body": "Legge ciò che scrivi nell'editor mentre digiti e può inviarlo al servizio che usa. Solo quando la correzione è attiva, e nulla finché un messaggio è impostato per essere firmato o cifrato. Il testo scritto prima di attivarlo potrebbe essere già stato controllato." }, "email_forward": { "title": "Inoltrare la tua posta", @@ -6394,7 +6394,7 @@ "title": "Correzione", "required": "Richiesto dalla tua organizzazione", "check_message": "Controlla questo messaggio", - "notice": "Mentre digiti, il testo che scrivi viene inviato a {checkers} per il controllo. La tua firma e il messaggio citato sono esclusi, e non viene inviato nulla per un messaggio che firmi o cifri.", + "notice": "Mentre digiti, il testo che scrivi viene inviato a {checkers} per il controllo. La tua firma e il messaggio citato sono esclusi. Non viene inviato nulla finché un messaggio è impostato per essere firmato o cifrato; ciò che hai scritto prima di attivarlo potrebbe essere già stato controllato.", "turn_on": "Attiva la correzione", "paused_protected": "Non viene controllato nulla finché questo messaggio è impostato per essere firmato o cifrato.", "paused_unknown": "{plugin} può firmare o cifrare i messaggi senza mostrarlo qui, quindi non viene controllato nulla finché è attivo.", @@ -6405,8 +6405,8 @@ "shortcuts": "Problema successivo: {next}. Precedente: {previous}." }, "notice": { - "on": "La correzione è attiva. Mentre digiti, il testo che scrivi viene inviato a {checkers} per il controllo, senza la tua firma né il messaggio citato. Non viene inviato nulla per un messaggio che firmi o cifri.", - "forced": "La tua organizzazione ha attivato la correzione. Mentre digiti, il testo che scrivi viene inviato a {checkers} per il controllo, senza la tua firma né il messaggio citato. Non viene inviato nulla per un messaggio che firmi o cifri.", + "on": "La correzione è attiva. Mentre digiti, il testo che scrivi viene inviato a {checkers} per il controllo, senza la tua firma né il messaggio citato. Non viene inviato nulla finché un messaggio è impostato per essere firmato o cifrato; ciò che hai scritto prima potrebbe essere già stato controllato.", + "forced": "La tua organizzazione ha attivato la correzione. Mentre digiti, il testo che scrivi viene inviato a {checkers} per il controllo, senza la tua firma né il messaggio citato. Non viene inviato nulla finché un messaggio è impostato per essere firmato o cifrato; ciò che hai scritto prima potrebbe essere già stato controllato.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Correzione", "description": "Sottolinea gli errori di ortografia e grammatica mentre scrivi, con suggerimenti per correggerli.", "enable": "Controlla ortografia e grammatica mentre digito", - "enable_description": "Il testo che scrivi viene inviato al componente aggiuntivo di correzione e al servizio che usa. La tua firma e i messaggi citati sono esclusi, e i messaggi firmati o cifrati non vengono mai controllati.", + "enable_description": "Il testo che scrivi viene inviato al componente aggiuntivo di correzione e al servizio che usa, senza la tua firma né i messaggi citati. Non viene inviato nulla finché un messaggio è impostato per essere firmato o cifrato.", "no_checker": "Nessun componente aggiuntivo di correzione installato. Chiedi all'amministratore di aggiungerne uno.", "plugin_description": "Invia ciò che scrivi a {plugin}.", "writing_language": "Lingua di scrittura", diff --git a/locales/ja/common.json b/locales/ja/common.json index c183069d3..777959ed5 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "書いた内容をチェック", - "body": "作成画面で入力中の内容を読み取り、利用するサービスに送信することがあります。校正がオンのときだけで、署名または暗号化するメッセージでは決して行いません。" + "body": "作成画面で入力中の内容を読み取り、利用するサービスに送信することがあります。校正がオンのときだけで、メッセージが署名または暗号化の設定になっている間は何も送信しません。その設定をオンにする前に書いたテキストは、すでにチェックされている可能性があります。" }, "email_forward": { "title": "メールを転送", @@ -6394,7 +6394,7 @@ "title": "校正", "required": "組織で必須に設定されています", "check_message": "このメッセージをチェック", - "notice": "入力中のテキストはチェックのため {checkers} に送信されます。署名と引用されたメッセージは除外され、署名または暗号化するメッセージでは何も送信されません。", + "notice": "入力中のテキストはチェックのため {checkers} に送信されます。署名と引用されたメッセージは除外されます。メッセージが署名または暗号化の設定になっている間は何も送信されませんが、その設定をオンにする前に書いた内容はすでにチェックされている可能性があります。", "turn_on": "校正をオンにする", "paused_protected": "このメッセージが署名または暗号化の設定になっている間は、何もチェックされません。", "paused_unknown": "{plugin} はここに表示せずにメッセージを署名または暗号化できるため、オンの間は何もチェックされません。", @@ -6405,8 +6405,8 @@ "shortcuts": "次の問題: {next}。前の問題: {previous}。" }, "notice": { - "on": "校正がオンです。入力中のテキストは、署名と引用されたメッセージを除いて、チェックのため {checkers} に送信されます。署名または暗号化するメッセージでは何も送信されません。", - "forced": "組織によって校正がオンになっています。入力中のテキストは、署名と引用されたメッセージを除いて、チェックのため {checkers} に送信されます。署名または暗号化するメッセージでは何も送信されません。", + "on": "校正がオンです。入力中のテキストは、署名と引用されたメッセージを除いて、チェックのため {checkers} に送信されます。メッセージが署名または暗号化の設定になっている間は何も送信されませんが、それ以前に書いた内容はすでにチェックされている可能性があります。", + "forced": "組織によって校正がオンになっています。入力中のテキストは、署名と引用されたメッセージを除いて、チェックのため {checkers} に送信されます。メッセージが署名または暗号化の設定になっている間は何も送信されませんが、それ以前に書いた内容はすでにチェックされている可能性があります。", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "校正", "description": "入力中にスペルや文法の誤りに下線を引き、修正候補を表示します。", "enable": "入力中にスペルと文法をチェックする", - "enable_description": "入力したテキストは校正アドオンと、そのアドオンが利用するサービスに送信されます。署名と引用されたメッセージは除外され、署名または暗号化されたメッセージはチェックされません。", + "enable_description": "入力したテキストは、署名と引用されたメッセージを除いて、校正アドオンとそのアドオンが利用するサービスに送信されます。メッセージが署名または暗号化の設定になっている間は何も送信されません。", "no_checker": "校正アドオンがインストールされていません。管理者に追加を依頼してください。", "plugin_description": "入力した内容を {plugin} に送信します。", "writing_language": "書く言語", diff --git a/locales/ko/common.json b/locales/ko/common.json index 1423fcc73..26b874a36 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "작성 내용 검사", - "body": "작성 창에서 입력하는 내용을 읽고, 사용하는 서비스로 보낼 수 있습니다. 교정이 켜져 있을 때만 그리고 서명하거나 암호화하는 메시지에서는 절대 하지 않습니다." + "body": "작성 창에서 입력하는 내용을 읽고, 사용하는 서비스로 보낼 수 있습니다. 교정이 켜져 있을 때만 보내며, 메시지가 서명 또는 암호화되도록 설정되어 있는 동안에는 아무것도 보내지 않습니다. 이 설정을 켜기 전에 작성한 텍스트는 이미 검사되었을 수 있습니다." }, "email_forward": { "title": "메일 전달", @@ -6394,7 +6394,7 @@ "title": "교정", "required": "조직에서 필수로 지정함", "check_message": "이 메시지 검사", - "notice": "입력하는 동안 작성한 텍스트가 검사를 위해 {checkers}(으)로 전송됩니다. 서명과 인용된 메시지는 제외되며, 서명하거나 암호화하는 메시지는 아무것도 전송되지 않습니다.", + "notice": "입력하는 동안 작성한 텍스트가 검사를 위해 {checkers}(으)로 전송됩니다. 서명과 인용된 메시지는 제외됩니다. 메시지가 서명 또는 암호화되도록 설정되어 있는 동안에는 아무것도 전송되지 않지만, 이 설정을 켜기 전에 작성한 내용은 이미 검사되었을 수 있습니다.", "turn_on": "교정 켜기", "paused_protected": "이 메시지가 서명 또는 암호화되도록 설정되어 있는 동안에는 아무것도 검사하지 않습니다.", "paused_unknown": "{plugin}은(는) 여기에 표시하지 않고 메시지에 서명하거나 메시지를 암호화할 수 있으므로, 켜져 있는 동안에는 아무것도 검사하지 않습니다.", @@ -6405,8 +6405,8 @@ "shortcuts": "다음 문제: {next}. 이전: {previous}." }, "notice": { - "on": "교정이 켜져 있습니다. 입력하는 동안 작성한 텍스트가 서명과 인용된 메시지를 제외하고 검사를 위해 {checkers}(으)로 전송됩니다. 서명하거나 암호화하는 메시지는 아무것도 전송되지 않습니다.", - "forced": "조직에서 교정을 켰습니다. 입력하는 동안 작성한 텍스트가 서명과 인용된 메시지를 제외하고 검사를 위해 {checkers}(으)로 전송됩니다. 서명하거나 암호화하는 메시지는 아무것도 전송되지 않습니다.", + "on": "교정이 켜져 있습니다. 입력하는 동안 작성한 텍스트가 서명과 인용된 메시지를 제외하고 검사를 위해 {checkers}(으)로 전송됩니다. 메시지가 서명 또는 암호화되도록 설정되어 있는 동안에는 아무것도 전송되지 않지만, 그 전에 작성한 내용은 이미 검사되었을 수 있습니다.", + "forced": "조직에서 교정을 켰습니다. 입력하는 동안 작성한 텍스트가 서명과 인용된 메시지를 제외하고 검사를 위해 {checkers}(으)로 전송됩니다. 메시지가 서명 또는 암호화되도록 설정되어 있는 동안에는 아무것도 전송되지 않지만, 그 전에 작성한 내용은 이미 검사되었을 수 있습니다.", "ok": "확인" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "교정", "description": "작성하는 동안 맞춤법과 문법 오류에 밑줄을 긋고 수정 제안을 보여줍니다.", "enable": "입력하는 동안 맞춤법과 문법 검사", - "enable_description": "작성한 텍스트는 교정 추가 기능과 그 기능이 사용하는 서비스로 전송됩니다. 서명과 인용된 메시지는 제외되며, 서명되거나 암호화된 메시지는 절대 검사하지 않습니다.", + "enable_description": "작성한 텍스트는 서명과 인용된 메시지를 제외하고 교정 추가 기능과 그 기능이 사용하는 서비스로 전송됩니다. 메시지가 서명 또는 암호화되도록 설정되어 있는 동안에는 아무것도 전송되지 않습니다.", "no_checker": "설치된 교정 추가 기능이 없습니다. 관리자에게 추가를 요청하세요.", "plugin_description": "작성한 내용을 {plugin}(으)로 보냅니다.", "writing_language": "작성 언어", diff --git a/locales/lv/common.json b/locales/lv/common.json index 06c36dbd8..b7c654f2c 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Pārbaudīt, ko rakstāt", - "body": "Rakstīšanas laikā lasa to, ko rakstāt redaktorā, un var to nosūtīt pakalpojumam, ko izmanto. Tikai tad, ja pārbaude ir ieslēgta, un nekad ziņojumam, ko parakstāt vai šifrējat." + "body": "Rakstīšanas laikā lasa to, ko rakstāt redaktorā, un var to nosūtīt pakalpojumam, ko izmanto. Tikai tad, ja pārbaude ir ieslēgta, un nekas, kamēr ziņojums ir iestatīts parakstīšanai vai šifrēšanai. Teksts, ko uzrakstījāt pirms šī iestatījuma ieslēgšanas, jau var būt pārbaudīts." }, "email_forward": { "title": "Pārsūtīt jūsu pastu", @@ -6394,7 +6394,7 @@ "title": "Teksta pārbaude", "required": "Pieprasa jūsu organizācija", "check_message": "Pārbaudīt šo ziņojumu", - "notice": "Kamēr rakstāt, jūsu teksts tiek nosūtīts pārbaudei uz {checkers}. Jūsu paraksts un citētais ziņojums netiek sūtīti, un ziņojumam, ko parakstāt vai šifrējat, netiek sūtīts nekas.", + "notice": "Kamēr rakstāt, jūsu teksts tiek nosūtīts pārbaudei uz {checkers}. Jūsu paraksts un citētais ziņojums netiek sūtīti. Kamēr ziņojums ir iestatīts parakstīšanai vai šifrēšanai, nekas netiek sūtīts; tas, ko uzrakstījāt pirms šī iestatījuma ieslēgšanas, jau var būt pārbaudīts.", "turn_on": "Ieslēgt teksta pārbaudi", "paused_protected": "Kamēr šis ziņojums ir iestatīts parakstīšanai vai šifrēšanai, nekas netiek pārbaudīts.", "paused_unknown": "{plugin} var parakstīt vai šifrēt ziņojumus, to šeit nerādot, tāpēc, kamēr tas ir ieslēgts, nekas netiek pārbaudīts.", @@ -6405,8 +6405,8 @@ "shortcuts": "Nākamā problēma: {next}. Iepriekšējā: {previous}." }, "notice": { - "on": "Teksta pārbaude ir ieslēgta. Kamēr rakstāt, jūsu teksts tiek nosūtīts pārbaudei uz {checkers}, bez jūsu paraksta un citētā ziņojuma. Ziņojumam, ko parakstāt vai šifrējat, netiek sūtīts nekas.", - "forced": "Jūsu organizācija ieslēdza teksta pārbaudi. Kamēr rakstāt, jūsu teksts tiek nosūtīts pārbaudei uz {checkers}, bez jūsu paraksta un citētā ziņojuma. Ziņojumam, ko parakstāt vai šifrējat, netiek sūtīts nekas.", + "on": "Teksta pārbaude ir ieslēgta. Kamēr rakstāt, jūsu teksts tiek nosūtīts pārbaudei uz {checkers}, bez jūsu paraksta un citētā ziņojuma. Kamēr ziņojums ir iestatīts parakstīšanai vai šifrēšanai, nekas netiek sūtīts; tas, ko uzrakstījāt pirms tam, jau var būt pārbaudīts.", + "forced": "Jūsu organizācija ieslēdza teksta pārbaudi. Kamēr rakstāt, jūsu teksts tiek nosūtīts pārbaudei uz {checkers}, bez jūsu paraksta un citētā ziņojuma. Kamēr ziņojums ir iestatīts parakstīšanai vai šifrēšanai, nekas netiek sūtīts; tas, ko uzrakstījāt pirms tam, jau var būt pārbaudīts.", "ok": "Labi" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Teksta pārbaude", "description": "Rakstīšanas laikā pasvītro pareizrakstības un gramatikas kļūdas un iesaka labojumus.", "enable": "Pārbaudīt pareizrakstību un gramatiku rakstīšanas laikā", - "enable_description": "Jūsu rakstītais teksts tiek nosūtīts teksta pārbaudes papildinājumam un pakalpojumam, ko tas izmanto. Jūsu paraksts un citētie ziņojumi netiek sūtīti, un parakstīti vai šifrēti ziņojumi nekad netiek pārbaudīti.", + "enable_description": "Jūsu rakstītais teksts tiek nosūtīts teksta pārbaudes papildinājumam un pakalpojumam, ko tas izmanto, bez jūsu paraksta un citēto ziņojumu. Kamēr ziņojums ir iestatīts parakstīšanai vai šifrēšanai, nekas netiek sūtīts.", "no_checker": "Nav instalēts neviens teksta pārbaudes papildinājums. Lūdziet administratoram to pievienot.", "plugin_description": "Nosūta jūsu rakstīto uz {plugin}.", "writing_language": "Rakstīšanas valoda", diff --git a/locales/mn/common.json b/locales/mn/common.json index 9fbc683de..22792dea6 100644 --- a/locales/mn/common.json +++ b/locales/mn/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Бичсэнээ шалгах", - "body": "Засварлагчид бичиж буй зүйлийг бичих явцад уншиж, ашигладаг үйлчилгээ рүүгээ илгээж болно. Зөвхөн шалгалт асаалттай үед, гарын үсэг зурах эсвэл шифрлэх мессежид хэзээ ч биш." + "body": "Засварлагчид бичиж буй зүйлийг бичих явцад уншиж, ашигладаг үйлчилгээ рүүгээ илгээж болно. Зөвхөн шалгалт асаалттай үед; мессежийг гарын үсэг зурах эсвэл шифрлэхээр тохируулсан үед юу ч илгээхгүй. Үүнийг асаахаас өмнө бичсэн текст аль хэдийн шалгагдсан байж болно." }, "email_forward": { "title": "Таны имэйлийг дамжуулах", @@ -6394,7 +6394,7 @@ "title": "Текст шалгалт", "required": "Таны байгууллага шаарддаг", "check_message": "Энэ мессежийг шалгах", - "notice": "Таныг бичих явцад бичсэн текст тань шалгуулахаар {checkers} руу очно. Таны гарын үсэг болон ишлэсэн мессеж орохгүй бөгөөд гарын үсэг зурах эсвэл шифрлэх мессежээс юу ч илгээгдэхгүй.", + "notice": "Таныг бичих явцад бичсэн текст тань шалгуулахаар {checkers} руу очно. Таны гарын үсэг болон ишлэсэн мессеж орохгүй. Мессежийг гарын үсэг зурах эсвэл шифрлэхээр тохируулсан үед юу ч илгээгдэхгүй; үүнийг асаахаас өмнө бичсэн зүйл тань аль хэдийн шалгагдсан байж болно.", "turn_on": "Текст шалгалтыг асаах", "paused_protected": "Энэ мессежийг гарын үсэг зурах эсвэл шифрлэхээр тохируулсан үед юу ч шалгагдахгүй.", "paused_unknown": "{plugin} нь энд харуулахгүйгээр мессежид гарын үсэг зурж эсвэл шифрлэж чаддаг тул асаалттай үед юу ч шалгагдахгүй.", @@ -6405,8 +6405,8 @@ "shortcuts": "Дараагийн асуудал: {next}. Өмнөх: {previous}." }, "notice": { - "on": "Текст шалгалт асаалттай байна. Таныг бичих явцад бичсэн текст тань гарын үсэг болон ишлэсэн мессежгүйгээр шалгуулахаар {checkers} руу очно. Гарын үсэг зурах эсвэл шифрлэх мессежээс юу ч илгээгдэхгүй.", - "forced": "Таны байгууллага текст шалгалтыг асаасан. Таныг бичих явцад бичсэн текст тань гарын үсэг болон ишлэсэн мессежгүйгээр шалгуулахаар {checkers} руу очно. Гарын үсэг зурах эсвэл шифрлэх мессежээс юу ч илгээгдэхгүй.", + "on": "Текст шалгалт асаалттай байна. Таныг бичих явцад бичсэн текст тань гарын үсэг болон ишлэсэн мессежгүйгээр шалгуулахаар {checkers} руу очно. Мессежийг гарын үсэг зурах эсвэл шифрлэхээр тохируулсан үед юу ч илгээгдэхгүй; түүнээс өмнө бичсэн зүйл тань аль хэдийн шалгагдсан байж болно.", + "forced": "Таны байгууллага текст шалгалтыг асаасан. Таныг бичих явцад бичсэн текст тань гарын үсэг болон ишлэсэн мессежгүйгээр шалгуулахаар {checkers} руу очно. Мессежийг гарын үсэг зурах эсвэл шифрлэхээр тохируулсан үед юу ч илгээгдэхгүй; түүнээс өмнө бичсэн зүйл тань аль хэдийн шалгагдсан байж болно.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Текст шалгалт", "description": "Бичих явцад зөв бичих болон хэл зүйн алдааны доогуур зурж, засах санал өгнө.", "enable": "Бичих явцад зөв бичих дүрэм болон хэл зүйг шалгах", - "enable_description": "Таны бичсэн текст текст шалгалтын нэмэлт болон түүний ашигладаг үйлчилгээ рүү очно. Таны гарын үсэг болон ишлэсэн мессежүүд орохгүй бөгөөд гарын үсэгтэй эсвэл шифрлэгдсэн мессежийг хэзээ ч шалгахгүй.", + "enable_description": "Таны бичсэн текст гарын үсэг болон ишлэсэн мессежгүйгээр текст шалгалтын нэмэлт болон түүний ашигладаг үйлчилгээ рүү очно. Мессежийг гарын үсэг зурах эсвэл шифрлэхээр тохируулсан үед юу ч илгээгдэхгүй.", "no_checker": "Текст шалгалтын нэмэлт суулгаагүй байна. Админаасаа нэгийг нэмэхийг хүснэ үү.", "plugin_description": "Таны бичсэнийг {plugin} руу илгээнэ.", "writing_language": "Бичих хэл", diff --git a/locales/nb/common.json b/locales/nb/common.json index c4d628ca5..d8bdee314 100644 --- a/locales/nb/common.json +++ b/locales/nb/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Sjekke det du skriver", - "body": "Leser det du skriver i redigeringsvinduet mens du skriver, og kan sende det til tjenesten det bruker. Bare når korrekturen er slått på, og aldri for en melding du signerer eller krypterer." + "body": "Leser det du skriver i redigeringsvinduet mens du skriver, og kan sende det til tjenesten det bruker. Bare når korrekturen er slått på, og ingenting så lenge en melding er satt til å bli signert eller kryptert. Tekst du skrev før du slo det på, kan allerede være sjekket." }, "email_forward": { "title": "Videresende e-posten din", @@ -6394,7 +6394,7 @@ "title": "Korrektur", "required": "Påkrevd av organisasjonen din", "check_message": "Sjekk denne meldingen", - "notice": "Mens du skriver, sendes teksten til {checkers} for å bli sjekket. Signaturen din og den siterte meldingen holdes utenfor, og ingenting sendes for en melding du signerer eller krypterer.", + "notice": "Mens du skriver, sendes teksten til {checkers} for å bli sjekket. Signaturen din og den siterte meldingen holdes utenfor. Ingenting sendes så lenge en melding er satt til å bli signert eller kryptert; det du skrev før du slo det på, kan allerede være sjekket.", "turn_on": "Slå på korrektur", "paused_protected": "Ingenting sjekkes så lenge denne meldingen er satt til å bli signert eller kryptert.", "paused_unknown": "{plugin} kan signere eller kryptere meldinger uten å vise det her, så ingenting sjekkes mens det er på.", @@ -6405,8 +6405,8 @@ "shortcuts": "Neste problem: {next}. Forrige: {previous}." }, "notice": { - "on": "Korrektur er på. Mens du skriver, sendes teksten til {checkers} for å bli sjekket, uten signaturen din eller den siterte meldingen. Ingenting sendes for en melding du signerer eller krypterer.", - "forced": "Organisasjonen din har slått på korrektur. Mens du skriver, sendes teksten til {checkers} for å bli sjekket, uten signaturen din eller den siterte meldingen. Ingenting sendes for en melding du signerer eller krypterer.", + "on": "Korrektur er på. Mens du skriver, sendes teksten til {checkers} for å bli sjekket, uten signaturen din eller den siterte meldingen. Ingenting sendes så lenge en melding er satt til å bli signert eller kryptert; det du skrev før det, kan allerede være sjekket.", + "forced": "Organisasjonen din har slått på korrektur. Mens du skriver, sendes teksten til {checkers} for å bli sjekket, uten signaturen din eller den siterte meldingen. Ingenting sendes så lenge en melding er satt til å bli signert eller kryptert; det du skrev før det, kan allerede være sjekket.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Korrektur", "description": "Understrek stave- og grammatikkfeil mens du skriver, med forslag til å rette dem.", "enable": "Sjekk stavemåte og grammatikk mens jeg skriver", - "enable_description": "Teksten du skriver, sendes til korrekturtillegget og tjenesten det bruker. Signaturen din og siterte meldinger holdes utenfor, og signerte eller krypterte meldinger sjekkes aldri.", + "enable_description": "Teksten du skriver, sendes til korrekturtillegget og tjenesten det bruker, uten signaturen din eller siterte meldinger. Ingenting sendes så lenge en melding er satt til å bli signert eller kryptert.", "no_checker": "Ingen korrekturtillegg er installert. Be administratoren din om å legge til et.", "plugin_description": "Sender det du skriver, til {plugin}.", "writing_language": "Skrivespråk", diff --git a/locales/nl/common.json b/locales/nl/common.json index 29df9aebc..102591e69 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Controleren wat u schrijft", - "body": "Leest wat u in de editor schrijft terwijl u typt en kan het naar de dienst sturen die het gebruikt. Alleen als de controle aan staat, en nooit voor een bericht dat u ondertekent of versleutelt." + "body": "Leest wat u in de editor schrijft terwijl u typt en kan het naar de dienst sturen die het gebruikt. Alleen als de controle aan staat, en niets zolang een bericht is ingesteld om te worden ondertekend of versleuteld. Tekst die u schreef voordat u dat aanzette, is mogelijk al gecontroleerd." }, "email_forward": { "title": "Je e-mail doorsturen", @@ -6394,7 +6394,7 @@ "title": "Tekstcontrole", "required": "Verplicht gesteld door je organisatie", "check_message": "Dit bericht controleren", - "notice": "Terwijl je typt, gaat de tekst die je schrijft naar {checkers} om te worden gecontroleerd. Je handtekening en het geciteerde bericht worden overgeslagen, en er wordt niets verstuurd voor een bericht dat je ondertekent of versleutelt.", + "notice": "Terwijl je typt, gaat de tekst die je schrijft naar {checkers} om te worden gecontroleerd. Je handtekening en het geciteerde bericht worden overgeslagen. Er wordt niets verstuurd zolang een bericht is ingesteld om te worden ondertekend of versleuteld; wat je schreef voordat je dat aanzette, is mogelijk al gecontroleerd.", "turn_on": "Tekstcontrole aanzetten", "paused_protected": "Er wordt niets gecontroleerd zolang dit bericht wordt ondertekend of versleuteld.", "paused_unknown": "{plugin} kan berichten ondertekenen of versleutelen zonder dat hier te tonen, dus er wordt niets gecontroleerd zolang het aan staat.", @@ -6405,8 +6405,8 @@ "shortcuts": "Volgend probleem: {next}. Vorige: {previous}." }, "notice": { - "on": "Tekstcontrole staat aan. Terwijl je typt, gaat de tekst die je schrijft naar {checkers} om te worden gecontroleerd, zonder je handtekening of het geciteerde bericht. Er wordt niets verstuurd voor een bericht dat je ondertekent of versleutelt.", - "forced": "Je organisatie heeft tekstcontrole aangezet. Terwijl je typt, gaat de tekst die je schrijft naar {checkers} om te worden gecontroleerd, zonder je handtekening of het geciteerde bericht. Er wordt niets verstuurd voor een bericht dat je ondertekent of versleutelt.", + "on": "Tekstcontrole staat aan. Terwijl je typt, gaat de tekst die je schrijft naar {checkers} om te worden gecontroleerd, zonder je handtekening of het geciteerde bericht. Er wordt niets verstuurd zolang een bericht is ingesteld om te worden ondertekend of versleuteld; wat je daarvoor schreef, is mogelijk al gecontroleerd.", + "forced": "Je organisatie heeft tekstcontrole aangezet. Terwijl je typt, gaat de tekst die je schrijft naar {checkers} om te worden gecontroleerd, zonder je handtekening of het geciteerde bericht. Er wordt niets verstuurd zolang een bericht is ingesteld om te worden ondertekend of versleuteld; wat je daarvoor schreef, is mogelijk al gecontroleerd.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Tekstcontrole", "description": "Spel- en grammaticafouten onderstrepen terwijl je schrijft, met suggesties om ze te verbeteren.", "enable": "Spelling en grammatica controleren terwijl ik typ", - "enable_description": "De tekst die je schrijft gaat naar de add-on voor tekstcontrole en de dienst die die gebruikt. Je handtekening en geciteerde berichten worden overgeslagen, en ondertekende of versleutelde berichten worden nooit gecontroleerd.", + "enable_description": "De tekst die je schrijft gaat naar de add-on voor tekstcontrole en de dienst die die gebruikt, zonder je handtekening of geciteerde berichten. Er wordt niets verstuurd zolang een bericht is ingesteld om te worden ondertekend of versleuteld.", "no_checker": "Er is geen add-on voor tekstcontrole geïnstalleerd. Vraag je beheerder er een toe te voegen.", "plugin_description": "Stuurt wat je schrijft naar {plugin}.", "writing_language": "Schrijftaal", diff --git a/locales/pl/common.json b/locales/pl/common.json index 268df44d5..c7180028c 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Sprawdzać to, co piszesz", - "body": "Czyta to, co piszesz w edytorze, w trakcie pisania i może wysyłać to do usługi, z której korzysta. Tylko gdy sprawdzanie jest włączone, i nigdy dla wiadomości, którą podpisujesz lub szyfrujesz." + "body": "Czyta to, co piszesz w edytorze, w trakcie pisania i może wysyłać to do usługi, z której korzysta. Tylko gdy sprawdzanie jest włączone, i nic, dopóki wiadomość ma zostać podpisana lub zaszyfrowana. Tekst napisany przed włączeniem tej opcji mógł już zostać sprawdzony." }, "email_forward": { "title": "Przekazywanie Twojej poczty", @@ -6394,7 +6394,7 @@ "title": "Korekta", "required": "Wymagane przez Twoją organizację", "check_message": "Sprawdź tę wiadomość", - "notice": "Podczas pisania Twój tekst trafia do {checkers} do sprawdzenia. Twój podpis i cytowana wiadomość są pomijane, a z wiadomości, którą podpisujesz lub szyfrujesz, nic nie jest wysyłane.", + "notice": "Podczas pisania Twój tekst trafia do {checkers} do sprawdzenia. Twój podpis i cytowana wiadomość są pomijane. Nic nie jest wysyłane, dopóki wiadomość ma zostać podpisana lub zaszyfrowana; to, co napisano przed włączeniem tej opcji, mogło już zostać sprawdzone.", "turn_on": "Włącz korektę", "paused_protected": "Nic nie jest sprawdzane, dopóki ta wiadomość ma zostać podpisana lub zaszyfrowana.", "paused_unknown": "{plugin} może podpisywać lub szyfrować wiadomości bez pokazywania tego tutaj, więc nic nie jest sprawdzane, dopóki jest włączony.", @@ -6405,8 +6405,8 @@ "shortcuts": "Następny problem: {next}. Poprzedni: {previous}." }, "notice": { - "on": "Korekta jest włączona. Podczas pisania Twój tekst trafia do {checkers} do sprawdzenia, bez Twojego podpisu i cytowanej wiadomości. Z wiadomości, którą podpisujesz lub szyfrujesz, nic nie jest wysyłane.", - "forced": "Twoja organizacja włączyła korektę. Podczas pisania Twój tekst trafia do {checkers} do sprawdzenia, bez Twojego podpisu i cytowanej wiadomości. Z wiadomości, którą podpisujesz lub szyfrujesz, nic nie jest wysyłane.", + "on": "Korekta jest włączona. Podczas pisania Twój tekst trafia do {checkers} do sprawdzenia, bez Twojego podpisu i cytowanej wiadomości. Nic nie jest wysyłane, dopóki wiadomość ma zostać podpisana lub zaszyfrowana; to, co napisano wcześniej, mogło już zostać sprawdzone.", + "forced": "Twoja organizacja włączyła korektę. Podczas pisania Twój tekst trafia do {checkers} do sprawdzenia, bez Twojego podpisu i cytowanej wiadomości. Nic nie jest wysyłane, dopóki wiadomość ma zostać podpisana lub zaszyfrowana; to, co napisano wcześniej, mogło już zostać sprawdzone.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Korekta", "description": "Podkreśla błędy pisowni i gramatyki podczas pisania i podpowiada, jak je poprawić.", "enable": "Sprawdzaj pisownię i gramatykę podczas pisania", - "enable_description": "Tekst, który piszesz, trafia do dodatku do korekty i usługi, z której korzysta. Twój podpis i cytowane wiadomości są pomijane, a podpisane lub zaszyfrowane wiadomości nigdy nie są sprawdzane.", + "enable_description": "Tekst, który piszesz, trafia do dodatku do korekty i usługi, z której korzysta, bez Twojego podpisu i cytowanych wiadomości. Nic nie jest wysyłane, dopóki wiadomość ma zostać podpisana lub zaszyfrowana.", "no_checker": "Nie zainstalowano żadnego dodatku do korekty. Poproś administratora o dodanie.", "plugin_description": "Wysyła to, co piszesz, do {plugin}.", "writing_language": "Język pisania", diff --git a/locales/pt/common.json b/locales/pt/common.json index 38e7f676d..686765fdc 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Verificar o que escreve", - "body": "Lê o que escreve no editor enquanto digita e pode enviá-lo ao serviço que usa. Só quando a revisão está ativada, e nunca numa mensagem que assine ou cifre." + "body": "Lê o que escreve no editor enquanto digita e pode enviá-lo ao serviço que usa. Só quando a revisão está ativada, e nada enquanto uma mensagem estiver configurada para ser assinada ou criptografada. O texto que escreveu antes de ativar isso pode já ter sido verificado." }, "email_forward": { "title": "Encaminhar seus e-mails", @@ -6394,7 +6394,7 @@ "title": "Revisão", "required": "Exigido pela sua organização", "check_message": "Verificar esta mensagem", - "notice": "Enquanto digita, o texto que escreve é enviado para {checkers} para ser verificado. Sua assinatura e a mensagem citada ficam de fora, e nada é enviado de uma mensagem que você assine ou criptografe.", + "notice": "Enquanto digita, o texto que escreve é enviado para {checkers} para ser verificado. Sua assinatura e a mensagem citada ficam de fora. Nada é enviado enquanto uma mensagem estiver configurada para ser assinada ou criptografada; o que você escreveu antes de ativar isso pode já ter sido verificado.", "turn_on": "Ativar a revisão", "paused_protected": "Nada é verificado enquanto esta mensagem estiver configurada para ser assinada ou criptografada.", "paused_unknown": "{plugin} pode assinar ou criptografar mensagens sem mostrar isso aqui, por isso nada é verificado enquanto ele estiver ativo.", @@ -6405,8 +6405,8 @@ "shortcuts": "Próximo problema: {next}. Anterior: {previous}." }, "notice": { - "on": "A revisão está ativada. Enquanto digita, o texto que escreve é enviado para {checkers} para ser verificado, sem sua assinatura nem a mensagem citada. Nada é enviado de uma mensagem que você assine ou criptografe.", - "forced": "Sua organização ativou a revisão. Enquanto digita, o texto que escreve é enviado para {checkers} para ser verificado, sem sua assinatura nem a mensagem citada. Nada é enviado de uma mensagem que você assine ou criptografe.", + "on": "A revisão está ativada. Enquanto digita, o texto que escreve é enviado para {checkers} para ser verificado, sem sua assinatura nem a mensagem citada. Nada é enviado enquanto uma mensagem estiver configurada para ser assinada ou criptografada; o que você escreveu antes pode já ter sido verificado.", + "forced": "Sua organização ativou a revisão. Enquanto digita, o texto que escreve é enviado para {checkers} para ser verificado, sem sua assinatura nem a mensagem citada. Nada é enviado enquanto uma mensagem estiver configurada para ser assinada ou criptografada; o que você escreveu antes pode já ter sido verificado.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Revisão", "description": "Sublinha erros de ortografia e gramática enquanto você escreve, com sugestões para corrigi-los.", "enable": "Verificar ortografia e gramática enquanto digito", - "enable_description": "O texto que você escreve é enviado para o extra de revisão e para o serviço que ele usa. Sua assinatura e as mensagens citadas ficam de fora, e mensagens assinadas ou criptografadas nunca são verificadas.", + "enable_description": "O texto que você escreve é enviado para o extra de revisão e para o serviço que ele usa, sem sua assinatura nem as mensagens citadas. Nada é enviado enquanto uma mensagem estiver configurada para ser assinada ou criptografada.", "no_checker": "Nenhum extra de revisão está instalado. Peça ao administrador para adicionar um.", "plugin_description": "Envia o que você escreve para {plugin}.", "writing_language": "Idioma de escrita", diff --git a/locales/ro/common.json b/locales/ro/common.json index 359a5d870..2aaa511e0 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Verificarea a ceea ce scrieți", - "body": "Citește ce scrieți în editor în timp ce tastați și poate trimite textul serviciului pe care îl folosește. Doar când verificarea este pornită și niciodată pentru un mesaj pe care îl semnați sau îl criptați." + "body": "Citește ce scrieți în editor în timp ce tastați și poate trimite textul serviciului pe care îl folosește. Doar când verificarea este pornită și nimic cât timp un mesaj este setat să fie semnat sau criptat. Textul scris înainte de a activa asta poate fi deja verificat." }, "email_forward": { "title": "Redirecționarea mesajelor dvs.", @@ -6394,7 +6394,7 @@ "title": "Verificare text", "required": "Cerută de organizația dvs.", "check_message": "Verifică acest mesaj", - "notice": "În timp ce tastați, textul scris ajunge la {checkers} pentru verificare. Semnătura și mesajul citat sunt lăsate deoparte, iar pentru un mesaj pe care îl semnați sau îl criptați nu se trimite nimic.", + "notice": "În timp ce tastați, textul scris ajunge la {checkers} pentru verificare. Semnătura și mesajul citat sunt lăsate deoparte. Nu se trimite nimic cât timp un mesaj este setat să fie semnat sau criptat; ce ați scris înainte de a activa asta poate fi deja verificat.", "turn_on": "Pornește verificarea textului", "paused_protected": "Nu se verifică nimic cât timp acest mesaj este setat să fie semnat sau criptat.", "paused_unknown": "{plugin} poate semna sau cripta mesaje fără să arate asta aici, așa că nu se verifică nimic cât timp este pornit.", @@ -6405,8 +6405,8 @@ "shortcuts": "Problema următoare: {next}. Anterioara: {previous}." }, "notice": { - "on": "Verificarea textului este pornită. În timp ce tastați, textul scris ajunge la {checkers} pentru verificare, fără semnătură și fără mesajul citat. Pentru un mesaj pe care îl semnați sau îl criptați nu se trimite nimic.", - "forced": "Organizația dvs. a pornit verificarea textului. În timp ce tastați, textul scris ajunge la {checkers} pentru verificare, fără semnătură și fără mesajul citat. Pentru un mesaj pe care îl semnați sau îl criptați nu se trimite nimic.", + "on": "Verificarea textului este pornită. În timp ce tastați, textul scris ajunge la {checkers} pentru verificare, fără semnătură și fără mesajul citat. Nu se trimite nimic cât timp un mesaj este setat să fie semnat sau criptat; ce ați scris înainte poate fi deja verificat.", + "forced": "Organizația dvs. a pornit verificarea textului. În timp ce tastați, textul scris ajunge la {checkers} pentru verificare, fără semnătură și fără mesajul citat. Nu se trimite nimic cât timp un mesaj este setat să fie semnat sau criptat; ce ați scris înainte poate fi deja verificat.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Verificare text", "description": "Subliniază greșelile de ortografie și gramatică în timp ce scrieți, cu sugestii de corectare.", "enable": "Verifică ortografia și gramatica în timp ce tastez", - "enable_description": "Textul pe care îl scrieți ajunge la suplimentul de verificare și la serviciul pe care îl folosește. Semnătura și mesajele citate sunt lăsate deoparte, iar mesajele semnate sau criptate nu sunt verificate niciodată.", + "enable_description": "Textul pe care îl scrieți ajunge la suplimentul de verificare și la serviciul pe care îl folosește, fără semnătură și fără mesajele citate. Nu se trimite nimic cât timp un mesaj este setat să fie semnat sau criptat.", "no_checker": "Nu este instalat niciun supliment de verificare a textului. Cereți administratorului să adauge unul.", "plugin_description": "Trimite ce scrieți către {plugin}.", "writing_language": "Limba de scriere", diff --git a/locales/ru/common.json b/locales/ru/common.json index 0aa1b8634..a4e9e1a03 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Проверять то, что вы пишете", - "body": "Читает то, что вы пишете в редакторе, во время ввода и может отправлять это в сервис, который использует. Только когда проверка включена, и никогда для письма, которое вы подписываете или шифруете." + "body": "Читает то, что вы пишете в редакторе, во время ввода и может отправлять это в сервис, который использует. Только когда проверка включена, и ничего, пока письмо настроено на подпись или шифрование. Текст, написанный до включения этой настройки, мог уже быть проверен." }, "email_forward": { "title": "Пересылать вашу почту", @@ -6394,7 +6394,7 @@ "title": "Проверка текста", "required": "Требуется вашей организацией", "check_message": "Проверить это письмо", - "notice": "Пока вы печатаете, ваш текст отправляется на проверку в {checkers}. Ваша подпись и цитируемое письмо не отправляются, а для письма, которое вы подписываете или шифруете, ничего не отправляется.", + "notice": "Пока вы печатаете, ваш текст отправляется на проверку в {checkers}. Ваша подпись и цитируемое письмо не отправляются. Пока письмо настроено на подпись или шифрование, ничего не отправляется; то, что вы написали до включения этой настройки, могло уже быть проверено.", "turn_on": "Включить проверку текста", "paused_protected": "Пока это письмо настроено на подпись или шифрование, ничего не проверяется.", "paused_unknown": "{plugin} может подписывать или шифровать письма, не показывая этого здесь, поэтому, пока он включён, ничего не проверяется.", @@ -6405,8 +6405,8 @@ "shortcuts": "Следующее замечание: {next}. Предыдущее: {previous}." }, "notice": { - "on": "Проверка текста включена. Пока вы печатаете, ваш текст отправляется на проверку в {checkers}, без вашей подписи и цитируемого письма. Для письма, которое вы подписываете или шифруете, ничего не отправляется.", - "forced": "Ваша организация включила проверку текста. Пока вы печатаете, ваш текст отправляется на проверку в {checkers}, без вашей подписи и цитируемого письма. Для письма, которое вы подписываете или шифруете, ничего не отправляется.", + "on": "Проверка текста включена. Пока вы печатаете, ваш текст отправляется на проверку в {checkers}, без вашей подписи и цитируемого письма. Пока письмо настроено на подпись или шифрование, ничего не отправляется; то, что вы написали до этого, могло уже быть проверено.", + "forced": "Ваша организация включила проверку текста. Пока вы печатаете, ваш текст отправляется на проверку в {checkers}, без вашей подписи и цитируемого письма. Пока письмо настроено на подпись или шифрование, ничего не отправляется; то, что вы написали до этого, могло уже быть проверено.", "ok": "ОК" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Проверка текста", "description": "Подчёркивает орфографические и грамматические ошибки при наборе и предлагает исправления.", "enable": "Проверять орфографию и грамматику при наборе", - "enable_description": "Текст, который вы пишете, отправляется в дополнение для проверки текста и в сервис, который оно использует. Ваша подпись и цитируемые письма не отправляются, а подписанные или зашифрованные письма никогда не проверяются.", + "enable_description": "Текст, который вы пишете, отправляется в дополнение для проверки текста и в сервис, который оно использует, без вашей подписи и цитируемых писем. Пока письмо настроено на подпись или шифрование, ничего не отправляется.", "no_checker": "Не установлено ни одного дополнения для проверки текста. Попросите администратора добавить его.", "plugin_description": "Отправляет то, что вы пишете, в {plugin}.", "writing_language": "Язык письма", diff --git a/locales/sk/common.json b/locales/sk/common.json index af6b2da32..bafe03cd7 100644 --- a/locales/sk/common.json +++ b/locales/sk/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Kontrolovať, čo píšete", - "body": "Počas písania číta, čo píšete v editore, a môže to posielať službe, ktorú používa. Len keď je kontrola zapnutá, a nikdy pri správe, ktorú podpisujete alebo šifrujete." + "body": "Počas písania číta, čo píšete v editore, a môže to posielať službe, ktorú používa. Len keď je kontrola zapnutá, a nič, kým je správa nastavená na podpísanie alebo zašifrovanie. Text, ktorý ste napísali predtým, ako ste to zapli, už mohol byť skontrolovaný." }, "email_forward": { "title": "Preposielať vašu poštu", @@ -6394,7 +6394,7 @@ "title": "Kontrola textu", "required": "Vyžaduje vaša organizácia", "check_message": "Skontrolovať túto správu", - "notice": "Počas písania sa váš text posiela na kontrolu službe {checkers}. Váš podpis a citovaná správa sa vynechávajú a pri správe, ktorú podpisujete alebo šifrujete, sa neposiela nič.", + "notice": "Počas písania sa váš text posiela na kontrolu službe {checkers}. Váš podpis a citovaná správa sa vynechávajú. Kým je správa nastavená na podpísanie alebo zašifrovanie, neposiela sa nič; to, čo ste napísali predtým, ako ste to zapli, už mohlo byť skontrolované.", "turn_on": "Zapnúť kontrolu textu", "paused_protected": "Kým je táto správa nastavená na podpísanie alebo zašifrovanie, nič sa nekontroluje.", "paused_unknown": "{plugin} môže podpisovať alebo šifrovať správy bez toho, aby sa to tu zobrazilo, preto sa nič nekontroluje, kým je zapnutý.", @@ -6405,8 +6405,8 @@ "shortcuts": "Ďalší problém: {next}. Predchádzajúci: {previous}." }, "notice": { - "on": "Kontrola textu je zapnutá. Počas písania sa váš text posiela na kontrolu službe {checkers}, bez vášho podpisu a citovanej správy. Pri správe, ktorú podpisujete alebo šifrujete, sa neposiela nič.", - "forced": "Vaša organizácia zapla kontrolu textu. Počas písania sa váš text posiela na kontrolu službe {checkers}, bez vášho podpisu a citovanej správy. Pri správe, ktorú podpisujete alebo šifrujete, sa neposiela nič.", + "on": "Kontrola textu je zapnutá. Počas písania sa váš text posiela na kontrolu službe {checkers}, bez vášho podpisu a citovanej správy. Kým je správa nastavená na podpísanie alebo zašifrovanie, neposiela sa nič; to, čo ste napísali predtým, už mohlo byť skontrolované.", + "forced": "Vaša organizácia zapla kontrolu textu. Počas písania sa váš text posiela na kontrolu službe {checkers}, bez vášho podpisu a citovanej správy. Kým je správa nastavená na podpísanie alebo zašifrovanie, neposiela sa nič; to, čo ste napísali predtým, už mohlo byť skontrolované.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Kontrola textu", "description": "Podčiarkuje pravopisné a gramatické chyby počas písania a ponúka návrhy na opravu.", "enable": "Kontrolovať pravopis a gramatiku počas písania", - "enable_description": "Text, ktorý píšete, sa posiela doplnku na kontrolu textu a službe, ktorú používa. Váš podpis a citované správy sa vynechávajú a podpísané alebo šifrované správy sa nikdy nekontrolujú.", + "enable_description": "Text, ktorý píšete, sa posiela doplnku na kontrolu textu a službe, ktorú používa, bez vášho podpisu a citovaných správ. Kým je správa nastavená na podpísanie alebo zašifrovanie, neposiela sa nič.", "no_checker": "Nie je nainštalovaný žiadny doplnok na kontrolu textu. Požiadajte správcu, aby nejaký pridal.", "plugin_description": "Posiela to, čo píšete, do {plugin}.", "writing_language": "Jazyk písania", diff --git a/locales/th/common.json b/locales/th/common.json index 4b4994fad..0fae969f6 100644 --- a/locales/th/common.json +++ b/locales/th/common.json @@ -3005,7 +3005,7 @@ }, "compose_check": { "title": "ตรวจสิ่งที่คุณเขียน", - "body": "อ่านสิ่งที่คุณเขียนในหน้าเขียนข้อความขณะพิมพ์ และอาจส่งไปยังบริการที่ใช้ เฉพาะเมื่อเปิดการตรวจทาน และไม่ทำกับข้อความที่คุณลงนามหรือเข้ารหัส" + "body": "อ่านสิ่งที่คุณเขียนในหน้าเขียนข้อความขณะพิมพ์ และอาจส่งไปยังบริการที่ใช้ เฉพาะเมื่อเปิดการตรวจทาน และไม่ส่งอะไรเลยขณะที่ข้อความตั้งค่าให้ลงนามหรือเข้ารหัส ข้อความที่คุณเขียนก่อนเปิดตัวเลือกนั้นอาจถูกตรวจไปแล้ว" }, "email_forward": { "title": "ส่งต่ออีเมลของคุณ", @@ -6402,7 +6402,7 @@ "title": "การตรวจทาน", "required": "องค์กรของคุณกำหนดให้เปิด", "check_message": "ตรวจข้อความนี้", - "notice": "ขณะที่คุณพิมพ์ ข้อความที่คุณเขียนจะถูกส่งไปยัง {checkers} เพื่อตรวจ ลายเซ็นและข้อความที่อ้างถึงจะไม่ถูกส่ง และจะไม่ส่งอะไรเลยสำหรับข้อความที่คุณลงนามหรือเข้ารหัส", + "notice": "ขณะที่คุณพิมพ์ ข้อความที่คุณเขียนจะถูกส่งไปยัง {checkers} เพื่อตรวจ ลายเซ็นและข้อความที่อ้างถึงจะไม่ถูกส่ง จะไม่ส่งอะไรเลยขณะที่ข้อความตั้งค่าให้ลงนามหรือเข้ารหัส แต่สิ่งที่คุณเขียนก่อนเปิดตัวเลือกนั้นอาจถูกตรวจไปแล้ว", "turn_on": "เปิดการตรวจทาน", "paused_protected": "จะไม่มีการตรวจใด ๆ ขณะที่ข้อความนี้ตั้งค่าให้ลงนามหรือเข้ารหัส", "paused_unknown": "{plugin} สามารถลงนามหรือเข้ารหัสข้อความโดยไม่แสดงที่นี่ จึงไม่มีการตรวจใด ๆ ขณะที่เปิดอยู่", @@ -6413,8 +6413,8 @@ "shortcuts": "ปัญหาถัดไป: {next} ก่อนหน้า: {previous}" }, "notice": { - "on": "เปิดการตรวจทานอยู่ ขณะที่คุณพิมพ์ ข้อความที่คุณเขียนจะถูกส่งไปยัง {checkers} เพื่อตรวจ โดยไม่รวมลายเซ็นและข้อความที่อ้างถึง จะไม่ส่งอะไรเลยสำหรับข้อความที่คุณลงนามหรือเข้ารหัส", - "forced": "องค์กรของคุณเปิดการตรวจทานไว้ ขณะที่คุณพิมพ์ ข้อความที่คุณเขียนจะถูกส่งไปยัง {checkers} เพื่อตรวจ โดยไม่รวมลายเซ็นและข้อความที่อ้างถึง จะไม่ส่งอะไรเลยสำหรับข้อความที่คุณลงนามหรือเข้ารหัส", + "on": "เปิดการตรวจทานอยู่ ขณะที่คุณพิมพ์ ข้อความที่คุณเขียนจะถูกส่งไปยัง {checkers} เพื่อตรวจ โดยไม่รวมลายเซ็นและข้อความที่อ้างถึง จะไม่ส่งอะไรเลยขณะที่ข้อความตั้งค่าให้ลงนามหรือเข้ารหัส แต่สิ่งที่คุณเขียนก่อนหน้านั้นอาจถูกตรวจไปแล้ว", + "forced": "องค์กรของคุณเปิดการตรวจทานไว้ ขณะที่คุณพิมพ์ ข้อความที่คุณเขียนจะถูกส่งไปยัง {checkers} เพื่อตรวจ โดยไม่รวมลายเซ็นและข้อความที่อ้างถึง จะไม่ส่งอะไรเลยขณะที่ข้อความตั้งค่าให้ลงนามหรือเข้ารหัส แต่สิ่งที่คุณเขียนก่อนหน้านั้นอาจถูกตรวจไปแล้ว", "ok": "ตกลง" }, "send_warning": { @@ -6427,7 +6427,7 @@ "title": "การตรวจทาน", "description": "ขีดเส้นใต้คำที่สะกดผิดและไวยากรณ์ที่ผิดขณะเขียน พร้อมคำแนะนำในการแก้ไข", "enable": "ตรวจการสะกดและไวยากรณ์ขณะที่ฉันพิมพ์", - "enable_description": "ข้อความที่คุณเขียนจะถูกส่งไปยังส่วนเสริมการตรวจทานและบริการที่ส่วนเสริมนั้นใช้ ลายเซ็นและข้อความที่อ้างถึงจะไม่ถูกส่ง และจะไม่ตรวจข้อความที่ลงนามหรือเข้ารหัสเลย", + "enable_description": "ข้อความที่คุณเขียนจะถูกส่งไปยังส่วนเสริมการตรวจทานและบริการที่ส่วนเสริมนั้นใช้ โดยไม่รวมลายเซ็นและข้อความที่อ้างถึง จะไม่ส่งอะไรเลยขณะที่ข้อความตั้งค่าให้ลงนามหรือเข้ารหัส", "no_checker": "ยังไม่ได้ติดตั้งส่วนเสริมการตรวจทาน กรุณาแจ้งผู้ดูแลระบบให้เพิ่ม", "plugin_description": "ส่งสิ่งที่คุณเขียนไปยัง {plugin}", "writing_language": "ภาษาที่ใช้เขียน", diff --git a/locales/tr/common.json b/locales/tr/common.json index 5a845e65b..5267b3049 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Yazdıklarınızı denetleme", - "body": "Düzenleyicide yazdıklarınızı siz yazarken okur ve kullandığı hizmete gönderebilir. Yalnızca yazım denetimi açıkken; imzaladığınız veya şifrelediğiniz bir iletide asla." + "body": "Düzenleyicide yazdıklarınızı siz yazarken okur ve kullandığı hizmete gönderebilir. Yalnızca yazım denetimi açıkken; bir ileti imzalanacak veya şifrelenecek şekilde ayarlıyken hiçbir şey gönderilmez. Bunu açmadan önce yazdıklarınız zaten denetlenmiş olabilir." }, "email_forward": { "title": "Postanızı iletme", @@ -6394,7 +6394,7 @@ "title": "Yazım denetimi", "required": "Kuruluşunuz tarafından zorunlu tutuluyor", "check_message": "Bu iletiyi denetle", - "notice": "Siz yazarken, yazdığınız metin denetlenmek üzere {checkers} hizmetine gider. İmzanız ve alıntılanan ileti dışarıda bırakılır; imzaladığınız veya şifrelediğiniz bir ileti için hiçbir şey gönderilmez.", + "notice": "Siz yazarken, yazdığınız metin denetlenmek üzere {checkers} hizmetine gider. İmzanız ve alıntılanan ileti dışarıda bırakılır. Bir ileti imzalanacak veya şifrelenecek şekilde ayarlıyken hiçbir şey gönderilmez; bunu açmadan önce yazdıklarınız zaten denetlenmiş olabilir.", "turn_on": "Yazım denetimini aç", "paused_protected": "Bu ileti imzalanacak veya şifrelenecek şekilde ayarlıyken hiçbir şey denetlenmez.", "paused_unknown": "{plugin} iletileri burada göstermeden imzalayabilir veya şifreleyebilir, bu yüzden açıkken hiçbir şey denetlenmez.", @@ -6405,8 +6405,8 @@ "shortcuts": "Sonraki sorun: {next}. Önceki: {previous}." }, "notice": { - "on": "Yazım denetimi açık. Siz yazarken, yazdığınız metin imzanız ve alıntılanan ileti olmadan denetlenmek üzere {checkers} hizmetine gider. İmzaladığınız veya şifrelediğiniz bir ileti için hiçbir şey gönderilmez.", - "forced": "Kuruluşunuz yazım denetimini açtı. Siz yazarken, yazdığınız metin imzanız ve alıntılanan ileti olmadan denetlenmek üzere {checkers} hizmetine gider. İmzaladığınız veya şifrelediğiniz bir ileti için hiçbir şey gönderilmez.", + "on": "Yazım denetimi açık. Siz yazarken, yazdığınız metin imzanız ve alıntılanan ileti olmadan denetlenmek üzere {checkers} hizmetine gider. Bir ileti imzalanacak veya şifrelenecek şekilde ayarlıyken hiçbir şey gönderilmez; bundan önce yazdıklarınız zaten denetlenmiş olabilir.", + "forced": "Kuruluşunuz yazım denetimini açtı. Siz yazarken, yazdığınız metin imzanız ve alıntılanan ileti olmadan denetlenmek üzere {checkers} hizmetine gider. Bir ileti imzalanacak veya şifrelenecek şekilde ayarlıyken hiçbir şey gönderilmez; bundan önce yazdıklarınız zaten denetlenmiş olabilir.", "ok": "Tamam" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Yazım denetimi", "description": "Siz yazarken yazım ve dil bilgisi hatalarının altını çizer ve düzeltme önerileri sunar.", "enable": "Yazarken yazım ve dil bilgisini denetle", - "enable_description": "Yazdığınız metin, yazım denetimi eklentisine ve onun kullandığı hizmete gider. İmzanız ve alıntılanan iletiler dışarıda bırakılır; imzalı veya şifreli iletiler hiçbir zaman denetlenmez.", + "enable_description": "Yazdığınız metin, imzanız ve alıntılanan iletiler olmadan yazım denetimi eklentisine ve onun kullandığı hizmete gider. Bir ileti imzalanacak veya şifrelenecek şekilde ayarlıyken hiçbir şey gönderilmez.", "no_checker": "Yüklü bir yazım denetimi eklentisi yok. Yöneticinizden bir tane eklemesini isteyin.", "plugin_description": "Yazdıklarınızı {plugin} eklentisine gönderir.", "writing_language": "Yazma dili", diff --git a/locales/uk/common.json b/locales/uk/common.json index 99b4749ec..1b6727da9 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "Перевіряти те, що ви пишете", - "body": "Читає те, що ви пишете в редакторі, під час набору і може надсилати це сервісу, який використовує. Лише коли перевірку ввімкнено, і ніколи для листа, який ви підписуєте чи шифруєте." + "body": "Читає те, що ви пишете в редакторі, під час набору і може надсилати це сервісу, який використовує. Лише коли перевірку ввімкнено, і нічого, поки лист налаштовано на підпис або шифрування. Текст, написаний до ввімкнення цього, міг уже бути перевірений." }, "email_forward": { "title": "Пересилати вашу пошту", @@ -6394,7 +6394,7 @@ "title": "Перевірка тексту", "required": "Вимагає ваша організація", "check_message": "Перевірити цей лист", - "notice": "Поки ви друкуєте, ваш текст надсилається на перевірку до {checkers}. Ваш підпис і цитований лист не надсилаються, а для листа, який ви підписуєте чи шифруєте, нічого не надсилається.", + "notice": "Поки ви друкуєте, ваш текст надсилається на перевірку до {checkers}. Ваш підпис і цитований лист не надсилаються. Поки лист налаштовано на підпис або шифрування, нічого не надсилається; те, що ви написали до ввімкнення цього, могло вже бути перевірено.", "turn_on": "Увімкнути перевірку тексту", "paused_protected": "Поки цей лист налаштовано на підпис або шифрування, нічого не перевіряється.", "paused_unknown": "{plugin} може підписувати або шифрувати листи, не показуючи цього тут, тому, поки його ввімкнено, нічого не перевіряється.", @@ -6405,8 +6405,8 @@ "shortcuts": "Наступне зауваження: {next}. Попереднє: {previous}." }, "notice": { - "on": "Перевірку тексту ввімкнено. Поки ви друкуєте, ваш текст надсилається на перевірку до {checkers}, без вашого підпису й цитованого листа. Для листа, який ви підписуєте чи шифруєте, нічого не надсилається.", - "forced": "Ваша організація ввімкнула перевірку тексту. Поки ви друкуєте, ваш текст надсилається на перевірку до {checkers}, без вашого підпису й цитованого листа. Для листа, який ви підписуєте чи шифруєте, нічого не надсилається.", + "on": "Перевірку тексту ввімкнено. Поки ви друкуєте, ваш текст надсилається на перевірку до {checkers}, без вашого підпису й цитованого листа. Поки лист налаштовано на підпис або шифрування, нічого не надсилається; те, що ви написали до цього, могло вже бути перевірено.", + "forced": "Ваша організація ввімкнула перевірку тексту. Поки ви друкуєте, ваш текст надсилається на перевірку до {checkers}, без вашого підпису й цитованого листа. Поки лист налаштовано на підпис або шифрування, нічого не надсилається; те, що ви написали до цього, могло вже бути перевірено.", "ok": "OK" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "Перевірка тексту", "description": "Підкреслює орфографічні та граматичні помилки під час набору й пропонує виправлення.", "enable": "Перевіряти орфографію та граматику під час набору", - "enable_description": "Текст, який ви пишете, надсилається до додатка перевірки тексту та сервісу, який він використовує. Ваш підпис і цитовані листи не надсилаються, а підписані чи зашифровані листи ніколи не перевіряються.", + "enable_description": "Текст, який ви пишете, надсилається до додатка перевірки тексту та сервісу, який він використовує, без вашого підпису й цитованих листів. Поки лист налаштовано на підпис або шифрування, нічого не надсилається.", "no_checker": "Не встановлено жодного додатка для перевірки тексту. Попросіть адміністратора додати його.", "plugin_description": "Надсилає те, що ви пишете, до {plugin}.", "writing_language": "Мова письма", diff --git a/locales/vi/common.json b/locales/vi/common.json index 3017ebaa0..172b5f7b3 100644 --- a/locales/vi/common.json +++ b/locales/vi/common.json @@ -6079,7 +6079,7 @@ }, "compose_check": { "title": "Kiểm tra những gì bạn viết", - "body": "Đọc những gì bạn viết trong trình soạn thảo khi bạn gõ và có thể gửi đến dịch vụ mà nó dùng. Chỉ khi bật soát lỗi, và không bao giờ với thư bạn ký hoặc mã hóa." + "body": "Đọc những gì bạn viết trong trình soạn thảo khi bạn gõ và có thể gửi đến dịch vụ mà nó dùng. Chỉ khi bật soát lỗi, và không gửi gì khi thư được đặt để ký hoặc mã hóa. Văn bản bạn viết trước khi bật tùy chọn đó có thể đã được kiểm tra." }, "email_forward": { "title": "Chuyển tiếp thư của bạn", @@ -6406,7 +6406,7 @@ "title": "Soát lỗi", "required": "Tổ chức của bạn yêu cầu bật", "check_message": "Kiểm tra thư này", - "notice": "Khi bạn gõ, văn bản bạn viết được gửi đến {checkers} để kiểm tra. Chữ ký của bạn và thư được trích dẫn không được gửi, và không có gì được gửi đi với thư bạn ký hoặc mã hóa.", + "notice": "Khi bạn gõ, văn bản bạn viết được gửi đến {checkers} để kiểm tra. Chữ ký của bạn và thư được trích dẫn không được gửi. Không có gì được gửi đi khi thư được đặt để ký hoặc mã hóa; những gì bạn viết trước khi bật tùy chọn đó có thể đã được kiểm tra.", "turn_on": "Bật soát lỗi", "paused_protected": "Không kiểm tra gì trong khi thư này được đặt để ký hoặc mã hóa.", "paused_unknown": "{plugin} có thể ký hoặc mã hóa thư mà không hiển thị ở đây, nên không kiểm tra gì khi nó đang bật.", @@ -6417,8 +6417,8 @@ "shortcuts": "Vấn đề tiếp theo: {next}. Trước đó: {previous}." }, "notice": { - "on": "Soát lỗi đang bật. Khi bạn gõ, văn bản bạn viết được gửi đến {checkers} để kiểm tra, không kèm chữ ký của bạn và thư được trích dẫn. Không có gì được gửi đi với thư bạn ký hoặc mã hóa.", - "forced": "Tổ chức của bạn đã bật soát lỗi. Khi bạn gõ, văn bản bạn viết được gửi đến {checkers} để kiểm tra, không kèm chữ ký của bạn và thư được trích dẫn. Không có gì được gửi đi với thư bạn ký hoặc mã hóa.", + "on": "Soát lỗi đang bật. Khi bạn gõ, văn bản bạn viết được gửi đến {checkers} để kiểm tra, không kèm chữ ký của bạn và thư được trích dẫn. Không có gì được gửi đi khi thư được đặt để ký hoặc mã hóa; những gì bạn viết trước đó có thể đã được kiểm tra.", + "forced": "Tổ chức của bạn đã bật soát lỗi. Khi bạn gõ, văn bản bạn viết được gửi đến {checkers} để kiểm tra, không kèm chữ ký của bạn và thư được trích dẫn. Không có gì được gửi đi khi thư được đặt để ký hoặc mã hóa; những gì bạn viết trước đó có thể đã được kiểm tra.", "ok": "OK" }, "send_warning": { @@ -6431,7 +6431,7 @@ "title": "Soát lỗi", "description": "Gạch dưới lỗi chính tả và ngữ pháp khi bạn viết, kèm gợi ý để sửa.", "enable": "Kiểm tra chính tả và ngữ pháp khi tôi gõ", - "enable_description": "Văn bản bạn viết được gửi đến tiện ích bổ sung soát lỗi và dịch vụ mà nó dùng. Chữ ký của bạn và thư được trích dẫn không được gửi, và thư đã ký hoặc mã hóa không bao giờ được kiểm tra.", + "enable_description": "Văn bản bạn viết được gửi đến tiện ích bổ sung soát lỗi và dịch vụ mà nó dùng, không kèm chữ ký của bạn và thư được trích dẫn. Không có gì được gửi đi khi thư được đặt để ký hoặc mã hóa.", "no_checker": "Chưa cài tiện ích bổ sung soát lỗi nào. Hãy nhờ quản trị viên thêm một tiện ích.", "plugin_description": "Gửi những gì bạn viết đến {plugin}.", "writing_language": "Ngôn ngữ viết", diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index b497de06f..aad5ddc52 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "檢查您寫的內容", - "body": "在您輸入時讀取您在編輯器中寫的內容,並可能傳送到它使用的服務。僅在校對開啟時進行,且絕不用於您簽署或加密的郵件。" + "body": "在您輸入時讀取您在編輯器中寫的內容,並可能傳送到它使用的服務。僅在校對開啟時進行,且郵件設定為簽署或加密期間不會傳送任何內容。在開啟該設定之前寫的文字可能已經被檢查過。" }, "email_forward": { "title": "轉寄您的郵件", @@ -6394,7 +6394,7 @@ "title": "校對", "required": "您的組織要求開啟", "check_message": "檢查此郵件", - "notice": "在您輸入時,您寫的文字會傳送到 {checkers} 進行檢查。您的簽名和引用的郵件不會傳送,您簽署或加密的郵件也不會傳送任何內容。", + "notice": "在您輸入時,您寫的文字會傳送到 {checkers} 進行檢查。您的簽名和引用的郵件不會傳送。郵件設定為簽署或加密期間不會傳送任何內容;但在開啟該設定之前寫的內容可能已經被檢查過。", "turn_on": "開啟校對", "paused_protected": "此郵件設定為簽署或加密期間,不會檢查任何內容。", "paused_unknown": "{plugin} 可以在此處不顯示的情況下簽署或加密郵件,因此在它開啟期間不會檢查任何內容。", @@ -6405,8 +6405,8 @@ "shortcuts": "下一個問題:{next}。上一個:{previous}。" }, "notice": { - "on": "校對已開啟。在您輸入時,您寫的文字會傳送到 {checkers} 進行檢查,不包括您的簽名和引用的郵件。您簽署或加密的郵件不會傳送任何內容。", - "forced": "您的組織已開啟校對。在您輸入時,您寫的文字會傳送到 {checkers} 進行檢查,不包括您的簽名和引用的郵件。您簽署或加密的郵件不會傳送任何內容。", + "on": "校對已開啟。在您輸入時,您寫的文字會傳送到 {checkers} 進行檢查,不包括您的簽名和引用的郵件。郵件設定為簽署或加密期間不會傳送任何內容;但在此之前寫的內容可能已經被檢查過。", + "forced": "您的組織已開啟校對。在您輸入時,您寫的文字會傳送到 {checkers} 進行檢查,不包括您的簽名和引用的郵件。郵件設定為簽署或加密期間不會傳送任何內容;但在此之前寫的內容可能已經被檢查過。", "ok": "確定" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "校對", "description": "在您書寫時為拼字和文法錯誤加上底線,並提供修改建議。", "enable": "輸入時檢查拼字和文法", - "enable_description": "您寫的文字會傳送到校對附加元件及其使用的服務。您的簽名和引用的郵件不會傳送,已簽署或加密的郵件絕不會被檢查。", + "enable_description": "您寫的文字會傳送到校對附加元件及其使用的服務,不包括您的簽名和引用的郵件。郵件設定為簽署或加密期間不會傳送任何內容。", "no_checker": "未安裝校對附加元件。請聯絡管理員新增。", "plugin_description": "將您寫的內容傳送到 {plugin}。", "writing_language": "書寫語言", diff --git a/locales/zh/common.json b/locales/zh/common.json index 957ba640a..0868cb4ba 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -6039,7 +6039,7 @@ }, "compose_check": { "title": "检查您写的内容", - "body": "在您输入时读取您在编辑器中写的内容,并可能将其发送到它使用的服务。仅在校对开启时进行,且绝不用于您签名或加密的邮件。" + "body": "在您输入时读取您在编辑器中写的内容,并可能将其发送到它使用的服务。仅在校对开启时进行,且邮件设置为签名或加密期间不会发送任何内容。在开启该设置之前写的文字可能已经被检查过。" }, "email_forward": { "title": "转发您的邮件", @@ -6394,7 +6394,7 @@ "title": "校对", "required": "您的组织要求开启", "check_message": "检查此邮件", - "notice": "在您输入时,您写的文字会发送到 {checkers} 进行检查。您的签名和引用的邮件不会发送,您签名或加密的邮件也不会发送任何内容。", + "notice": "在您输入时,您写的文字会发送到 {checkers} 进行检查。您的签名和引用的邮件不会发送。邮件设置为签名或加密期间不会发送任何内容;但在开启该设置之前写的内容可能已经被检查过。", "turn_on": "开启校对", "paused_protected": "此邮件设置为签名或加密期间,不会检查任何内容。", "paused_unknown": "{plugin} 可以在此处不显示的情况下签名或加密邮件,因此在它开启期间不会检查任何内容。", @@ -6405,8 +6405,8 @@ "shortcuts": "下一个问题:{next}。上一个:{previous}。" }, "notice": { - "on": "校对已开启。在您输入时,您写的文字会发送到 {checkers} 进行检查,不包括您的签名和引用的邮件。您签名或加密的邮件不会发送任何内容。", - "forced": "您的组织已开启校对。在您输入时,您写的文字会发送到 {checkers} 进行检查,不包括您的签名和引用的邮件。您签名或加密的邮件不会发送任何内容。", + "on": "校对已开启。在您输入时,您写的文字会发送到 {checkers} 进行检查,不包括您的签名和引用的邮件。邮件设置为签名或加密期间不会发送任何内容;但在此之前写的内容可能已经被检查过。", + "forced": "您的组织已开启校对。在您输入时,您写的文字会发送到 {checkers} 进行检查,不包括您的签名和引用的邮件。邮件设置为签名或加密期间不会发送任何内容;但在此之前写的内容可能已经被检查过。", "ok": "确定" }, "send_warning": { @@ -6419,7 +6419,7 @@ "title": "校对", "description": "在您书写时为拼写和语法错误添加下划线,并提供修改建议。", "enable": "输入时检查拼写和语法", - "enable_description": "您写的文字会发送到校对附加组件及其使用的服务。您的签名和引用的邮件不会发送,已签名或加密的邮件绝不会被检查。", + "enable_description": "您写的文字会发送到校对附加组件及其使用的服务,不包括您的签名和引用的邮件。邮件设置为签名或加密期间不会发送任何内容。", "no_checker": "未安装校对附加组件。请联系管理员添加。", "plugin_description": "将您写的内容发送到 {plugin}。", "writing_language": "书写语言", diff --git a/stores/__tests__/settings-store-plugin-hooks.test.ts b/stores/__tests__/settings-store-plugin-hooks.test.ts index f0a64283c..3db7a92be 100644 --- a/stores/__tests__/settings-store-plugin-hooks.test.ts +++ b/stores/__tests__/settings-store-plugin-hooks.test.ts @@ -55,6 +55,19 @@ describe('settings plugin hooks', () => { expect(after).not.toHaveBeenCalled(); }); + it('lets no plugin hold up turning proofreading off, overall or for one plugin (#1397)', async () => { + useSettingsStore.setState({ proofreading: true, proofreadingPlugins: {} }); + settingsHooks.onBeforeSettingChange.register('test', () => false); + useSettingsStore.getState().updateSetting('proofreading', false); + useSettingsStore.getState().updateSetting('proofreadingPlugins', { lt: false }); + expect(useSettingsStore.getState().proofreading).toBe(false); + expect(useSettingsStore.getState().proofreadingPlugins).toEqual({ lt: false }); + // Turning it on still asks. + useSettingsStore.getState().updateSetting('proofreading', true); + await flush(); + expect(useSettingsStore.getState().proofreading).toBe(false); + }); + it('applies synchronously when no plugin intercepts', () => { const after = vi.fn(); settingsHooks.onSettingChange.register('test', after); diff --git a/stores/__tests__/settings-store-proofreading.test.ts b/stores/__tests__/settings-store-proofreading.test.ts index 1eea474c8..71ad2c1ec 100644 --- a/stores/__tests__/settings-store-proofreading.test.ts +++ b/stores/__tests__/settings-store-proofreading.test.ts @@ -14,7 +14,7 @@ import { currentPolicyView } from '@/components/email/proofreading/use-proofread const settings = () => useSettingsStore.getState(); const loadPolicy = (patch: Partial) => - usePolicyStore.setState({ policy: { ...DEFAULT_POLICY, ...patch }, loaded: true, partial: false, subjectPending: false }); + usePolicyStore.setState({ policy: { ...DEFAULT_POLICY, ...patch }, loaded: true, partial: false, subjectPending: false, source: 'server' }); beforeEach(() => { localStorage.clear(); @@ -62,13 +62,18 @@ describe('proofreading settings', () => { it('checks nothing until the policy has loaded', () => { useSettingsStore.setState({ proofreading: true }); - usePolicyStore.setState({ loaded: false }); + usePolicyStore.setState({ loaded: false, source: 'server' }); expect(checkerAccess(currentPolicyView(), settings(), 'lt').on).toBe(false); usePolicyStore.setState({ loaded: true, partial: true }); expect(checkerAccess(currentPolicyView(), settings(), 'lt').on).toBe(false); usePolicyStore.setState({ partial: false, subjectPending: true }); expect(checkerAccess(currentPolicyView(), settings(), 'lt').on).toBe(false); - usePolicyStore.setState({ subjectPending: false }); + usePolicyStore.setState({ subjectPending: false, source: 'fallback' }); + // The fetch failed and nothing was ever fetched: the defaults could undo a lock. + expect(checkerAccess(currentPolicyView(), settings(), 'lt').on).toBe(false); + usePolicyStore.setState({ source: 'cache' }); + expect(checkerAccess(currentPolicyView(), settings(), 'lt').on).toBe(true); + usePolicyStore.setState({ source: 'server' }); expect(checkerAccess(currentPolicyView(), settings(), 'lt').on).toBe(true); }); }); diff --git a/stores/settings-store.ts b/stores/settings-store.ts index 9eafefa48..a373a08da 100644 --- a/stores/settings-store.ts +++ b/stores/settings-store.ts @@ -252,12 +252,23 @@ const BOOKKEEPING_SETTING_KEYS = new Set(['onboardingCompleted', 'tourCo * tells onSettingChange. With no interceptor registered the change applies * synchronously, so inputs bound to the store keep behaving as before. */ +/** + * Turning proofreading off, overall or for one plugin, stops text going to a + * plugin: no plugin may hold that up (onBeforeSettingChange). + */ +function stopsSendingText(key: string, value: unknown, previous: unknown): boolean { + if (key === 'proofreading') return value === false; + if (key !== 'proofreadingPlugins' || !value || typeof value !== 'object') return false; + const before = (previous && typeof previous === 'object' ? previous : {}) as Record; + return Object.entries(value as Record).some(([id, on]) => on === false && before[id] !== false); +} + function changePreference(key: string, value: unknown, previous: unknown, apply: () => void): void { const commit = () => { apply(); void settingsHooks.onSettingChange.emit({ key, value, previous }); }; - if (settingsHooks.onBeforeSettingChange.size === 0) { + if (settingsHooks.onBeforeSettingChange.size === 0 || stopsSendingText(key, value, previous)) { commit(); return; }