feat(i18n): the interface speaks both languages
The account has had a `locale` since the beginning and it only chose which grammar quick add read a line with — so picking English changed nothing anybody could see, and the field said so honestly: "Sprog i hurtig tilføjelse". It says "Sprog" now, because it means it. Every component goes through `t()`. Danish is the source: a key missing from English falls through to Danish rather than to the key, because a Danish sentence in an English screen can be read and `settings.notifications.title` cannot. `tag()` is the single place `Intl` is asked. Before, `'da-DK'` was written into a dozen components, and that is the half of a translation that gets left behind — an English interface still saying "24. aug." is exactly the shape of a job half done. There is a smoke test that switches to English and refuses to see a Danish month name in the calendar's heading. Colour and theme names are keys now. `colors.js` keeps its ids untouched — they are what the database stores, and `internal/store` reads them out of that file — but `name` is `'color.tomato'`, resolved where it is drawn. The Inbox keeps its name. It is a project row the server creates at signup, not a string in the interface, and it can be renamed like any other project. Four copies of "3 hours ago" became one, in `web/src/lib/when.js`. They had already drifted: one said "aldrig logget ind", another "aldrig", and two stopped at days where the others carried on to a date. Routes stay Danish. `/indstillinger`, `/projekt`, `/uddelegeret` are bookmarks and they are in every smoke test; translating them buys nothing and costs both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e3c7fc792a
commit
4d8b70918b
@@ -3,6 +3,7 @@
|
||||
import { api, humanMessage } from '$lib/api.js';
|
||||
import { app } from '$lib/stores.svelte.js';
|
||||
import * as push from '$lib/push.js';
|
||||
import { t, plural } from '$lib/i18n.svelte.js';
|
||||
|
||||
// --- web push ----------------------------------------------------------------
|
||||
|
||||
@@ -95,10 +96,9 @@
|
||||
|
||||
<section class="panel">
|
||||
<header>
|
||||
<h2>Notifikationer på enheden</h2>
|
||||
<h2>{t('push.title')}</h2>
|
||||
<p class="hint">
|
||||
Påmindelser og beskeder fra andre, også når fanen er lukket. Beskeden
|
||||
krypteres til din browser undervejs — push-tjenesten kan ikke læse den.
|
||||
{t('push.hint')}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
@@ -106,42 +106,39 @@
|
||||
<p class="empty">…</p>
|
||||
{:else if pushState === 'unsupported'}
|
||||
<p class="hint">
|
||||
Denne browser understøtter det ikke. Safari kræver, at appen er lagt på
|
||||
hjemmeskærmen; over almindelig HTTP virker det kun på localhost.
|
||||
{t('push.unsupported')}
|
||||
</p>
|
||||
{:else if pushState === 'blocked'}
|
||||
<p class="hint">
|
||||
Browseren har blokeret notifikationer for dette site. Det kan kun laves om i
|
||||
browserens egne indstillinger — en side kan ikke spørge igen, når svaret har
|
||||
været nej.
|
||||
{t('push.blocked')}
|
||||
</p>
|
||||
{:else if pushState === 'on'}
|
||||
<p class="hint">Slået til på denne enhed.</p>
|
||||
<p class="hint">{t('push.on')}</p>
|
||||
<div class="row">
|
||||
<button class="secondary" onclick={disablePush} disabled={busy}>Slå fra</button>
|
||||
<button class="secondary" onclick={disablePush} disabled={busy}>{t('push.turnOff')}</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="row">
|
||||
<button class="primary" onclick={enablePush} disabled={busy}>
|
||||
Slå til på denne enhed
|
||||
{t('push.turnOn')}
|
||||
</button>
|
||||
</div>
|
||||
<p class="hint">Gælder kun den browser, du sidder i nu.</p>
|
||||
<p class="hint">{t('push.perBrowser')}</p>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<header>
|
||||
<h2>Seneste</h2>
|
||||
<h2>{t('push.recent')}</h2>
|
||||
{#if unread > 0}
|
||||
<p class="hint">{unread} ulæst{unread === 1 ? '' : 'e'}.</p>
|
||||
<p class="hint">{plural(unread, 'push.unreadOne', 'push.unreadMany')}</p>
|
||||
{/if}
|
||||
</header>
|
||||
|
||||
{#if loading}
|
||||
<p class="empty">…</p>
|
||||
{:else if notifications.length === 0}
|
||||
<p class="empty">Ingenting endnu.</p>
|
||||
<p class="empty">{t('push.none')}</p>
|
||||
{:else}
|
||||
<ul class="list">
|
||||
{#each notifications as note (note.id)}
|
||||
@@ -154,7 +151,7 @@
|
||||
</span>
|
||||
</div>
|
||||
{#if note.task_id && note.project_id}
|
||||
<a class="go" href="/projekt/{note.project_id}">Åbn</a>
|
||||
<a class="go" href="/projekt/{note.project_id}">{t('push.open')}</a>
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
@@ -162,7 +159,7 @@
|
||||
|
||||
{#if unread > 0}
|
||||
<div class="row">
|
||||
<button class="secondary" onclick={markAllRead}>Markér alle som læst</button>
|
||||
<button class="secondary" onclick={markAllRead}>{t('push.markAllRead')}</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -171,11 +168,11 @@
|
||||
{#if version}
|
||||
<section class="panel">
|
||||
<header>
|
||||
<h2>Version</h2>
|
||||
<h2>{t('push.version')}</h2>
|
||||
</header>
|
||||
|
||||
<p class="hint">
|
||||
Kører <span class="mono">{version.current}</span>.
|
||||
{t('push.running')} <span class="mono">{version.current}</span>.
|
||||
{#if version.disabled}
|
||||
Der bliver ikke set efter opdateringer.
|
||||
{:else if version.update_available}
|
||||
@@ -192,7 +189,7 @@
|
||||
{#if version.url}
|
||||
<div class="row">
|
||||
<a class="release" href={version.url} target="_blank" rel="noreferrer noopener">
|
||||
Se udgivelsen
|
||||
{t('push.seeRelease')}
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user