As a homeowner exporting a bank report in a language other than my UI language, I want every string in the generated PDF to come from the report language so that the document I hand to my bank is entirely in one language, with no possibility of a stray UI-locale string leaking in.
Parent Epic: none (Bank Report Wizard mini-epic)
Priority: Should Have
Context
#1993 / #2000 moved the PDF page-footer label out of a t() call inside reportPdf/* and into ReportContentLabels, populated once via reportT. #1938 did the same for the page header. Three call sites of the identical shape remain on beta:
| Location |
Key |
client/src/lib/reportPdf/coverLetterPdf.ts:46 |
sourceReports.coverLetter.reference |
client/src/lib/reportPdf/coverLetterPdf.ts:54 |
sourceReports.coverLetter.subjectLabel |
client/src/lib/reportPdf/overviewPdf.ts:844 |
sourceReports.table.${reason} (skip-footnote reason — dynamic key) |
merge.ts still threads its t parameter into buildCoverLetterContent (line 114) and buildOverviewContent (line 118), so TFunction remains a parameter of all three reportPdf/* modules.
These are not live user-visible bugs. The sole production caller (ReportWizardPage.tsx:318) injects reportT (= i18n.getFixedT(reportLanguage, 'budget')), so all three currently resolve in the report language. They are latent hazards: ADR-034's "reportPdf/* modules must never reach the ambient locale" is enforced only by the convention that every caller remembers to pass reportT rather than t. ReportWizardPage.tsx has both variables in scope in the same component, one character apart.
Fixing these one call site at a time has now happened twice (#1938, #1993) and leaves the next one to be found by review. This story closes the class.
Acceptance Criteria
Notes
As a homeowner exporting a bank report in a language other than my UI language, I want every string in the generated PDF to come from the report language so that the document I hand to my bank is entirely in one language, with no possibility of a stray UI-locale string leaking in.
Parent Epic: none (Bank Report Wizard mini-epic)
Priority: Should Have
Context
#1993 / #2000 moved the PDF page-footer label out of a
t()call insidereportPdf/*and intoReportContentLabels, populated once viareportT. #1938 did the same for the page header. Three call sites of the identical shape remain onbeta:client/src/lib/reportPdf/coverLetterPdf.ts:46sourceReports.coverLetter.referenceclient/src/lib/reportPdf/coverLetterPdf.ts:54sourceReports.coverLetter.subjectLabelclient/src/lib/reportPdf/overviewPdf.ts:844sourceReports.table.${reason}(skip-footnote reason — dynamic key)merge.tsstill threads itstparameter intobuildCoverLetterContent(line 114) andbuildOverviewContent(line 118), soTFunctionremains a parameter of all threereportPdf/*modules.These are not live user-visible bugs. The sole production caller (
ReportWizardPage.tsx:318) injectsreportT(=i18n.getFixedT(reportLanguage, 'budget')), so all three currently resolve in the report language. They are latent hazards: ADR-034's "reportPdf/*modules must never reach the ambient locale" is enforced only by the convention that every caller remembers to passreportTrather thant.ReportWizardPage.tsxhas both variables in scope in the same component, one character apart.Fixing these one call site at a time has now happened twice (#1938, #1993) and leaves the next one to be found by review. This story closes the class.
Acceptance Criteria
ReportContentLabelsgains fields for the three strings above; each is populated inbuildReportContent.tsviareportT, in the samebudgetnamespacereportTis bound to.overviewPdf.ts:844uses a dynamic key — its resolution is moved to wherereportTis available, and everyreasonvalue the code can produce resolves to a real string in bothenandde(no key echoed into the PDF).buildCoverLetterContentandbuildOverviewContentno longer accept aTFunctionparameter.generateReportPdfno longer accepts aTFunctionparameter, andReportWizardPage.tsx:313is updated accordingly.grep -rn "TFunction" client/src/lib/reportPdf/ --include=*.ts | grep -v testreturns no matches in production files — the contract is compiler-enforced, not convention-enforced.client/src/i18n/en/budget.jsonandclient/src/i18n/de/budget.json.TFunctioninstead ofreportContent.labels. The test must be verified to discriminate — confirm it fails when the production line is reverted, not merely that it passes as written.npm run typecheckpasses. Note the trap from fix(reports): pageFooter uses injected report-language label instead of ambient locale (#1993) #2000: adding required fields toReportContentLabelsbreaks every explicitly-typed construction site, andts-jestemits no type diagnostics here — a fully green Jest run does not mean the field is wired everywhere. Sweep all construction sites (ReportContentEditor.test.tsxwas the one missed in fix(reports): pageFooter uses injected report-language label instead of ambient locale (#1993) #2000).Quality Gatesgreen.Notes
ReportContentLabelsinterface andmerge.ts, and would conflict.