sd-app: i18n/l10n Foundation + Localized Gross-Up
Section titled “sd-app: i18n/l10n Foundation + Localized Gross-Up”Date: 2026-03-09 Session: Phase A (shadcn-svelte) + Phase B (Paraglide i18n) Status: ✅ Complete Commit: 4909ec9
Summary
Section titled “Summary”Established two foundational layers for apps/sd-app:
- shadcn-svelte — copy-paste UI components (Button, Input, Label, Card) built on existing Tailwind CSS custom properties
- Paraglide i18n — compile-time translations via
@inlang/paraglide-sveltekitwith shared message files inpackages/i18n/
Proved the foundation by localizing the RRSP Gross-Up calculator into /ca/en/gross-up (English) and /ca/fr/gross-up (French), both prerendered at build time.
Tasks Completed
Section titled “Tasks Completed”- shadcn-svelte: Button, Input, Label, Card components
-
cn()utility (clsx + tailwind-merge) -
packages/i18n/with en.json + fr.json (25 keys each) - Paraglide Vite plugin configured,
src/paraglide/generated at build - Route restructure:
/[country]/[lang]/with static prerender entries - Localized dashboard + gross-up calculator (Intl.NumberFormat for en-CA / fr-CA)
-
jurisdiction.ts(RRSP/TFSA vs 401k/Roth IRA config) -
formatters.ts(locale-aware Intl.NumberFormat) -
profile.svelte.tsglobal state store ($state class pattern) -
TaxCalculatorInterface+CanadaTaxCalculatorstrategy pattern - Cloudflare edge
_worker.js(Accept-Language detection, old-path 301s) -
static/_redirectsfor reference - MiniAppsShowcase.astro updated (iframe src →
/ca/en/gross-up?embed) - ESLint fixed: TypeScript parser for Svelte script blocks + rest-prop override
Files Modified/Created
Section titled “Files Modified/Created”packages/i18n/ (new package)
package.json— @smart-debt/i18n workspace packagemessages/en.json— 25 English keysmessages/fr.json— 25 French (Canadian) keys
apps/sd-app/
package.json— added @inlang/paraglide-sveltekit (dev), clsx, tailwind-mergevite.config.ts— added paraglide() pluginsvelte.config.js— added $paraglide alias.gitignore— added src/paraglide/project.inlang/settings.json— Paraglide project configsrc/paraglide-types.d.ts— TypeScript declarations for generated modulessrc/app.css— added —card/—card-foreground CSS variablestailwind.config.js— added card color tokenssrc/lib/utils.ts— cn() helpersrc/lib/i18n.ts— re-exports from $paraglide/runtime + messagessrc/lib/jurisdiction.ts— country config (CA/US product names)src/lib/formatters.ts— Intl.NumberFormat currency formattingsrc/lib/stores/profile.svelte.ts— global $state profilesrc/lib/calculators/interfaces.ts— TaxCalculatorInterfacesrc/lib/calculators/ca/index.ts— CanadaTaxCalculatorsrc/lib/components/ui/— Button, Input, Label, Card (shadcn-style)src/routes/+layout.svelte— stripped to bare (css import only)src/routes/+page.svelte— redirect to /ca/ensrc/routes/[country]/[lang]/— full route tree (6 pages × 2 locales = 12 pages)static/_worker.js— Cloudflare edge workerstatic/_redirects— old-path redirects reference
Deleted:
src/routes/gross-up/+page.sveltesrc/routes/int-only/+page.sveltesrc/routes/term-loan/+page.sveltesrc/routes/demo/+page.svelte→ moved to [country]/[lang]/demo/
sites/Template:
MiniAppsShowcase.astro— iframe src updated to /ca/en/gross-up?embed
Root:
eslint.config.mjs— TypeScript parser for Svelte + ui component rule override
Test Results
Section titled “Test Results”pnpm check: 0 errors · 0 warningspnpm build: ✓ 10 prerendered HTML pages
build/ca/en.html ← English dashboardbuild/ca/en/gross-up.html ← "RRSP Gross-Up Calculator"build/ca/en/demo.htmlbuild/ca/en/int-only.htmlbuild/ca/en/term-loan.htmlbuild/ca/fr.html ← French dashboardbuild/ca/fr/gross-up.html ← "Calculateur de majoration REER"build/ca/fr/demo.htmlbuild/ca/fr/int-only.htmlbuild/ca/fr/term-loan.html
English: grep -c "RRSP Gross-Up Calculator" build/ca/en/gross-up.html → 2 ✓French: grep -c "Calculateur de majoration REER" build/ca/fr/gross-up.html → 2 ✓French dashboard: grep -c "Bientôt disponible" build/ca/fr.html → 1 ✓Architecture Decisions
Section titled “Architecture Decisions”- Locale in URL path (
/ca/en/,/ca/fr/) — SvelteKitentries()for prerendering - Paraglide compile-time — messages baked into prerendered HTML, zero runtime overhead
setLocale()in layout load — runs on both server (prerender) and client (navigation)_worker.js— handles all Cloudflare requests;_redirectsnot processed when worker present- shadcn copy-paste model — components live in sd-app (not packages/components — different format)
Known Issues
Section titled “Known Issues”None. Pre-existing ESLint limitations with Svelte files (TypeScript parser not configured) fixed as part of this session.
Next Steps
Section titled “Next Steps”- Add
/us/en/entries when US market is ready (just add toentries()array) - Add
packages/i18n/messages/fr-CA.jsonif regional French distinction needed - Phase C: deploy to Cloudflare Pages and verify edge worker + Accept-Language detection
- Write decision logs: i18n-Paraglide.md + Web Styling.md update