feat(frontend): add PL/EN i18n using @inlang/paraglide-js v2
- Install @inlang/paraglide-js v2 with Vite plugin and paraglideMiddleware hook - Add messages/pl.json and messages/en.json with ~400 translation keys - Create project.inlang/settings.json (PL as base locale) - Add LanguageSwitcher component (cookie-based, no URL prefix needed) - Replace all hardcoded strings across 14 pages/components with m.*() calls - ProductForm uses derived label maps for all enum types (category, texture, etc.) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9524e4df54
commit
99584521a1
22 changed files with 1742 additions and 612 deletions
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { enhance } from '$app/forms';
|
||||
import type { ActionData, PageData } from './$types';
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
import { Badge } from '$lib/components/ui/badge';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
|
||||
|
|
@ -34,16 +35,16 @@
|
|||
let filterFlag = $derived(data.flag ?? '');
|
||||
</script>
|
||||
|
||||
<svelte:head><title>Lab Results — innercontext</title></svelte:head>
|
||||
<svelte:head><title>{m["labResults_title"]()} — innercontext</title></svelte:head>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold tracking-tight">Lab Results</h2>
|
||||
<p class="text-muted-foreground">{data.results.length} results</p>
|
||||
<h2 class="text-2xl font-bold tracking-tight">{m["labResults_title"]()}</h2>
|
||||
<p class="text-muted-foreground">{m["labResults_count"]({ count: data.results.length })}</p>
|
||||
</div>
|
||||
<Button variant="outline" onclick={() => (showForm = !showForm)}>
|
||||
{showForm ? 'Cancel' : '+ Add result'}
|
||||
{showForm ? m.common_cancel() : m["labResults_addNew"]()}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
@ -51,12 +52,12 @@
|
|||
<div class="rounded-md bg-destructive/10 px-4 py-3 text-sm text-destructive">{form.error}</div>
|
||||
{/if}
|
||||
{#if form?.created}
|
||||
<div class="rounded-md bg-green-50 px-4 py-3 text-sm text-green-700">Result added.</div>
|
||||
<div class="rounded-md bg-green-50 px-4 py-3 text-sm text-green-700">{m["labResults_added"]()}</div>
|
||||
{/if}
|
||||
|
||||
<!-- Filter -->
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-sm text-muted-foreground">Flag:</span>
|
||||
<span class="text-sm text-muted-foreground">{m["labResults_flagFilter"]()}</span>
|
||||
<Select
|
||||
type="single"
|
||||
value={filterFlag}
|
||||
|
|
@ -64,9 +65,9 @@
|
|||
goto(v ? `/health/lab-results?flag=${v}` : '/health/lab-results');
|
||||
}}
|
||||
>
|
||||
<SelectTrigger class="w-32">{filterFlag || 'All'}</SelectTrigger>
|
||||
<SelectTrigger class="w-32">{filterFlag || m["labResults_flagAll"]()}</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="">All</SelectItem>
|
||||
<SelectItem value="">{m["labResults_flagAll"]()}</SelectItem>
|
||||
{#each flags as f (f)}
|
||||
<SelectItem value={f}>{f}</SelectItem>
|
||||
{/each}
|
||||
|
|
@ -76,40 +77,40 @@
|
|||
|
||||
{#if showForm}
|
||||
<Card>
|
||||
<CardHeader><CardTitle>New lab result</CardTitle></CardHeader>
|
||||
<CardHeader><CardTitle>{m["labResults_newTitle"]()}</CardTitle></CardHeader>
|
||||
<CardContent>
|
||||
<form method="POST" action="?/create" use:enhance class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-1">
|
||||
<Label for="collected_at">Date *</Label>
|
||||
<Label for="collected_at">{m["labResults_date"]()}</Label>
|
||||
<Input id="collected_at" name="collected_at" type="date" required />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="test_code">LOINC code * <span class="text-xs text-muted-foreground">(e.g. 718-7)</span></Label>
|
||||
<Label for="test_code">{m["labResults_loincCode"]()} <span class="text-xs text-muted-foreground">(e.g. 718-7)</span></Label>
|
||||
<Input id="test_code" name="test_code" required placeholder="718-7" />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="test_name_original">Test name</Label>
|
||||
<Input id="test_name_original" name="test_name_original" placeholder="e.g. Hemoglobin" />
|
||||
<Label for="test_name_original">{m["labResults_testName"]()}</Label>
|
||||
<Input id="test_name_original" name="test_name_original" placeholder={m["labResults_testNamePlaceholder"]()} />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="lab">Lab</Label>
|
||||
<Input id="lab" name="lab" placeholder="e.g. LabCorp" />
|
||||
<Label for="lab">{m["labResults_lab"]()}</Label>
|
||||
<Input id="lab" name="lab" placeholder={m["labResults_labPlaceholder"]()} />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="value_num">Value</Label>
|
||||
<Label for="value_num">{m["labResults_value"]()}</Label>
|
||||
<Input id="value_num" name="value_num" type="number" step="any" />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="unit_original">Unit</Label>
|
||||
<Input id="unit_original" name="unit_original" placeholder="e.g. g/dL" />
|
||||
<Label for="unit_original">{m["labResults_unit"]()}</Label>
|
||||
<Input id="unit_original" name="unit_original" placeholder={m["labResults_unitPlaceholder"]()} />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label>Flag</Label>
|
||||
<Label>{m["labResults_flag"]()}</Label>
|
||||
<input type="hidden" name="flag" value={selectedFlag} />
|
||||
<Select type="single" value={selectedFlag} onValueChange={(v) => (selectedFlag = v)}>
|
||||
<SelectTrigger>{selectedFlag || 'None'}</SelectTrigger>
|
||||
<SelectTrigger>{selectedFlag || m["labResults_flagNone"]()}</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="">None</SelectItem>
|
||||
<SelectItem value="">{m["labResults_flagNone"]()}</SelectItem>
|
||||
{#each flags as f (f)}
|
||||
<SelectItem value={f}>{f}</SelectItem>
|
||||
{/each}
|
||||
|
|
@ -117,7 +118,7 @@
|
|||
</Select>
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<Button type="submit">Add</Button>
|
||||
<Button type="submit">{m.common_add()}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
|
|
@ -128,12 +129,12 @@
|
|||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Date</TableHead>
|
||||
<TableHead>Test</TableHead>
|
||||
<TableHead>LOINC</TableHead>
|
||||
<TableHead>Value</TableHead>
|
||||
<TableHead>Flag</TableHead>
|
||||
<TableHead>Lab</TableHead>
|
||||
<TableHead>{m["labResults_colDate"]()}</TableHead>
|
||||
<TableHead>{m["labResults_colTest"]()}</TableHead>
|
||||
<TableHead>{m["labResults_colLoinc"]()}</TableHead>
|
||||
<TableHead>{m["labResults_colValue"]()}</TableHead>
|
||||
<TableHead>{m["labResults_colFlag"]()}</TableHead>
|
||||
<TableHead>{m["labResults_colLab"]()}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
|
|
@ -165,7 +166,7 @@
|
|||
{:else}
|
||||
<TableRow>
|
||||
<TableCell colspan={6} class="text-center text-muted-foreground py-8">
|
||||
No lab results found.
|
||||
{m["labResults_noResults"]()}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { enhance } from '$app/forms';
|
||||
import type { ActionData, PageData } from './$types';
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
import { Badge } from '$lib/components/ui/badge';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
|
||||
|
|
@ -21,18 +22,26 @@
|
|||
herbal: 'bg-emerald-100 text-emerald-800',
|
||||
other: 'bg-gray-100 text-gray-700'
|
||||
};
|
||||
|
||||
const kindLabels: Record<string, () => string> = {
|
||||
prescription: m["medications_kindPrescription"],
|
||||
otc: m["medications_kindOtc"],
|
||||
supplement: m["medications_kindSupplement"],
|
||||
herbal: m["medications_kindHerbal"],
|
||||
other: m["medications_kindOther"]
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:head><title>Medications — innercontext</title></svelte:head>
|
||||
<svelte:head><title>{m.medications_title()} — innercontext</title></svelte:head>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold tracking-tight">Medications</h2>
|
||||
<p class="text-muted-foreground">{data.medications.length} entries</p>
|
||||
<h2 class="text-2xl font-bold tracking-tight">{m.medications_title()}</h2>
|
||||
<p class="text-muted-foreground">{m.medications_count({ count: data.medications.length })}</p>
|
||||
</div>
|
||||
<Button variant="outline" onclick={() => (showForm = !showForm)}>
|
||||
{showForm ? 'Cancel' : '+ Add medication'}
|
||||
{showForm ? m.common_cancel() : m["medications_addNew"]()}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
@ -40,40 +49,40 @@
|
|||
<div class="rounded-md bg-destructive/10 px-4 py-3 text-sm text-destructive">{form.error}</div>
|
||||
{/if}
|
||||
{#if form?.created}
|
||||
<div class="rounded-md bg-green-50 px-4 py-3 text-sm text-green-700">Medication added.</div>
|
||||
<div class="rounded-md bg-green-50 px-4 py-3 text-sm text-green-700">{m.medications_added()}</div>
|
||||
{/if}
|
||||
|
||||
{#if showForm}
|
||||
<Card>
|
||||
<CardHeader><CardTitle>New medication</CardTitle></CardHeader>
|
||||
<CardHeader><CardTitle>{m["medications_newTitle"]()}</CardTitle></CardHeader>
|
||||
<CardContent>
|
||||
<form method="POST" action="?/create" use:enhance class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-1 col-span-2">
|
||||
<Label>Kind</Label>
|
||||
<Label>{m.medications_kind()}</Label>
|
||||
<input type="hidden" name="kind" value={kind} />
|
||||
<Select type="single" value={kind} onValueChange={(v) => (kind = v)}>
|
||||
<SelectTrigger>{kind}</SelectTrigger>
|
||||
<SelectTrigger>{kindLabels[kind]?.() ?? kind}</SelectTrigger>
|
||||
<SelectContent>
|
||||
{#each kinds as k (k)}
|
||||
<SelectItem value={k}>{k}</SelectItem>
|
||||
<SelectItem value={k}>{kindLabels[k]?.() ?? k}</SelectItem>
|
||||
{/each}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="product_name">Product name *</Label>
|
||||
<Input id="product_name" name="product_name" required placeholder="e.g. Vitamin D3" />
|
||||
<Label for="product_name">{m["medications_productName"]()}</Label>
|
||||
<Input id="product_name" name="product_name" required placeholder={m["medications_productNamePlaceholder"]()} />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="active_substance">Active substance</Label>
|
||||
<Input id="active_substance" name="active_substance" placeholder="e.g. cholecalciferol" />
|
||||
<Label for="active_substance">{m["medications_activeSubstance"]()}</Label>
|
||||
<Input id="active_substance" name="active_substance" placeholder={m["medications_activeSubstancePlaceholder"]()} />
|
||||
</div>
|
||||
<div class="space-y-1 col-span-2">
|
||||
<Label for="notes">Notes</Label>
|
||||
<Label for="notes">{m.medications_notes()}</Label>
|
||||
<Input id="notes" name="notes" />
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<Button type="submit">Add</Button>
|
||||
<Button type="submit">{m.common_add()}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
|
|
@ -86,21 +95,21 @@
|
|||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="rounded-full px-2 py-0.5 text-xs font-medium {kindColors[med.kind] ?? ''}">
|
||||
{med.kind}
|
||||
{kindLabels[med.kind]?.() ?? med.kind}
|
||||
</span>
|
||||
<span class="font-medium">{med.product_name}</span>
|
||||
{#if med.active_substance}
|
||||
<span class="text-sm text-muted-foreground">{med.active_substance}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<Badge variant="secondary">{med.usage_history.length} usages</Badge>
|
||||
<Badge variant="secondary">{m.medications_usages({ count: med.usage_history.length })}</Badge>
|
||||
</div>
|
||||
{#if med.notes}
|
||||
<p class="mt-1 text-sm text-muted-foreground">{med.notes}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-sm text-muted-foreground">No medications recorded.</p>
|
||||
<p class="text-sm text-muted-foreground">{m["medications_noMedications"]()}</p>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue