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
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* How long ago something was, in the account's language.
|
||||
*
|
||||
* Four pages had a copy of this — the user list, the error log, the history page
|
||||
* and the backup panel — and they had already drifted: one said "aldrig logget
|
||||
* ind", another "aldrig", and two of them stopped at days while the others carried
|
||||
* on to a date. Four copies is three chances for the fifth one to be different
|
||||
* again.
|
||||
*
|
||||
* Written by hand rather than with `Intl.RelativeTimeFormat`, which would say
|
||||
* "3 hours ago" and "for 3 timer siden" correctly and cannot say "lige nu" — and
|
||||
* "just now" is the answer for most of what these lists show.
|
||||
*/
|
||||
import { t, tag } from './i18n.svelte.js';
|
||||
|
||||
export function ago(iso, { never = 'when.never' } = {}) {
|
||||
if (!iso) return t(never);
|
||||
|
||||
const then = new Date(iso);
|
||||
const seconds = Math.round((Date.now() - then) / 1000);
|
||||
|
||||
if (seconds < 60) return t('when.justNow');
|
||||
if (seconds < 3600) return t('when.minutes', { n: Math.floor(seconds / 60) });
|
||||
if (seconds < 86400) return t('when.hours', { n: Math.floor(seconds / 3600) });
|
||||
if (seconds < 604800) return t('when.days', { n: Math.floor(seconds / 86400) });
|
||||
return then.toLocaleDateString(tag(), { day: 'numeric', month: 'short', year: 'numeric' });
|
||||
}
|
||||
|
||||
/** A date and a time, for a list where the time of day matters. */
|
||||
export function stamp(iso) {
|
||||
return new Date(iso).toLocaleString(tag(), {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
/** A day, without the year — for something inside the next few months. */
|
||||
export function shortDate(iso) {
|
||||
return new Date(iso).toLocaleDateString(tag(), { day: 'numeric', month: 'short' });
|
||||
}
|
||||
|
||||
/** A day with the year, for something that could be any distance away. */
|
||||
export function fullDate(iso) {
|
||||
return new Date(iso).toLocaleDateString(tag(), {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric'
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user