Gonçalo Rodrigues 4b7c01e632 feat(finance): i18n — TOML-based translations for all personal finance templates
Adds a full translation layer (English + European Portuguese) using
BurntSushi/toml with go:embed. Locale detection reads the lang cookie,
falls back to Accept-Language, then defaults to "en". A language switcher
in the nav writes the cookie and redirects back. All 20 personal finance
templates now use {{.T.Get "key"}} for every UI string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 22:32:49 +01:00

151 lines
6.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{{define "content"}}
{{$d := .}}
<h1 style="margin-bottom:24px;">{{$d.T.Get "import.title"}}</h1>
{{if $d.Error}}
<div class="card" style="border-color:var(--red); background:var(--red-dim);">
<span style="color:var(--red); font-size:13.5px;">⚠ {{$d.Error}}</span>
</div>
{{end}}
{{if $d.Preview}}
<!-- Preview table -->
<div class="card animate-on-scroll">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px; flex-wrap:wrap; gap:8px;">
<div>
<h2 style="font-size:15px; font-weight:700; color:var(--text); text-transform:none; letter-spacing:0;">{{$d.T.Get "import.preview.section_title"}}</h2>
<p class="text-muted" style="margin-top:3px;">
{{$d.Preview.Total}} {{$d.T.Get "import.preview.rows_label"}}
{{if $d.DuplicateCount}}
<span style="color:var(--yellow, #f59e0b); font-weight:600;">{{$d.DuplicateCount}} {{$d.T.Get "import.preview.already_imported"}}</span> {{$d.T.Get "import.preview.skip_note"}}
{{end}}
</p>
</div>
<a href="/import" class="btn btn-outline btn-sm">{{$d.T.Get "import.preview.btn_back"}}</a>
</div>
<form method="POST" action="/import/confirm">
<input type="hidden" name="account_id" value="{{$d.Preview.AccountID}}">
<input type="hidden" name="format" value="{{$d.SelectedFormat}}">
<input type="hidden" name="raw_data" value="{{$d.RawData}}">
<div class="table-wrap">
<table>
<thead>
<tr>
<th>{{$d.T.Get "import.preview.col_date"}}</th>
<th>{{$d.T.Get "import.preview.col_description"}}</th>
<th class="text-right">{{$d.T.Get "import.preview.col_amount"}}</th>
<th>{{$d.T.Get "import.preview.col_category"}}</th>
</tr>
</thead>
<tbody>
{{range $i, $row := $d.Preview.Rows}}
<tr {{if $row.Duplicate}}style="opacity:0.4;"{{end}}>
<td style="white-space:nowrap; color:var(--text2);">{{$row.Date}}</td>
<td style="max-width:280px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
{{$row.Description}}
{{if $row.Duplicate}}<span style="font-size:11px; font-weight:600; color:var(--muted); margin-left:6px;">{{$d.T.Get "import.preview.duplicate_label"}}</span>{{end}}
</td>
<td class="cents {{if lt $row.AmountCents 0}}negative{{else}}positive{{end}}" style="font-weight:600; white-space:nowrap;">
{{if lt $row.AmountCents 0}}{{else}}+{{end}}€{{cents (centsAbs $row.AmountCents)}}
</td>
<td>
{{if $row.Duplicate}}
<span style="font-size:12px; color:var(--muted);"></span>
{{else}}
<select name="categories" class="cat-select" data-selected="{{$row.Category}}"
style="font-size:12.5px; padding:5px 8px; border:1.5px solid var(--border2);
border-radius:6px; background:var(--bg2); color:var(--text);
border-left-width:4px; cursor:pointer; min-width:130px;">
{{range $cat := $d.Categories}}
<option value="{{$cat}}" {{if eq $cat $row.Category}}selected{{end}}>{{$cat}}</option>
{{end}}
</select>
{{end}}
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div style="display:flex; gap:10px; margin-top:18px; flex-wrap:wrap;">
<button type="submit" class="btn btn-primary">{{$d.T.Get "import.preview.btn_confirm"}}</button>
<a href="/import" class="btn btn-outline">{{$d.T.Get "import.preview.btn_cancel"}}</a>
</div>
</form>
</div>
<script>
const catColors = { {{range $k,$v := $d.CategoryColors}}"{{$k}}":"{{$v}}",{{end}} };
document.querySelectorAll('.cat-select').forEach(sel => {
function paint() {
const color = catColors[sel.value] || '';
sel.style.borderLeftColor = color || 'var(--border2)';
}
sel.addEventListener('change', paint);
paint();
});
</script>
{{else}}
<!-- Upload forms -->
<div class="grid-2" style="align-items:start;">
<div class="card animate-on-scroll">
<h2 style="font-size:15px; font-weight:700; color:var(--text); text-transform:none; letter-spacing:0; margin-bottom:18px;">
{{$d.T.Get "import.upload.bank_section_title"}}
</h2>
<form method="POST" action="/import/preview" enctype="multipart/form-data">
<div class="form-group">
<label>{{$d.T.Get "import.upload.label_account"}}</label>
<select name="account_id" required>
<option value="">{{$d.T.Get "import.upload.placeholder_account"}}</option>
{{range $d.Accounts}}
<option value="{{.ID}}">{{.Name}} ({{.Type}})</option>
{{end}}
</select>
</div>
<div class="form-group">
<label>{{$d.T.Get "import.upload.label_format"}}</label>
<select name="format">
<option value="cgd">{{$d.T.Get "import.upload.format_cgd"}}</option>
<option value="traderepublic">{{$d.T.Get "import.upload.format_tr"}}</option>
<option value="generic">{{$d.T.Get "import.upload.format_generic"}}</option>
</select>
</div>
<div class="form-group">
<label>{{$d.T.Get "import.upload.label_csv_file"}}</label>
<input type="file" name="file" accept=".csv,.txt" required
style="padding:10px; cursor:pointer; font-size:13px;">
</div>
<button type="submit" class="btn btn-primary" style="width:100%;">{{$d.T.Get "import.upload.btn_preview"}}</button>
</form>
</div>
<div class="card animate-on-scroll">
<h2 style="font-size:15px; font-weight:700; color:var(--text); text-transform:none; letter-spacing:0; margin-bottom:18px;">
{{$d.T.Get "import.upload.securities_title"}}
</h2>
<p class="text-muted" style="margin-bottom:16px; font-size:13px; line-height:1.6;">
{{$d.T.Get "import.upload.securities_desc"}}
</p>
<form method="POST" action="/import/securities" enctype="multipart/form-data">
<div class="form-group">
<label>{{$d.T.Get "import.upload.label_securities_file"}}</label>
<input type="file" name="file" accept=".csv,.txt" required
style="padding:10px; cursor:pointer; font-size:13px;">
</div>
<button type="submit" class="btn btn-primary" style="width:100%;">{{$d.T.Get "import.upload.btn_import_trades"}}</button>
</form>
<div style="margin-top:20px; padding-top:16px; border-top:1px solid var(--border);">
<p class="text-muted" style="font-size:12px; line-height:1.6;">
{{$d.T.Get "import.upload.after_import_note"}} <a href="/portfolio" style="color:var(--accent);">Portfolio</a>
</p>
</div>
</div>
</div>
{{end}}
{{end}}