fix(i18n): preserve safe interview and attachment purpose fallbacks

This commit is contained in:
cesnimda
2026-11-02 23:01:23 +01:00
parent fb48563557
commit 80d3849125
6 changed files with 66 additions and 15 deletions
@@ -502,5 +502,5 @@ function interviewCategoryLabel(t: (key: any) => string, category: string) {
note: "interviewCategoryNotes",
debrief: "interviewCategoryDebrief",
};
return keys[category] ? t(keys[category]) : category;
return Object.hasOwn(keys, category) ? t(keys[category]) : category;
}
@@ -22,6 +22,15 @@ function page(items: ReturnType<typeof item>[] = [item()], currentPage = 1, tota
return { items, page: currentPage, pageSize: 25, total, totalPages: Math.ceil(total / 25) };
}
describe.each(['en', 'nb'])('%s unknown attachment purposes', language => {
test.each(['constructor', 'toString', '__proto__'])('uses the localized Other label for %s', async purpose => {
localStorage.setItem('uiLanguage', language);
mockedApi.get.mockResolvedValue({ data: page([item({ type: 'attachment', purpose, visibility: 'private' })]) } as never);
renderPage();
expect(await screen.findByText(`${copy('attachmentsPurpose')}: ${copy('attachmentsPurposeOther')}`)).toBeVisible();
});
});
function deferred<T>() {
let resolve!: (value: T) => void;
let reject!: (reason: unknown) => void;
@@ -62,6 +62,16 @@ beforeEach(() => {
window.localStorage.setItem("uiLanguage", "en");
});
describe.each(['en', 'nb'])('%s unknown interview categories', language => {
test.each(['constructor', 'toString', '__proto__'])('preserves %s as a category label', async category => {
window.localStorage.setItem('uiLanguage', language);
routeGet({ board: { ...board, groups: [{ ...board.groups[0], category }] } });
render(<ApplicationInterviewPrep jobId={7} />);
expect(await screen.findByText(category)).toBeInTheDocument();
expect(screen.getByText('Funding history')).toBeInTheDocument();
});
});
test("localizes interview categories while preserving user-authored preparation", async () => {
window.localStorage.setItem("uiLanguage", "nb");
routeGet();
+1 -1
View File
@@ -240,7 +240,7 @@ export default function DocumentsPage() {
{item.type === "email-draft" ? <Typography variant="caption" color="text.secondary">{t("documentsEmailHelp")}</Typography> : null}
{item.type === "attachment" ? <>
<Typography variant="caption" color="text.secondary">{[item.fileType, item.fileSize != null ? t("documentsFileSize", { size: new Intl.NumberFormat(locale, { maximumFractionDigits: 1 }).format(item.fileSize / 1024) }) : null].filter(Boolean).join(" · ")}</Typography>
{item.purpose ? <Typography variant="caption" color="text.secondary">{t("attachmentsPurpose")}: {t(({ resume: "attachmentsPurposeResume", "cover-letter": "attachmentsPurposeCoverLetter", portfolio: "attachmentsPurposePortfolio", "case-study": "attachmentsPurposeCaseStudy", certificate: "attachmentsPurposeCertificate" } as Record<string, TranslationKey>)[item.purpose.toLowerCase()] ?? "attachmentsPurposeOther")}</Typography> : null}
{item.purpose ? <Typography variant="caption" color="text.secondary">{t("attachmentsPurpose")}: {t((Object.assign(Object.create(null), { resume: "attachmentsPurposeResume", "cover-letter": "attachmentsPurposeCoverLetter", portfolio: "attachmentsPurposePortfolio", "case-study": "attachmentsPurposeCaseStudy", certificate: "attachmentsPurposeCertificate" }) as Record<string, TranslationKey>)[item.purpose.toLowerCase()] ?? "attachmentsPurposeOther")}</Typography> : null}
{item.purpose?.toLowerCase() === "certificate" ? <Typography variant="caption" color="text.secondary">{t("documentsCertificateHelp")}</Typography> : null}
{item.useForAi != null ? <Typography variant="caption" color="text.secondary">{t("attachmentsAiUse")}: {t(item.useForAi ? "attachmentsAiEnabled" : "attachmentsAiDisabled")}</Typography> : null}
</> : null}