From b6e8624e967121ff1968a745e30a3d8f1d3bbb11 Mon Sep 17 00:00:00 2001 From: jona159 Date: Mon, 3 Aug 2026 14:12:48 +0200 Subject: [PATCH 1/3] feat: translations --- .../device-detail/device-detail-box.tsx | 72 +++++++++++++------ app/components/device-detail/entry-logs.tsx | 44 ++++++------ app/components/device-detail/share-link.tsx | 8 ++- app/components/mydevices/dt/columns.tsx | 1 - app/components/rjsf/arrayFieldTemplate.tsx | 4 +- app/routes/device.$deviceId.edit.logs.tsx | 33 +++++---- public/locales/de/data-table.json | 2 +- public/locales/de/device-detail-box.json | 25 ++++++- public/locales/de/edit-device-logs.json | 13 ++++ public/locales/de/ui-components.json | 3 + public/locales/en/device-detail-box.json | 25 ++++++- public/locales/en/edit-device-logs.json | 13 ++++ public/locales/en/ui-components.json | 3 + 13 files changed, 177 insertions(+), 69 deletions(-) create mode 100644 public/locales/de/edit-device-logs.json create mode 100644 public/locales/en/edit-device-logs.json diff --git a/app/components/device-detail/device-detail-box.tsx b/app/components/device-detail/device-detail-box.tsx index fa9822d65..4f83ca10c 100644 --- a/app/components/device-detail/device-detail-box.tsx +++ b/app/components/device-detail/device-detail-box.tsx @@ -1,5 +1,6 @@ import clsx from 'clsx' import { format, formatDistanceToNow } from 'date-fns' +import { de, enUS } from 'date-fns/locale' import { ChevronUp, Minus, @@ -94,7 +95,8 @@ export default function DeviceDetailBox() { const navigate = useNavigate() const matches = useMatches() const { toast } = useToast() - const { t } = useTranslation('device-detail-box') + const { t, i18n } = useTranslation('device-detail-box') + const dateLocale = i18n.language.startsWith('de') ? de : enUS const sensorIds = new Set() @@ -267,7 +269,7 @@ export default function DeviceDetailBox() { href={getArchiveLink(data.device)} target="_blank" rel="noopener noreferrer" - title="Open archive" + title={t('open_archive')} className="w-full cursor-pointer" > {t('open_archive')} @@ -281,7 +283,7 @@ export default function DeviceDetailBox() { href={data.device.link || '#'} target="_blank" rel="noopener noreferrer" - title="Open external link" + title={t('open_external_link')} className="w-full cursor-pointer" > {t('open_external_link')} @@ -311,7 +313,7 @@ export default function DeviceDetailBox() { {data.deviceImageUrl ? ( device_image ) : ( @@ -323,33 +325,39 @@ export default function DeviceDetailBox() {
{data.device.expiresAt && ( <> )} @@ -359,7 +367,7 @@ export default function DeviceDetailBox() {
- Tags + {t('tags')}
@@ -486,10 +494,10 @@ export default function DeviceDetailBox() { className="flex h-full flex-col" onClick={() => toast({ - title: - 'Cant select more than 2 sensors', - description: - 'Deselect one sensor to select another', + title: t('sensor_limit_title'), + description: t( + 'sensor_limit_description', + ), variant: 'destructive', }) } @@ -550,8 +558,17 @@ export default function DeviceDetailBox() { >

{sensor.lastMeasurement - ? `${formatDistanceToNow(new Date(sensor.lastMeasurement.createdAt), { addSuffix: true })}` - : 'No recent data'} + ? formatDistanceToNow( + new Date( + sensor.lastMeasurement + .createdAt, + ), + { + addSuffix: true, + locale: dateLocale, + }, + ) + : t('no_recent_data')}

@@ -628,8 +645,17 @@ export default function DeviceDetailBox() { >

{sensor.lastMeasurement - ? `${formatDistanceToNow(new Date(sensor.lastMeasurement.createdAt), { addSuffix: true })}` - : 'No recent data'} + ? formatDistanceToNow( + new Date( + sensor.lastMeasurement + .createdAt, + ), + { + addSuffix: true, + locale: dateLocale, + }, + ) + : t('no_recent_data')}

diff --git a/app/components/device-detail/entry-logs.tsx b/app/components/device-detail/entry-logs.tsx index 3765e85de..259d15afb 100644 --- a/app/components/device-detail/entry-logs.tsx +++ b/app/components/device-detail/entry-logs.tsx @@ -1,6 +1,7 @@ import { useMediaQuery } from '@mantine/hooks' import { Activity, Clock, ExternalLink } from 'lucide-react' import { useState } from 'react' +import { useTranslation } from 'react-i18next' import { Button } from '../ui/button' import { Dialog, @@ -37,11 +38,12 @@ export default function EntryLogs({ }) { const [open, setOpen] = useState(false) const isDesktop = useMediaQuery('(min-width: 768px)') + const { t, i18n } = useTranslation('device-detail-box') if (isDesktop) { return (
-

Logs

+

{t('logs')}

@@ -53,7 +55,7 @@ export default function EntryLogs({

- {new Date(entryLogs[0].createdAt).toLocaleString()} + {new Date(entryLogs[0].createdAt).toLocaleString(i18n.language)}
@@ -68,7 +70,7 @@ export default function EntryLogs({ -

Show all logs.

+

{t('show_all_logs')}

@@ -76,13 +78,10 @@ export default function EntryLogs({ - Device Logs - - If this is your device, you can make changes in your device - settings. - + {t('device_logs')} + {t('logs_owner_hint')} - +
@@ -93,7 +92,7 @@ export default function EntryLogs({ return (
-

Logs

+

{t('logs')}

@@ -103,7 +102,7 @@ export default function EntryLogs({

{entryLogs[0].content}

- {new Date(entryLogs[0].createdAt).toLocaleString()} + {new Date(entryLogs[0].createdAt).toLocaleString(i18n.language)}
@@ -116,16 +115,13 @@ export default function EntryLogs({ - Device Logs - - If this is your device, you can make changes in your device - settings. - + {t('device_logs')} + {t('logs_owner_hint')} - + - + @@ -135,9 +131,15 @@ export default function EntryLogs({ ) } -function LogList({ entryLogs = [] }: { entryLogs: LogEntry[] }) { +function LogList({ + entryLogs = [], + locale, +}: { + entryLogs: LogEntry[] + locale: string +}) { return ( - +
{entryLogs.map((log, index) => (
@@ -149,7 +151,7 @@ function LogList({ entryLogs = [] }: { entryLogs: LogEntry[] }) {

{log.content}

- {new Date(log.createdAt).toLocaleString()} + {new Date(log.createdAt).toLocaleString(locale)}
diff --git a/app/components/device-detail/share-link.tsx b/app/components/device-detail/share-link.tsx index 9c61e9fc4..5d3d22fdb 100644 --- a/app/components/device-detail/share-link.tsx +++ b/app/components/device-detail/share-link.tsx @@ -1,10 +1,12 @@ import { Copy, Link } from 'lucide-react' +import { useTranslation } from 'react-i18next' import { Button } from '../ui/button' import { Input } from '../ui/input' import { useToast } from '@/components/ui/use-toast' export default function ShareLink() { const { toast } = useToast() + const { t } = useTranslation('device-detail-box') return (
@@ -84,14 +86,14 @@ export default function ShareLink() { onClick={() => { void navigator.clipboard.writeText(window.location.href) toast({ - title: 'Copied to clipboard', - description: 'Go ahead and share your link! 🎉', + title: t('copied_to_clipboard'), + description: t('share_link_hint'), }) }} className="bg-primary text-primary-foreground inline-flex h-9 transform items-center justify-center rounded-md px-2 py-2 text-sm font-medium shadow-sm transition-transform active:scale-75" > - Copy + {t('copy')}
diff --git a/app/components/mydevices/dt/columns.tsx b/app/components/mydevices/dt/columns.tsx index 609acee91..ac3f88aaa 100644 --- a/app/components/mydevices/dt/columns.tsx +++ b/app/components/mydevices/dt/columns.tsx @@ -202,7 +202,6 @@ export function getColumns( )} navigator.clipboard.writeText(device.id)} className="cursor-pointer" > diff --git a/app/components/rjsf/arrayFieldTemplate.tsx b/app/components/rjsf/arrayFieldTemplate.tsx index 0a90f4536..f0d0be80c 100644 --- a/app/components/rjsf/arrayFieldTemplate.tsx +++ b/app/components/rjsf/arrayFieldTemplate.tsx @@ -1,9 +1,11 @@ import { type ArrayFieldTemplateProps } from '@rjsf/utils' import React from 'react' +import { useTranslation } from 'react-i18next' import { Button } from '~/components/ui/button' import { Card, CardContent } from '~/components/ui/card' export function ArrayFieldTemplate(props: ArrayFieldTemplateProps) { + const { t } = useTranslation('ui-components') const { items, canAdd, onAddClick } = props const renderedItems = React.Children.toArray(items) @@ -18,7 +20,7 @@ export function ArrayFieldTemplate(props: ArrayFieldTemplateProps) { {canAdd && ( )}
diff --git a/app/routes/device.$deviceId.edit.logs.tsx b/app/routes/device.$deviceId.edit.logs.tsx index 662c78e29..03a32f138 100644 --- a/app/routes/device.$deviceId.edit.logs.tsx +++ b/app/routes/device.$deviceId.edit.logs.tsx @@ -1,5 +1,6 @@ import { Save, Trash } from 'lucide-react' import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { data, redirect, @@ -60,7 +61,7 @@ export async function action({ request, params }: Route.ActionArgs) { await createLogEntry({ deviceId: deviceID, content, public: false }) return data({ success: true, - message: 'Log added successfully!', + message: 'log_added', }) } case 'deleteLog': { @@ -68,7 +69,7 @@ export async function action({ request, params }: Route.ActionArgs) { await deleteLogEntry(logEntryId) return data({ success: true, - message: 'Log deleted successfully!', + message: 'log_deleted', }) } case 'togglePublic': { @@ -77,18 +78,15 @@ export async function action({ request, params }: Route.ActionArgs) { await updateLogEntryVisibility(logEntryId, isPublic === 'true') return data({ success: true, - message: 'Log visibility updated!', + message: 'visibility_updated', }) } default: - return data({ success: false, message: 'Unknown action.' }) + return data({ success: false, message: 'unknown_action' }) } } catch (error) { console.error('Error processing action:', error) - return data( - { success: false, message: 'Something went wrong.' }, - { status: 500 }, - ) + return data({ success: false, message: 'error' }, { status: 500 }) } } @@ -96,6 +94,7 @@ export default function Logs() { const { logEntries } = useLoaderData() const actionData = useActionData() const { toast } = useToast() + const { t } = useTranslation('edit-device-logs') const [newLogContent, setNewLogContent] = useState('') const submit = useSubmit() @@ -105,17 +104,17 @@ export default function Logs() { if (actionData) { if (actionData.success) { toast({ - title: actionData.message, + title: t(actionData.message), variant: 'success', }) } else { toast({ - title: actionData.message, + title: t(actionData.message), variant: 'destructive', }) } } - }, [actionData, toast]) + }, [actionData, t, toast]) return (
@@ -127,7 +126,7 @@ export default function Logs() { {/* Title */}
-

Device Logs

+

{t('title')}

@@ -138,7 +137,7 @@ export default function Logs() {
setNewLogContent(e.target.value)} @@ -159,10 +158,10 @@ export default function Logs() { - Content - Created At - Public - Actions + {t('content')} + {t('created_at')} + {t('public')} + {t('actions')} diff --git a/public/locales/de/data-table.json b/public/locales/de/data-table.json index 3a0177e39..4463624a6 100644 --- a/public/locales/de/data-table.json +++ b/public/locales/de/data-table.json @@ -9,7 +9,7 @@ "next_page": "NĂ€chste Seite", "last_page": "Letzte Seite", - "device_id": "Device ID", + "device_id": "GerĂ€te-ID", "actions": "Aktionen", "name": "Name", "createdAt": "Erstellt am", diff --git a/public/locales/de/device-detail-box.json b/public/locales/de/device-detail-box.json index 98c1ea89b..d10bb8084 100644 --- a/public/locales/de/device-detail-box.json +++ b/public/locales/de/device-detail-box.json @@ -12,5 +12,28 @@ "sensor_order_hint_link": "Sensoreinstellungen", "compare_devices": "GerĂ€te vergleichen", "choose_device_for_comparison": "WĂ€hle ein GerĂ€t auf der Karte fĂŒr den Vergleich.", - "open_device_details": "Öffne GerĂ€tedetails" + "open_device_details": "GerĂ€tedetails öffnen", + "device_image": "GerĂ€tebild", + "exposure": "Standort", + "exposure_values": { + "indoor": "Drinnen", + "outdoor": "Draußen", + "mobile": "Mobil" + }, + "unknown": "Unbekannt", + "sensor_model": "Sensormodell", + "last_updated": "Zuletzt aktualisiert", + "created_at": "Erstellt am", + "expires_at": "LĂ€uft ab am", + "tags": "Tags", + "sensor_limit_title": "Es können höchstens zwei Sensoren ausgewĂ€hlt werden", + "sensor_limit_description": "Hebe die Auswahl eines Sensors auf, um einen anderen auszuwĂ€hlen", + "no_recent_data": "Keine aktuellen Daten", + "logs": "Protokoll", + "show_all_logs": "Alle ProtokolleintrĂ€ge anzeigen.", + "device_logs": "GerĂ€teprotokoll", + "logs_owner_hint": "Wenn dies dein GerĂ€t ist, kannst du Änderungen in den GerĂ€teeinstellungen vornehmen.", + "copied_to_clipboard": "In die Zwischenablage kopiert", + "share_link_hint": "Jetzt kannst du deinen Link teilen! 🎉", + "copy": "Kopieren" } diff --git a/public/locales/de/edit-device-logs.json b/public/locales/de/edit-device-logs.json new file mode 100644 index 000000000..f06a52901 --- /dev/null +++ b/public/locales/de/edit-device-logs.json @@ -0,0 +1,13 @@ +{ + "title": "GerĂ€teprotokoll", + "content_placeholder": "Protokolleintrag eingeben", + "content": "Inhalt", + "created_at": "Erstellt am", + "public": "Öffentlich", + "actions": "Aktionen", + "log_added": "Protokolleintrag erfolgreich hinzugefĂŒgt!", + "log_deleted": "Protokolleintrag erfolgreich gelöscht!", + "visibility_updated": "Sichtbarkeit des Protokolleintrags aktualisiert!", + "unknown_action": "Unbekannte Aktion.", + "error": "Etwas ist schiefgelaufen." +} diff --git a/public/locales/de/ui-components.json b/public/locales/de/ui-components.json index 10f573319..3cd4f883d 100644 --- a/public/locales/de/ui-components.json +++ b/public/locales/de/ui-components.json @@ -5,5 +5,8 @@ "callout_title_important": "Wichtig", "callout_title_warning": "Warnung", "callout_title_caution": "Achtung" + }, + "array_field": { + "add_item": "Eintrag hinzufĂŒgen" } } diff --git a/public/locales/en/device-detail-box.json b/public/locales/en/device-detail-box.json index 4214e94d6..2c7f4fe22 100644 --- a/public/locales/en/device-detail-box.json +++ b/public/locales/en/device-detail-box.json @@ -12,5 +12,28 @@ "sensor_order_hint_link": "sensor settings", "compare_devices": "Compare devices", "choose_device_for_comparison": "Choose a device on the map to compare with.", - "open_device_details": "Open device details" + "open_device_details": "Open device details", + "device_image": "Device image", + "exposure": "Exposure", + "exposure_values": { + "indoor": "Indoor", + "outdoor": "Outdoor", + "mobile": "Mobile" + }, + "unknown": "Unknown", + "sensor_model": "Sensor model", + "last_updated": "Last updated", + "created_at": "Created at", + "expires_at": "Expires at", + "tags": "Tags", + "sensor_limit_title": "Can't select more than 2 sensors", + "sensor_limit_description": "Deselect one sensor to select another", + "no_recent_data": "No recent data", + "logs": "Logs", + "show_all_logs": "Show all logs.", + "device_logs": "Device logs", + "logs_owner_hint": "If this is your device, you can make changes in your device settings.", + "copied_to_clipboard": "Copied to clipboard", + "share_link_hint": "Go ahead and share your link! 🎉", + "copy": "Copy" } diff --git a/public/locales/en/edit-device-logs.json b/public/locales/en/edit-device-logs.json new file mode 100644 index 000000000..b0c526bb9 --- /dev/null +++ b/public/locales/en/edit-device-logs.json @@ -0,0 +1,13 @@ +{ + "title": "Device Logs", + "content_placeholder": "Enter log content", + "content": "Content", + "created_at": "Created at", + "public": "Public", + "actions": "Actions", + "log_added": "Log added successfully!", + "log_deleted": "Log deleted successfully!", + "visibility_updated": "Log visibility updated!", + "unknown_action": "Unknown action.", + "error": "Something went wrong." +} diff --git a/public/locales/en/ui-components.json b/public/locales/en/ui-components.json index 2177d5bb8..c11757889 100644 --- a/public/locales/en/ui-components.json +++ b/public/locales/en/ui-components.json @@ -5,5 +5,8 @@ "callout_title_important": "Important", "callout_title_warning": "Warning", "callout_title_caution": "Caution" + }, + "array_field": { + "add_item": "Add item" } } From 76124c68e25a9d66a3974e39e3706efb3024dc66 Mon Sep 17 00:00:00 2001 From: jona159 Date: Mon, 3 Aug 2026 14:13:06 +0200 Subject: [PATCH 2/3] fix: exposure translation --- app/components/device-detail/device-detail-box.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/components/device-detail/device-detail-box.tsx b/app/components/device-detail/device-detail-box.tsx index 4f83ca10c..13a097caf 100644 --- a/app/components/device-detail/device-detail-box.tsx +++ b/app/components/device-detail/device-detail-box.tsx @@ -326,7 +326,13 @@ export default function DeviceDetailBox() { Date: Fri, 7 Aug 2026 14:43:07 +0200 Subject: [PATCH 3/3] feat: add missing --- app/components/mydevices/dt/columns.tsx | 4 ++++ app/routes/device.$deviceId.edit.tsx | 26 ++++++++++++++++++------- public/locales/de/data-table.json | 8 +++++++- public/locales/de/device-overview.json | 9 +++++++++ public/locales/en/data-table.json | 6 ++++++ public/locales/en/device-overview.json | 9 +++++++++ 6 files changed, 54 insertions(+), 8 deletions(-) diff --git a/app/components/mydevices/dt/columns.tsx b/app/components/mydevices/dt/columns.tsx index ac3f88aaa..f3978eb5c 100644 --- a/app/components/mydevices/dt/columns.tsx +++ b/app/components/mydevices/dt/columns.tsx @@ -107,6 +107,10 @@ export function getColumns( ) }, + cell: ({ row }) => { + const exposure = row.original.exposure + return
{t(`exposure_values.${exposure}`)}
+ }, }, /* { accessorKey: "model", diff --git a/app/routes/device.$deviceId.edit.tsx b/app/routes/device.$deviceId.edit.tsx index 30a44490d..b34804c91 100644 --- a/app/routes/device.$deviceId.edit.tsx +++ b/app/routes/device.$deviceId.edit.tsx @@ -48,21 +48,33 @@ export default function EditBox() { const { deviceId } = useParams() const staticNavItems = [ - { title: 'General', href: `/device/${deviceId}/edit/general`, icon: Sheet }, - { title: 'Sensors', href: `/device/${deviceId}/edit/sensors`, icon: Cpu }, { - title: 'Location', + title: t('sidebar.general'), + href: `/device/${deviceId}/edit/general`, + icon: Sheet, + }, + { + title: t('sidebar.sensors'), + href: `/device/${deviceId}/edit/sensors`, + icon: Cpu, + }, + { + title: t('sidebar.location'), href: `/device/${deviceId}/edit/location`, icon: MapPin, }, - { title: 'Logs', href: `/device/${deviceId}/edit/logs`, icon: NotepadText }, { - title: 'Security', + title: t('sidebar.logs'), + href: `/device/${deviceId}/edit/logs`, + icon: NotepadText, + }, + { + title: t('sidebar.security'), href: `/device/${deviceId}/edit/security`, icon: Lock, }, { - title: 'Script', + title: t('sidebar.script'), href: `/device/${deviceId}/edit/script`, icon: FileText, }, @@ -82,7 +94,7 @@ export default function EditBox() { ...staticNavItems, ...integrationItems, { - title: 'Transfer', + title: t('sidebar.transfer'), href: `/device/${deviceId}/edit/transfer`, icon: ArrowRightLeft, }, diff --git a/public/locales/de/data-table.json b/public/locales/de/data-table.json index 4463624a6..2b178ce79 100644 --- a/public/locales/de/data-table.json +++ b/public/locales/de/data-table.json @@ -13,7 +13,13 @@ "actions": "Aktionen", "name": "Name", "createdAt": "Erstellt am", - "exposure": "Exposition", + "exposure": "Standort", + "exposure_values": { + "indoor": "Drinnen", + "outdoor": "Draußen", + "mobile": "Mobil", + "unknown": "Unbekannt" + }, "model": "Modell", "overview": "Überblick", "show_on_map": "Auf der Karte anzeigen", diff --git a/public/locales/de/device-overview.json b/public/locales/de/device-overview.json index f7f24944a..e4cb31017 100644 --- a/public/locales/de/device-overview.json +++ b/public/locales/de/device-overview.json @@ -13,6 +13,15 @@ "api_key_label": "API Key", "device_settings": "GerĂ€tseinstellungen", "manage_device_data": "Verwalte die Daten deines GerĂ€ts.", + "sidebar": { + "general": "Allgemein", + "sensors": "Sensoren", + "location": "Standort", + "logs": "Protokolle", + "security": "Sicherheit", + "script": "Skript", + "transfer": "Übertragen" + }, "show_on_map": "Auf der Karte anzeigen", "device_updated": "GerĂ€t erfolgreich aktualisiert", "view": "Ansicht" diff --git a/public/locales/en/data-table.json b/public/locales/en/data-table.json index 116ed6712..9bae7e98a 100644 --- a/public/locales/en/data-table.json +++ b/public/locales/en/data-table.json @@ -14,6 +14,12 @@ "name": "Name", "createdAt": "Created at", "exposure": "Exposure", + "exposure_values": { + "indoor": "Indoor", + "outdoor": "Outdoor", + "mobile": "Mobile", + "unknown": "Unknown" + }, "model": "Model", "overview": "Overview", "show_on_map": "Show on map", diff --git a/public/locales/en/device-overview.json b/public/locales/en/device-overview.json index d30d8872c..239443e29 100644 --- a/public/locales/en/device-overview.json +++ b/public/locales/en/device-overview.json @@ -13,6 +13,15 @@ "api_key_label": "API Key", "device_settings": "Device settings", "manage_device_data": "Manage your device data.", + "sidebar": { + "general": "General", + "sensors": "Sensors", + "location": "Location", + "logs": "Logs", + "security": "Security", + "script": "Script", + "transfer": "Transfer" + }, "show_on_map": "Show on map", "device_updated": "Device successfully updated", "view": "View"