From dce8800f93bb37c08a083d0741404e5b52fa2f3a Mon Sep 17 00:00:00 2001 From: Kristian Wind Date: Thu, 20 Aug 2026 07:23:42 +0200 Subject: [PATCH] =?UTF-8?q?fix(web):=20otte=20steder=20hvor=20gr=C3=A6nsef?= =?UTF-8?q?laden=20blev=20ved=20med=20at=20v=C3=A6re=20dansk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skærmbillederne skulle tages på engelsk, og det var dét, der fandt dem: en konto sat til engelsk fik stadig "Forsinket" over de forsinkede opgaver, "Uden sektion" over den første kolonne på tavlen, "Aldrig" i gentagelses- menuen, "hver dag" på rækken, "til instansen · inviteret af" på invitationen, "3 projekter" på kontoen, og hele historikken — nitten hændelser, alle på dansk. En funktion, der kun virker på det ene sprog, er en funktion, der er i stykker. Gentagelsen er den eneste, der ikke bare manglede en nøgle. Serveren skriver `recurrence_text` på dansk og skal blive ved med det: én besked går over WebSocket til alle i projektet på én gang, og de læser ikke det samme sprog. Så de regler, der har et navn, får det i when.js — på den side, der ved, hvis skærm det er — og alt mere specifikt falder tilbage til serverens ord. Mellemrummet i historikken er skrevet ud. Svelte fjerner det, der åbner en blok, så "inviterede" og adressen løb sammen til ét ord. Co-Authored-By: Claude Opus 5 --- web/src/lib/components/BoardView.svelte | 2 +- web/src/lib/components/TaskDetail.svelte | 16 +----- web/src/lib/components/TaskRow.svelte | 11 ++-- web/src/lib/events.js | 56 +++++++++++-------- web/src/lib/locales/da.js | 33 +++++++++++ web/src/lib/locales/en.js | 33 +++++++++++ web/src/lib/when.js | 29 ++++++++++ web/src/routes/+page.svelte | 4 +- .../routes/indstillinger/brugere/+page.svelte | 9 ++- .../indstillinger/historik/+page.svelte | 7 ++- web/src/routes/projekt/[id]/+page.svelte | 5 +- web/src/routes/upcoming/+page.svelte | 8 +-- 12 files changed, 158 insertions(+), 55 deletions(-) diff --git a/web/src/lib/components/BoardView.svelte b/web/src/lib/components/BoardView.svelte index c62e587..f11e47e 100644 --- a/web/src/lib/components/BoardView.svelte +++ b/web/src/lib/components/BoardView.svelte @@ -64,7 +64,7 @@ // The unsectioned column comes first, as it does in the list view: it is where // quick add puts things, so it is where new work appears. let columns = $derived([ - { id: '', name: 'Uden sektion' }, + { id: '', name: t('project.noSection') }, ...sections.map((s) => ({ id: s.id, name: s.name })) ]); diff --git a/web/src/lib/components/TaskDetail.svelte b/web/src/lib/components/TaskDetail.svelte index 4d26252..fd94c79 100644 --- a/web/src/lib/components/TaskDetail.svelte +++ b/web/src/lib/components/TaskDetail.svelte @@ -14,6 +14,7 @@ import { app, projectName } from '$lib/stores.svelte.js'; import { focusOnMount } from '$lib/focus.js'; import { t } from '$lib/i18n.svelte.js'; + import { REPEATS } from '$lib/when.js'; let { task, onclose } = $props(); @@ -29,17 +30,6 @@ /** Who the task can be given to. Only ever more than one on a shared project. */ let members = $state([]); - // The rules worth a menu entry. Anything more specific is typed into the - // title, where the parser understands far more than a list ever could. - const REPEATS = [ - { rule: '', label: 'Aldrig' }, - { rule: 'FREQ=DAILY', label: 'Hver dag' }, - { rule: 'FREQ=WEEKLY', label: 'Hver uge' }, - { rule: 'FREQ=WEEKLY;INTERVAL=2', label: 'Hver anden uge' }, - { rule: 'FREQ=MONTHLY', label: t('detail.monthly') }, - { rule: 'FREQ=YEARLY', label: t('detail.yearly') } - ]; - let subtasks = $state([]); let comments = $state([]); let attachments = $state([]); @@ -452,13 +442,13 @@ diff --git a/web/src/lib/components/TaskRow.svelte b/web/src/lib/components/TaskRow.svelte index 57f77b5..b4470dd 100644 --- a/web/src/lib/components/TaskRow.svelte +++ b/web/src/lib/components/TaskRow.svelte @@ -10,9 +10,12 @@ */ import { app } from '$lib/stores.svelte.js'; import { t, tag } from '$lib/i18n.svelte.js'; + import { repeatLabel } from '$lib/when.js'; let { task, onedit } = $props(); + let repeats = $derived(repeatLabel(task.recurrence_rule, task.recurrence_text)); + let leaving = $state(false); const priorityLabel = (p) => (p === 4 ? t('task.noPriority') : t('task.priority', { n: p })); @@ -84,7 +87,7 @@ {task.description} {/if} - {#if assignee || due || task.labels?.length || task.recurrence_text || task.subtask_count || task.attachment_count} + {#if assignee || due || task.labels?.length || repeats || task.subtask_count || task.attachment_count} {#if assignee} {eventName(entry.event)}{#if eventDetail(entry)} - {eventDetail(entry)}{/if}{eventName(entry.event)}{#if eventDetail(entry)}{' '}{eventDetail(entry)}{/if} {entry.project_name} diff --git a/web/src/routes/projekt/[id]/+page.svelte b/web/src/routes/projekt/[id]/+page.svelte index 5ed5a8e..a8df623 100644 --- a/web/src/routes/projekt/[id]/+page.svelte +++ b/web/src/routes/projekt/[id]/+page.svelte @@ -581,8 +581,9 @@ >{entry.user_name || t('history.deletedAccount')} - {eventName(entry.event)}{#if eventDetail(entry)} - {eventDetail(entry)}{/if} + {eventName(entry.event)}{#if eventDetail(entry)}{' '}{eventDetail(entry)}{/if} {when(entry.created_at)} diff --git a/web/src/routes/upcoming/+page.svelte b/web/src/routes/upcoming/+page.svelte index 9caf3a1..635f807 100644 --- a/web/src/routes/upcoming/+page.svelte +++ b/web/src/routes/upcoming/+page.svelte @@ -20,7 +20,7 @@ import TaskRow from '$lib/components/TaskRow.svelte'; import QuickAdd from '$lib/components/QuickAdd.svelte'; import CalendarView from '$lib/components/CalendarView.svelte'; - import { t } from '$lib/i18n.svelte.js'; + import { t, tag } from '$lib/i18n.svelte.js'; let days = $state([]); @@ -48,9 +48,9 @@ const today = new Date(new Date().toDateString()); const diff = Math.round((day - today) / 86400000); - if (diff === 0) return 'I dag'; - if (diff === 1) return 'I morgen'; - return day.toLocaleDateString('da-DK', { weekday: 'long', day: 'numeric', month: 'short' }); + if (diff === 0) return t('task.today'); + if (diff === 1) return t('task.tomorrow'); + return day.toLocaleDateString(tag(), { weekday: 'long', day: 'numeric', month: 'short' }); } // Reading tasks back out of the store keeps a completed row disappearing