add translations and error messages for encrypted/password protected files being edited

This commit is contained in:
EthanHealy01 2025-09-25 18:49:17 +01:00
parent f412a3a60e
commit c9ac919c55
4 changed files with 81 additions and 1 deletions

View File

@ -74,6 +74,8 @@
},
"error": {
"pdfPassword": "The PDF Document is passworded and either the password was not provided or was incorrect",
"encryptedPdfMustRemovePassword": "This PDF is encrypted or password-protected. Please unlock it before converting to PDF/A.",
"incorrectPasswordProvided": "The PDF password is incorrect or not provided.",
"_value": "Error",
"dismissAllErrors": "Dismiss All Errors",
"sorry": "Sorry for the issue!",

View File

@ -68,6 +68,8 @@
},
"error": {
"pdfPassword": "The PDF Document is passworded and either the password was not provided or was incorrect",
"encryptedPdfMustRemovePassword": "This PDF is encrypted or password-protected. Please unlock it before converting to PDF/A.",
"incorrectPasswordProvided": "The PDF password is incorrect or not provided.",
"_value": "Error",
"sorry": "Sorry for the issue!",
"needHelp": "Need help / Found an issue?",

View File

@ -1,6 +1,7 @@
import axios from 'axios';
import { alert } from '../components/toast';
import { broadcastErroredFiles, extractErrorFileIds, normalizeAxiosErrorData } from './errorUtils';
import { showSpecialErrorToast } from './specialErrorToasts';
const FRIENDLY_FALLBACK = 'There was an error processing your request.';
@ -49,6 +50,11 @@ function extractAxiosErrorMessage(error: any): { title: string; body: string } {
if (typeof raw === 'string') return raw;
try { return JSON.stringify(data); } catch { return ''; }
})();
// Specific friendly mapping for encrypted PDFs (centralized toast also fires in interceptor)
if (ENCRYPTION_ERROR_REGEX.test(body)) {
const title = titleForStatus(error.response?.status);
return { title, body: ENCRYPTION_FRIENDLY };
}
const ids = extractIds();
const title = titleForStatus(status);
if (ids && ids.length > 0) {
@ -64,6 +70,9 @@ function extractAxiosErrorMessage(error: any): { title: string; body: string } {
}
try {
const msg = (error?.message || String(error)) as string;
if (ENCRYPTION_ERROR_REGEX.test(msg)) {
return { title: 'Request error', body: ENCRYPTION_FRIENDLY };
}
return { title: 'Network error', body: isUnhelpfulMessage(msg) ? FRIENDLY_FALLBACK : msg };
} catch (e) {
// ignore extraction errors
@ -109,7 +118,17 @@ const __INTERCEPTOR_ID__ = axios.interceptors.response.use(
}
}
alert({ alertType: 'error', title, body, expandable: true, isPersistentPopup: false });
// Show specialized friendly toasts if matched; otherwise show the generic one
const raw = (error?.response?.data) as any;
let rawString: string | undefined;
try {
if (typeof raw === 'string') rawString = raw;
else rawString = await normalizeAxiosErrorData(raw).then((d) => (typeof d === 'string' ? d : JSON.stringify(d)));
} catch { /* ignore */ }
const handled = showSpecialErrorToast(rawString, { status });
if (!handled) {
alert({ alertType: 'error', title, body, expandable: true, isPersistentPopup: false });
}
return Promise.reject(error);
}
);

View File

@ -0,0 +1,57 @@
import { alert } from '../components/toast';
interface ErrorToastMapping {
regex: RegExp;
i18nKey: string;
defaultMessage: string;
}
// Centralized list of special backend error message patterns → friendly, translated toasts
const MAPPINGS: ErrorToastMapping[] = [
{
regex: /pdf contains an encryption dictionary/i,
i18nKey: 'errors.encryptedPdfMustRemovePassword',
defaultMessage: 'This PDF is encrypted. Please unlock it using the Unlock PDF Forms tool.'
},
{
regex: /the pdf document is passworded and either the password was not provided or was incorrect/i,
i18nKey: 'errors.incorrectPasswordProvided',
defaultMessage: 'The PDF password is incorrect or not provided.'
},
];
function titleForStatus(status?: number): string {
if (!status) return 'Network error';
if (status >= 500) return 'Server error';
if (status >= 400) return 'Request error';
return 'Request failed';
}
/**
* Match a raw backend error string against known patterns and show a friendly toast.
* Returns true if a special toast was shown, false otherwise.
*/
export function showSpecialErrorToast(rawError: string | undefined, options?: { status?: number }): boolean {
const message = (rawError || '').toString();
if (!message) return false;
for (const mapping of MAPPINGS) {
if (mapping.regex.test(message)) {
// Best-effort translation without hard dependency on i18n config
let body = mapping.defaultMessage;
try {
const anyGlobal: any = (globalThis as any);
const i18next = anyGlobal?.i18next;
if (i18next && typeof i18next.t === 'function') {
body = i18next.t(mapping.i18nKey, { defaultValue: mapping.defaultMessage });
}
} catch { /* ignore translation errors */ }
const title = titleForStatus(options?.status);
alert({ alertType: 'error', title, body, expandable: true, isPersistentPopup: false });
return true;
}
}
return false;
}