Per-draft `language` column drives the .docx output language for the
submission generator. The lawyer picks DE or EN on the draft editor's
sidebar; the generator selects the language-matched template variant
(falling back through {code}.{lang} → {code} → _skeleton.{lang} →
_skeleton → letterhead) and resolves language-aware variables
({{procedural_event.name}} → name_de vs name_en).
Schema (mig 130 — bumped from 129 to deconflict with atlas's #96):
- paliad.submission_drafts.language text NOT NULL DEFAULT 'de'
CHECK IN ('de','en'). Existing rows inherit 'de' via the default,
preserving every legacy draft's behaviour byte-for-byte.
Backend (Go):
- SubmissionVarsContext.Lang overrides the user's UI lang. Build()
uses it when set; falls back to user.Lang otherwise — Slice 1's
format-only /generate path keeps working unchanged.
- SubmissionDraftService.BuildRenderBag now threads draft.Language
through. Create/EnsureLatest seed from the UI lang (DE default).
- DraftPatch.Language landed; Update validates and rejects values
outside {de,en}. Project-scoped + global PATCH endpoints both
surface the field.
- resolveSubmissionTemplate(ctx, code, lang) replaces the lang-less
predecessor. Returns the matched tier (per_code_lang / per_code /
skeleton_lang / skeleton / letterhead) so the editor knows whether
to surface the "Fallback: universelles Skelett" notice.
- fileRegistry registers the EN skeleton sibling (`_skeleton.en.docx`)
alongside the DE one; per-code EN variants land in a parallel
submissionTemplateENRegistry (empty for now — EN templates land per
HLC authoring). 404s from Gitea fall through silently.
- /api/projects/{id}/submissions/{code}/generate accepts
`?language=de|en` query override (one-shot path, no draft row to
pull the column from); defaults to the user's UI lang.
Frontend (TS/JSX):
- DE/EN radio above the variables list in the draft editor sidebar.
Switching the radio PATCHes `language` and the server returns the
freshly-resolved bag + preview HTML so the lawyer sees EN values
immediately.
- Fallback notice ("Fallback: universelles Skelett (keine
sprachspezifische Vorlage)") shows when the resolved tier doesn't
match the requested language.
- 4 new i18n keys (DE + EN) + CSS for the toggle.
Tests:
- normalizeDraftLanguage covers DE/EN/case/whitespace/unknown.
- addRuleVars language-pick test pins procedural_event.name and the
rule.name alias to the language-matched value.
- languageFallback truth table covers all 10 (lang × tier) combos.
Build hygiene: go build/vet/test clean; bun run build clean.
44 lines
1.5 KiB
Go
44 lines
1.5 KiB
Go
package handlers
|
|
|
|
// Regression tests for the template-tier → language-fallback mapping
|
|
// (t-paliad-276). The editor surfaces a "Fallback: universelles
|
|
// Skelett" notice when the requested draft language has no per-firm
|
|
// language-matched template — these tests pin which tier counts as a
|
|
// fallback for each language so the UI signal stays stable.
|
|
|
|
import "testing"
|
|
|
|
func TestLanguageFallback(t *testing.T) {
|
|
t.Parallel()
|
|
cases := []struct {
|
|
name string
|
|
lang string
|
|
tier submissionTemplateTier
|
|
want bool
|
|
}{
|
|
// DE drafts: every non-letterhead tier is a first-class match.
|
|
{"de_per_code_lang", "de", tplTierPerCodeLang, false},
|
|
{"de_per_code", "de", tplTierPerCode, false},
|
|
{"de_skeleton_lang", "de", tplTierSkeletonLang, false},
|
|
{"de_skeleton", "de", tplTierSkeleton, false},
|
|
{"de_letterhead", "de", tplTierLetterhead, true},
|
|
|
|
// EN drafts: per_code (DE-baked) and skeleton (DE-baked) both
|
|
// surface the fallback notice so the lawyer knows the rendered
|
|
// body lacks EN prose.
|
|
{"en_per_code_lang", "en", tplTierPerCodeLang, false},
|
|
{"en_per_code", "en", tplTierPerCode, true},
|
|
{"en_skeleton_lang", "en", tplTierSkeletonLang, false},
|
|
{"en_skeleton", "en", tplTierSkeleton, true},
|
|
{"en_letterhead", "en", tplTierLetterhead, true},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
if got := languageFallback(c.lang, c.tier); got != c.want {
|
|
t.Errorf("languageFallback(%q, %q) = %v, want %v", c.lang, c.tier, got, c.want)
|
|
}
|
|
})
|
|
}
|
|
}
|