Skip to content

fix(pdf): respect export-request language over DB template language#537

Merged
Ar1anit merged 1 commit into
mainfrom
fix/harvard-classic-export-language
May 31, 2026
Merged

fix(pdf): respect export-request language over DB template language#537
Ar1anit merged 1 commit into
mainfrom
fix/harvard-classic-export-language

Conversation

@Ar1anit
Copy link
Copy Markdown
Collaborator

@Ar1anit Ar1anit commented May 31, 2026

Fixes #536.

Problem

For Harvard Classic (and latently for the other two registered designs), selecting German in the application edit flow renders correctly in the in-app preview but the exported PDF comes out with English section headings (Education, Skills, Professional Experience) while the body text (LLM-generated bullets, summary, cover letter prose) is in German.

Root cause

The preview renders client-side via a frontend component that takes the user-chosen language directly as a prop.

The export, however, renders server-side via ReactPdfRendererService, which constructs meta.language from the resolved DB template row's language column. The three registered templates picked their localized section headings with:

const lang = meta.language || data.language || 'en';

seed-multilingual-templates.ts only seeds one variant per design (English), so application.processor.ts → resolveTemplateForLanguage('<category>', 'de', …) doesn't find a German variant and falls back to the English row (with a warning log). meta.language is then 'en', the precedence above picks 'en', and the localized headings render in English — even though data.language was set to 'de' from the export request and the LLM body is already in German.

Fix

Flip the precedence in all three resume templates so the explicit export-request language wins over the DB row's static language column:

const lang = data.language || meta.language || 'en';

This makes export-time language correctness independent of which language variants happen to be seeded — a one-line change in each of:

The processor's language propagation, controller (requestExport), and DTO are all already correct — no other code needed to change.

Regression coverage

Adds apps/api/src/pdf-v2/templates/language-precedence.unit.spec.ts which, for each of the 3 registered templates:

  • calls the factory directly (bypassing the react-pdf-loader.ts dynamic-import shim that doesn't work under Vitest's transform),
  • renders to a PDF buffer with data.language='de' against a stubbed meta.language='en' (simulating the DE-variant-missing fallback path),
  • rasterises text via pdf-parse and asserts German headings (Berufserfahrung, Ausbildung, Fähigkeiten) are present and English equivalents are NOT.

Sanity-checked by reverting the source fix and confirming all 3 tests fail without it.

Out of scope

  • Seeding German DB template variants. Removes the noisy Template not found for category … falling back to English warning log and gives PDF metadata a correct language tag, but the template-layer fix is complete on its own — should be a separate cleanup issue.
  • application.processor.ts regenerate path (regenerate(), line ~2247) doesn't pass language when re-enqueueing FAILED applications. Pre-existing, separate from the export flow this issue is about.
  • Harvard Classic cover-letter meta: _meta underscore-discard — left as-is since the CL body is already language-correct (from LLM) and there's no localized chrome to translate.

Verification

  • pnpm vitest run src/pdf-v2/templates/language-precedence.unit.spec.ts → 3 passed
  • pnpm lint → 0 lint issues introduced on touched files (the 299 pre-existing warnings live elsewhere)
  • Manual repro recommended before merge: pick Harvard Classic + German on a real application, export, confirm German section headings in the downloaded PDF.

The react-pdf resume templates picked their localized section headings
via `meta.language || data.language`, where `meta.language` comes from
the resolved DB template row. The seed only ships one variant per design
(English), so when a user exported Harvard Classic + German, the
language-variant lookup in application.processor.ts fell back to the
English row, meta.language was 'en', and the resume rendered with
English headings (Experience / Education / Skills) even though the
LLM-generated body was correctly in German — partial-language bug
reported in #536.

Flip the precedence in all three registered templates so the explicit
export-request language (data.language, set from the export DTO by the
application processor) wins over the DB row's static language column.
This makes export-time language correctness independent of which
language variants happen to be seeded.

Adds a unit test that renders each template at the factory level with
data.language='de' against a stubbed meta.language='en' and asserts the
rasterised PDF text contains German headings (Berufserfahrung,
Ausbildung, Fähigkeiten) and not their English equivalents.

Fixes #536
@Ar1anit Ar1anit merged commit 2f0302c into main May 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(templates): Harvard Classic exports English PDF when German is selected

1 participant