feat(design): the Copy rule — names, verbs, dates and units (E28 S3g)
The owner's picks (all the recommended options) become DESIGN.md's Copy chapter and a sweep of both languages (742 values, no key drift): - One name per feature: the rail's label, which may shorten it (Alerts -> Notifications, Fiókok -> Felhasználók, Download Center -> Downloads, Channel manager -> Channels); one "sync from YouTube" verb. - Four verbs, four meanings (Delete / Remove / Dismiss / Discard, and their Hungarian pairs), checked against what each action does. - Sentence case, the app's places capitalised; "…" on a command that asks first (a popover or form title got its own key without it); one-sentence messages without a stop or a tick, "Couldn't …"; curly quotes. - Dates only from lib/format.ts, in the UI language: formatDateTime names the month and stops at the minute (it printed "9/27/2026, 8:30:14 AM"), formatDateTimeSec for the log, the audit and the recorder; ui/RelativeTime pairs "3d ago" with the exact minute on hover. local/date-format refuses toLocale*String and Intl date formatters elsewhere. - Units from Intl, short (formatUnit): runtimes, totals, the Scheduler countdown, the stats range, the player's tap skip. "2h 13m" was English in both languages. - src/i18n/copyRule.test.ts holds the machine-readable part (quotes, "...", ticks, bare unit letters).
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { withActiveAccount } from "../lib/activeAccount";
|
||||
import { qk } from "../lib/queryKeys";
|
||||
import { formatDateTime } from "../lib/format";
|
||||
import { formatDate, formatDateTime } from "../lib/format";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { Activity, Bell, Check, Loader2, Monitor, RefreshCw, Trash2, User } from "lucide-react";
|
||||
@@ -519,7 +519,7 @@ function SignInMethods({ me }: { me: Me }) {
|
||||
// and does a one-time "Plex is master" import. Admin-only (rides the server admin token) and only
|
||||
// shown when the Plex module is enabled. Two-way push/pull arrive in later phases.
|
||||
function PlexWatchSync() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
const qc = useQueryClient();
|
||||
const link = useQuery({ queryKey: qk.plexWatchLink(), queryFn: () => api.plexWatchLink() });
|
||||
const enabled = link.data?.sync_enabled ?? false;
|
||||
@@ -562,9 +562,7 @@ function PlexWatchSync() {
|
||||
// Reflect the toggle's target immediately (optimistic) so the switch responds on click rather than
|
||||
// only flipping once the multi-second import returns — otherwise it reads as "nothing happened".
|
||||
const shown = toggle.isPending ? Boolean(toggle.variables) : enabled;
|
||||
const last = link.data?.last_watch_sync_at
|
||||
? new Date(link.data.last_watch_sync_at).toLocaleString(i18n.language)
|
||||
: null;
|
||||
const last = link.data?.last_watch_sync_at ? formatDateTime(link.data.last_watch_sync_at) : null;
|
||||
|
||||
return (
|
||||
<Section title={t("settings.plexSync.title")}>
|
||||
@@ -670,9 +668,7 @@ export function DownloadAuth() {
|
||||
) : (
|
||||
<div className="divide-y divide-border">
|
||||
{hosts.map((h) => {
|
||||
const when = h.updated_at
|
||||
? new Date(h.updated_at).toLocaleDateString(i18n.language)
|
||||
: "";
|
||||
const when = h.updated_at ? formatDate(h.updated_at, i18n.language) : "";
|
||||
const open = openHost === h.host_key;
|
||||
return (
|
||||
<div key={h.host_key} className="py-2" data-testid={`cookie-host-${h.host_key}`}>
|
||||
|
||||
Reference in New Issue
Block a user