fix(i18n): localize application workflows
Use stable checklist and interview keys for system copy while preserving user-authored content. Localize creation-tab errors, language selectors, match bands, dates, and selected-CV context.
This commit is contained in:
@@ -26,6 +26,7 @@ import { useI18n } from "../i18n/I18nProvider";
|
||||
// is application-specific by nature. docs/architecture/application-workspace.md.
|
||||
|
||||
function useAsset<T>(load: () => Promise<T>, deps: React.DependencyList) {
|
||||
const { t } = useI18n();
|
||||
const [data, setData] = useState<T | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -44,7 +45,7 @@ function useAsset<T>(load: () => Promise<T>, deps: React.DependencyList) {
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (!cancelled) setError(getApiErrorMessage(err, "Could not load this section."));
|
||||
if (!cancelled) setError(getApiErrorMessage(err, t("assetsLoadFailed")));
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setLoading(false);
|
||||
@@ -52,7 +53,7 @@ function useAsset<T>(load: () => Promise<T>, deps: React.DependencyList) {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [run]);
|
||||
}, [run, t]);
|
||||
|
||||
useEffect(() => reload(), [reload]);
|
||||
|
||||
@@ -98,7 +99,7 @@ export function ApplicationCvSection({ jobId }: { jobId: number }) {
|
||||
setData(await applicationAssetsApi.attachVariant(jobId, variantId));
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(getApiErrorMessage(err, "Could not change the attached CV."));
|
||||
setError(getApiErrorMessage(err, t("assetsAttachCvFailed")));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -110,11 +111,11 @@ export function ApplicationCvSection({ jobId }: { jobId: number }) {
|
||||
if (!data?.attachedVariantId) return;
|
||||
setBusy(true);
|
||||
try {
|
||||
const copy = await cvBuilderApi.duplicate(data.attachedVariantId, `${data.attachedVariantName || "CV"} — tailored copy`);
|
||||
const copy = await cvBuilderApi.duplicate(data.attachedVariantId, t("assetsTailoredCopyName", { name: data.attachedVariantName || "CV" }));
|
||||
setData(await applicationAssetsApi.attachVariant(jobId, copy.id));
|
||||
window.location.assign(`/career/builder/${copy.id}`);
|
||||
} catch (err) {
|
||||
setError(getApiErrorMessage(err, "Could not create a tailored CV copy."));
|
||||
setError(getApiErrorMessage(err, t("assetsDuplicateCvFailed")));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -308,7 +309,7 @@ export function ApplicationCoverLetterSection({ jobId, onDirtyChange }: { jobId:
|
||||
setDraftAiAction(null);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(getApiErrorMessage(err, "Could not save the cover letter."));
|
||||
setError(getApiErrorMessage(err, t("assetsCoverSaveFailed")));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -322,7 +323,7 @@ export function ApplicationCoverLetterSection({ jobId, onDirtyChange }: { jobId:
|
||||
setDraftAiAction(null);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(getApiErrorMessage(err, "Could not restore that version."));
|
||||
setError(getApiErrorMessage(err, t("assetsCoverRestoreFailed")));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -394,7 +395,7 @@ export function ApplicationCoverLetterSection({ jobId, onDirtyChange }: { jobId:
|
||||
{v.isCurrent && <Chip size="small" color="primary" variant="outlined" label={t("assetsCurrent")} />}
|
||||
</Stack>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{new Date(v.createdAtUtc).toLocaleString()} · {t("assetsCharacterCount", { count: v.length })}
|
||||
{new Date(v.createdAtUtc).toLocaleString(language === "nb" ? "nb-NO" : "en")} · {t("assetsCharacterCount", { count: v.length })}
|
||||
</Typography>
|
||||
</Box>
|
||||
{!v.isCurrent && (
|
||||
@@ -458,7 +459,7 @@ function CoverLetterAiAssistant({ jobId, currentText, onApply }: { jobId: number
|
||||
});
|
||||
setSuggestion(result.result.text?.trim() ?? "");
|
||||
} catch (err) {
|
||||
setError(getApiErrorMessage(err, "Could not generate a cover-letter suggestion."));
|
||||
setError(getApiErrorMessage(err, t("coverAiGenerateFailed")));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -499,8 +500,8 @@ function CoverLetterAiAssistant({ jobId, currentText, onApply }: { jobId: number
|
||||
<FormControl size="small" sx={{ minWidth: 190 }}>
|
||||
<InputLabel>{t("coverAiDocumentLanguage")}</InputLabel>
|
||||
<Select label={t("coverAiDocumentLanguage")} value={language} onChange={(event) => setLanguage(event.target.value as "en" | "nb-NO")}>
|
||||
<MenuItem value="en">English</MenuItem>
|
||||
<MenuItem value="nb-NO">Norsk bokmål</MenuItem>
|
||||
<MenuItem value="en">{t("languageEnglish")}</MenuItem>
|
||||
<MenuItem value="nb-NO">{t("languageNorwegianBokmal")}</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
@@ -598,7 +599,7 @@ export function ApplicationPackageDraftsSection({
|
||||
setError(null);
|
||||
onSaved?.();
|
||||
} catch (err) {
|
||||
setError(getApiErrorMessage(err, "Could not save the application drafts."));
|
||||
setError(getApiErrorMessage(err, t("assetsDraftsSaveFailed")));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user