From b7176fc8f37fd6e717be75c518407934a3340610 Mon Sep 17 00:00:00 2001 From: Arsfy Date: Sun, 27 Oct 2024 14:30:34 +0800 Subject: [PATCH 1/5] add copy linux install command --- beszel/site/src/components/add-system.tsx | 243 +++++++++++++++------- 1 file changed, 167 insertions(+), 76 deletions(-) diff --git a/beszel/site/src/components/add-system.tsx b/beszel/site/src/components/add-system.tsx index 85abeff..56cd2d0 100644 --- a/beszel/site/src/components/add-system.tsx +++ b/beszel/site/src/components/add-system.tsx @@ -9,6 +9,12 @@ import { DialogTrigger, } from '@/components/ui/dialog' import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "@/components/ui/tabs" import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' @@ -41,6 +47,10 @@ export function AddSystemButton({ className }: { className?: string }) { # FILESYSTEM: /dev/sda1 # override the root partition / device for disk I/O stats`) } + function copyInstallCommand(port: string) { + copyToClipboard(`curl -sL https://raw.githubusercontent.com/henrygd/beszel/main/supplemental/scripts/install-agent.sh -o install-agent.sh && chmod +x install-agent.sh && ./install-agent.sh -p ${port} -k "${publicKey}"`) + } + async function handleSubmit(e: SubmitEvent) { e.preventDefault() const formData = new FormData(e.target as HTMLFormElement) @@ -68,82 +78,163 @@ export function AddSystemButton({ className }: { className?: string }) { - - Add New System - - The agent must be running on the system to connect. Copy the{' '} - docker-compose.yml for the agent - below. - - -
-
-
- - -
-
- - -
-
- - -
-
- - -
- - - - - - -

Click to copy

-
-
-
-
-
- - - - -
+ + + Add New System + + Docker + Binary + + + + + The agent must be running on the system to connect. Copy the{' '} + docker-compose.yml for the agent + below. + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + + + + + +

Click to copy

+
+
+
+
+
+ + + + +
+
+ + + The agent must be running on the system to connect. Copy the{' '} + install command for the agent below. + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + + + + + +

Click to copy

+
+
+
+
+
+ + + + +
+
+
- + ) } From 376e8d46213fd6cb2db92cec80e2a8b0d28302e9 Mon Sep 17 00:00:00 2001 From: Arsfy Date: Mon, 28 Oct 2024 13:37:21 +0800 Subject: [PATCH 2/5] ctrl k & i18n --- beszel/site/bun.lockb | Bin 155650 -> 158101 bytes beszel/site/package.json | 3 + beszel/site/src/components/add-system.tsx | 48 ++++----- .../src/components/alerts/alert-button.tsx | 15 +-- beszel/site/src/components/lang-toggle.tsx | 42 ++++++++ beszel/site/src/components/mode-toggle.tsx | 10 +- beszel/site/src/components/routes/home.tsx | 24 +++-- .../components/routes/settings/general.tsx | 45 +++++---- .../src/components/routes/settings/layout.tsx | 49 ++++----- .../routes/settings/notifications.tsx | 13 ++- beszel/site/src/components/routes/system.tsx | 10 +- .../systems-table/systems-table.tsx | 37 +++---- beszel/site/src/lib/i18n.ts | 22 ++++ beszel/site/src/lib/languages.json | 30 ++++++ beszel/site/src/locales/en/translation.json | 94 ++++++++++++++++++ beszel/site/src/locales/es/translation.json | 94 ++++++++++++++++++ beszel/site/src/main.tsx | 19 ++-- 17 files changed, 441 insertions(+), 114 deletions(-) create mode 100644 beszel/site/src/components/lang-toggle.tsx create mode 100644 beszel/site/src/lib/i18n.ts create mode 100644 beszel/site/src/lib/languages.json create mode 100644 beszel/site/src/locales/en/translation.json create mode 100644 beszel/site/src/locales/es/translation.json diff --git a/beszel/site/bun.lockb b/beszel/site/bun.lockb index 32b1d2dd37edb3530fd1baeb1969ecebd49a9520..d316e9006091c17741f49c2edfa7ad8b0dd36331 100755 GIT binary patch delta 29286 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 delta 27879 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�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 diff --git a/beszel/site/package.json b/beszel/site/package.json index ef0ec63..0763443 100644 --- a/beszel/site/package.json +++ b/beszel/site/package.json @@ -31,11 +31,14 @@ "clsx": "^2.1.1", "cmdk": "^1.0.0", "d3-time": "^3.1.0", + "i18next": "^23.16.4", + "i18next-browser-languagedetector": "^8.0.0", "lucide-react": "^0.452.0", "nanostores": "^0.11.3", "pocketbase": "^0.21.5", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-i18next": "^15.1.0", "recharts": "^2.13.0", "tailwind-merge": "^2.5.4", "tailwindcss-animate": "^1.0.7", diff --git a/beszel/site/src/components/add-system.tsx b/beszel/site/src/components/add-system.tsx index 56cd2d0..0662670 100644 --- a/beszel/site/src/components/add-system.tsx +++ b/beszel/site/src/components/add-system.tsx @@ -24,8 +24,11 @@ import { useState, useRef, MutableRefObject } from 'react' import { useStore } from '@nanostores/react' import { cn, copyToClipboard, isReadOnlyUser } from '@/lib/utils' import { navigate } from './router' +import { useTranslation } from 'react-i18next' export function AddSystemButton({ className }: { className?: string }) { + const { t } = useTranslation() + const [open, setOpen] = useState(false) const port = useRef() as MutableRefObject const publicKey = useStore($publicKey) @@ -74,41 +77,40 @@ export function AddSystemButton({ className }: { className?: string }) { className={cn('flex gap-1 max-xs:h-[2.4rem]', className, isReadOnlyUser() && 'hidden')} > - Add System + {t('add')} {t('system')} - Add New System + {t('add_system.add_new_system')} Docker - Binary + {t('add_system.binary')} - The agent must be running on the system to connect. Copy the{' '} - docker-compose.yml for the agent - below. + {t('add_system.dialog_des_1')}{' '} + docker-compose.yml {t('add_system.dialog_des_2')}
-

Click to copy

+

{t('add_system.click_to_copy')}

@@ -154,34 +156,34 @@ export function AddSystemButton({ className }: { className?: string }) { variant={'ghost'} onClick={() => copyDockerCompose(port.current.value)} > - Copy docker compose + {t('copy')} docker compose - + - The agent must be running on the system to connect. Copy the{' '} - install command for the agent below. + {t('add_system.dialog_des_1')}{' '} + install command {t('add_system.dialog_des_2')}
-

Click to copy

+

{t('add_system.click_to_copy')}

@@ -227,14 +229,14 @@ export function AddSystemButton({ className }: { className?: string }) { variant={'ghost'} onClick={() => copyInstallCommand(port.current.value)} > - Copy linux command + {t('copy')} linux {t('add_system.command')} - + - + ) } diff --git a/beszel/site/src/components/alerts/alert-button.tsx b/beszel/site/src/components/alerts/alert-button.tsx index 929e69a..ecdc569 100644 --- a/beszel/site/src/components/alerts/alert-button.tsx +++ b/beszel/site/src/components/alerts/alert-button.tsx @@ -17,6 +17,7 @@ import { Link } from '../router' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Checkbox } from '../ui/checkbox' import { SystemAlert, SystemAlertGlobal } from './alerts-system' +import { useTranslation } from 'react-i18next' export default memo(function AlertsButton({ system }: { system: SystemRecord }) { const alerts = useStore($alerts) @@ -54,6 +55,8 @@ function TheContent({ }: { data: { system: SystemRecord; alerts: AlertRecord[]; systemAlerts: AlertRecord[] } }) { + const { t } = useTranslation() + const [overwriteExisting, setOverwriteExisting] = useState(false) const systems = $systems.get() @@ -69,13 +72,13 @@ function TheContent({ return ( <> - Alerts + {t('alerts.title')} - See{' '} + {t('alerts.subtitle_1')}{' '} - notification settings + {t('alerts.notification_settings')} {' '} - to configure how you receive alerts. + {t('alerts.subtitle_2')} @@ -86,7 +89,7 @@ function TheContent({ - All systems + {t('all_systems')} @@ -107,7 +110,7 @@ function TheContent({ checked={overwriteExisting} onCheckedChange={setOverwriteExisting} /> - Overwrite existing alerts + {t('alerts.overwrite_existing_alerts')}
{data.map((d) => ( diff --git a/beszel/site/src/components/lang-toggle.tsx b/beszel/site/src/components/lang-toggle.tsx new file mode 100644 index 0000000..46ffa80 --- /dev/null +++ b/beszel/site/src/components/lang-toggle.tsx @@ -0,0 +1,42 @@ +import { useEffect } from 'react' +import { GlobeIcon, Languages } from 'lucide-react' + +import { Button } from '@/components/ui/button' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' +import { useTranslation } from 'react-i18next' +import languages from '../lib/languages.json' + +export function LangToggle() { + const { i18n } = useTranslation(); + + useEffect(() => { + document.documentElement.lang = i18n.language; + }, [i18n.language]); + + return ( + + + + + + {languages.map(({ lang, label }) => ( + i18n.changeLanguage(lang)} + > + {label} + + ))} + + + ) +} diff --git a/beszel/site/src/components/mode-toggle.tsx b/beszel/site/src/components/mode-toggle.tsx index 35e1bc3..257c70c 100644 --- a/beszel/site/src/components/mode-toggle.tsx +++ b/beszel/site/src/components/mode-toggle.tsx @@ -8,8 +8,10 @@ import { DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { useTheme } from '@/components/theme-provider' +import { useTranslation } from 'react-i18next' export function ModeToggle() { + const { t } = useTranslation() const { setTheme } = useTheme() return ( @@ -18,21 +20,21 @@ export function ModeToggle() { setTheme('light')}> - Light + {t('themes.light')} setTheme('dark')}> - Dark + {t('themes.dark')} setTheme('system')}> - System + {t('themes.system')} diff --git a/beszel/site/src/components/routes/home.tsx b/beszel/site/src/components/routes/home.tsx index 0a0f646..8bcca40 100644 --- a/beszel/site/src/components/routes/home.tsx +++ b/beszel/site/src/components/routes/home.tsx @@ -9,10 +9,15 @@ import { AlertRecord, SystemRecord } from '@/types' import { Input } from '../ui/input' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' import { Link } from '../router' +import { useTranslation } from 'react-i18next' const SystemsTable = lazy(() => import('../systems-table/systems-table')) +const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; + export default function () { + const { t } = useTranslation() + const hubVersion = useStore($hubVersion) const [filter, setFilter] = useState() const alerts = useStore($alerts) @@ -58,7 +63,7 @@ export default function () {
- Active Alerts + {t('home.active_alerts')}
@@ -76,8 +81,11 @@ export default function () { {alert.sysname} {info.name} - Exceeds {alert.value} - {info.unit} average in last {alert.min} min + {t('active_des', { + value: alert.value, + unit: info.unit, + minutes: alert.min + })}
- All Systems + {t('all_systems')} - Updated in real time. Press{' '} + {t('home.subtitle_1')}{' '} - K + {isMac ? '⌘' : "Ctrl"}K {' '} - to open the command palette. + {t('home.subtitle_2')}
setFilter(e.target.value)} className="w-full md:w-56 lg:w-80 ml-auto px-4" /> diff --git a/beszel/site/src/components/routes/settings/general.tsx b/beszel/site/src/components/routes/settings/general.tsx index 9cc331a..f16eca7 100644 --- a/beszel/site/src/components/routes/settings/general.tsx +++ b/beszel/site/src/components/routes/settings/general.tsx @@ -12,10 +12,18 @@ import { Separator } from '@/components/ui/separator' import { LoaderCircleIcon, SaveIcon } from 'lucide-react' import { UserSettings } from '@/types' import { saveSettings } from './layout' -import { useState } from 'react' +import { useState, useEffect } from 'react' // import { Input } from '@/components/ui/input' +import { useTranslation } from 'react-i18next' +import languages from '../../../lib/languages.json' export default function SettingsProfilePage({ userSettings }: { userSettings: UserSettings }) { + const { t, i18n } = useTranslation() + + useEffect(() => { + document.documentElement.lang = i18n.language; + }, [i18n.language]); + const [isLoading, setIsLoading] = useState(false) async function handleSubmit(e: React.FormEvent) { @@ -30,46 +38,49 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us return (
-

General

+

{t('settings.general.title')}

- Change general application options. + {t('settings.general.subtitle')}

- {/*
-

Language

+

{t('settings.general.language.title')}

- Internationalization will be added in a future release. Please see the{' '} + {t('settings.general.language.subtitle_1')}{' '} - discussion on GitHub + Crowdin {' '} - for more details. + {t('settings.general.language.subtitle_2')}

- i18n.changeLanguage(lang)}> - English + {languages.map((lang) => ( + + {lang.label} + + ))} -
*/} +
-

Chart options

+

{t('settings.general.chart_options.title')}

- Adjust display options for charts. + {t('settings.general.chart_options.subtitle')}

- Sets the default time range for charts when a system is viewed. + {t('settings.general.chart_options.default_time_period_des')}

@@ -102,7 +113,7 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us ) : ( )} - Save settings + {t('settings.save_settings')}
diff --git a/beszel/site/src/components/routes/settings/layout.tsx b/beszel/site/src/components/routes/settings/layout.tsx index 05d547b..29b3736 100644 --- a/beszel/site/src/components/routes/settings/layout.tsx +++ b/beszel/site/src/components/routes/settings/layout.tsx @@ -13,27 +13,7 @@ import General from './general.tsx' import Notifications from './notifications.tsx' import ConfigYaml from './config-yaml.tsx' import { isAdmin } from '@/lib/utils.ts' - -const sidebarNavItems = [ - { - title: 'General', - href: '/settings/general', - icon: SettingsIcon, - }, - { - title: 'Notifications', - href: '/settings/notifications', - icon: BellIcon, - }, -] - -if (isAdmin()) { - sidebarNavItems.push({ - title: 'YAML Config', - href: '/settings/config', - icon: FileSlidersIcon, - }) -} +import { useTranslation } from 'react-i18next' export async function saveSettings(newSettings: Partial) { try { @@ -64,6 +44,29 @@ export async function saveSettings(newSettings: Partial) { } export default function SettingsLayout() { + const { t } = useTranslation() + + const sidebarNavItems = [ + { + title: t('settings.general.title'), + href: '/settings/general', + icon: SettingsIcon, + }, + { + title: t('settings.notifications.title'), + href: '/settings/notifications', + icon: BellIcon, + }, + ] + + if (isAdmin()) { + sidebarNavItems.push({ + title: 'YAML Config', + href: '/settings/config', + icon: FileSlidersIcon, + }) + } + const page = useStore($router) useEffect(() => { @@ -77,8 +80,8 @@ export default function SettingsLayout() { return ( - Settings - Manage display and notification preferences. + {t('settings.settings')} + {t('settings.subtitle')} diff --git a/beszel/site/src/components/routes/settings/notifications.tsx b/beszel/site/src/components/routes/settings/notifications.tsx index 503ccef..372bbd6 100644 --- a/beszel/site/src/components/routes/settings/notifications.tsx +++ b/beszel/site/src/components/routes/settings/notifications.tsx @@ -12,6 +12,7 @@ import { UserSettings } from '@/types' import { saveSettings } from './layout' import * as v from 'valibot' import { isAdmin } from '@/lib/utils' +import { useTranslation } from 'react-i18next' interface ShoutrrrUrlCardProps { url: string @@ -25,6 +26,8 @@ const NotificationSchema = v.object({ }) const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSettings }) => { + const { t } = useTranslation() + const [webhooks, setWebhooks] = useState(userSettings.webhooks ?? []) const [emails, setEmails] = useState(userSettings.emails ?? []) const [isLoading, setIsLoading] = useState(false) @@ -69,13 +72,13 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting return (
-

Notifications

+

{t('settings.notifications.title')}

- Configure how you receive alert notifications. + {t('settings.notifications.subtitle_1')}

- Looking instead for where to create alerts? Click the bell{' '} - icons in the systems table. + {t('settings.notifications.subtitle_2')}{' '} + {t('settings.notifications.subtitle_3')}

@@ -161,7 +164,7 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting ) : ( )} - Save settings + {t('settings.save_settings')}
diff --git a/beszel/site/src/components/routes/system.tsx b/beszel/site/src/components/routes/system.tsx index 94b20e3..ff41b31 100644 --- a/beszel/site/src/components/routes/system.tsx +++ b/beszel/site/src/components/routes/system.tsx @@ -21,6 +21,7 @@ import { ChartAverage, ChartMax, Rows, TuxIcon } from '../ui/icons' import { useIntersectionObserver } from '@/lib/use-intersection-observer' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select' import { timeTicks } from 'd3-time' +import { useTranslation } from 'react-i18next' const AreaChartDefault = lazy(() => import('../charts/area-chart')) const ContainerChart = lazy(() => import('../charts/container-chart')) @@ -374,9 +375,8 @@ export default function SystemDetail({ name }: { name: string }) { : null} > ) => { @@ -534,7 +536,7 @@ function ContainerFilterBar() { return ( <> ) { const val = info.getValue() as number @@ -102,6 +103,8 @@ function sortableHeader( } export default function SystemsTable({ filter }: { filter?: string }) { + const { t } = useTranslation() + const data = useStore($systems) const hubVersion = useStore($hubVersion) const [sorting, setSorting] = useState([]) @@ -145,32 +148,32 @@ export default function SystemsTable({ filter }: { filter?: string }) { ) }, - header: ({ column }) => sortableHeader(column, 'System', ServerIcon), + header: ({ column }) => sortableHeader(column, t('systems_table.system'), ServerIcon), }, { accessorKey: 'info.cpu', invertSorting: true, cell: CellFormatter, - header: ({ column }) => sortableHeader(column, 'CPU', CpuIcon), + header: ({ column }) => sortableHeader(column, t('systems_table.cpu'), CpuIcon), }, { accessorKey: 'info.mp', invertSorting: true, cell: CellFormatter, - header: ({ column }) => sortableHeader(column, 'Memory', MemoryStickIcon), + header: ({ column }) => sortableHeader(column, t('systems_table.memory'), MemoryStickIcon), }, { accessorKey: 'info.dp', invertSorting: true, cell: CellFormatter, - header: ({ column }) => sortableHeader(column, 'Disk', HardDriveIcon), + header: ({ column }) => sortableHeader(column, t('systems_table.disk'), HardDriveIcon), }, { accessorFn: (originalRow) => originalRow.info.b || 0, id: 'n', invertSorting: true, size: 115, - header: ({ column }) => sortableHeader(column, 'Net', EthernetIcon), + header: ({ column }) => sortableHeader(column, t('systems_table.net'), EthernetIcon), cell: (info) => { const val = info.getValue() as number return ( @@ -184,7 +187,7 @@ export default function SystemsTable({ filter }: { filter?: string }) { accessorKey: 'info.v', invertSorting: true, size: 50, - header: ({ column }) => sortableHeader(column, 'Agent', WifiIcon, true), + header: ({ column }) => sortableHeader(column, t('systems_table.agent'), WifiIcon, true), cell: (info) => { const version = info.getValue() as string if (!version || !hubVersion) { @@ -217,7 +220,7 @@ export default function SystemsTable({ filter }: { filter?: string }) { @@ -233,44 +236,42 @@ export default function SystemsTable({ filter }: { filter?: string }) { {status === 'paused' ? ( <> - Resume + {t('systems_table.resume')} ) : ( <> - Pause + {t('systems_table.pause')} )} copyToClipboard(host)}> - Copy host + {t('systems_table.copy_host')} - Delete + {t('systems_table.delete')} - Are you sure you want to delete {name}? + {t('systems_table.delete_confirm', { name })} - This action cannot be undone. This will permanently delete all current records - for {name} from the - database. + {t('systems_table.delete_confirm_des_1')} {name} {t('systems_table.delete_confirm_des_2')} - Cancel + {t('cancel')} pb.collection('systems').delete(id)} > - Continue + {t('continue')} @@ -354,7 +355,7 @@ export default function SystemsTable({ filter }: { filter?: string }) { ) : ( - No systems found + {t('systems_table.no_systems_found')} )} diff --git a/beszel/site/src/lib/i18n.ts b/beszel/site/src/lib/i18n.ts new file mode 100644 index 0000000..3990a1f --- /dev/null +++ b/beszel/site/src/lib/i18n.ts @@ -0,0 +1,22 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; + +import en from '../locales/en/translation.json'; +import es from '../locales/es/translation.json'; + +i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + resources: { + en: { translation: en }, + es: { translation: es } + }, + fallbackLng: 'en', + interpolation: { + escapeValue: false + } + }); + +export { i18n }; \ No newline at end of file diff --git a/beszel/site/src/lib/languages.json b/beszel/site/src/lib/languages.json new file mode 100644 index 0000000..4cb11f0 --- /dev/null +++ b/beszel/site/src/lib/languages.json @@ -0,0 +1,30 @@ +[ + { + "lang": "en", + "label": "English" + }, + { + "lang": "es", + "label": "Español" + }, + { + "lang": "fr", + "label": "Français" + }, + { + "lang": "de", + "label": "Deutsch" + }, + { + "lang": "ru", + "label": "Русский" + }, + { + "lang": "zh-Hans", + "label": "简体中文" + }, + { + "lang": "zh-Hant", + "label": "繁體中文" + } +] \ No newline at end of file diff --git a/beszel/site/src/locales/en/translation.json b/beszel/site/src/locales/en/translation.json new file mode 100644 index 0000000..b689e9d --- /dev/null +++ b/beszel/site/src/locales/en/translation.json @@ -0,0 +1,94 @@ +{ + "all_systems": "All Systems", + "filter": "Filter...", + "copy": "Copy", + "add": "Add", + "system": "System", + "systems": "Systems", + "cancel": "Cancel", + "continue": "Continue", + "home": { + "active_alerts": "Active Alerts", + "active_des": "Exceeds {{value}}{{unit}} average in last {{minutes}} minutes", + "subtitle_1": "Updated in real time. Press", + "subtitle_2": "to open the command palette." + }, + "systems_table": { + "system": "System", + "memory": "Memory", + "cpu": "CPU", + "disk": "Disk", + "net": "Net", + "agent": "Agent", + "no_systems_found": "No systems found.", + "open_menu": "Open menu", + "resume": "Resume", + "pause": "Pause", + "copy_host": "Copy host", + "delete": "Delete", + "delete_confirm": "Are you sure you want to delete {{name}}?", + "delete_confirm_des_1": "This action cannot be undone. This will permanently delete all current records for", + "delete_confirm_des_2": "from the database." + }, + "alerts": { + "title": "Alerts", + "subtitle_1": "See", + "notification_settings": "notification settings", + "subtitle_2": "to configure how you receive alerts.", + "overwrite_existing_alerts": "Overwrite existing alerts" + }, + "settings": { + "settings": "Settings", + "subtitle": "Manage display and notification preferences.", + "save_settings": "Save Settings", + "general": { + "title": "General", + "subtitle": "Change general application options.", + "language": { + "title": "Language", + "subtitle_1": "Want to help us make our translations even better? Check out", + "subtitle_2": "for more details.", + "preferred_language": "Preferred Language" + }, + "chart_options": { + "title": "Chart options", + "subtitle": "Adjust display options for charts.", + "default_time_period": "Default time period", + "default_time_period_des": "Sets the default time range for charts when a system is viewed." + } + }, + "notifications": { + "title": "Notifications", + "subtitle_1": "Configure how you receive alert notifications.", + "subtitle_2": "Looking instead for where to create alerts? Click the bell", + "subtitle_3": "icons in the systems table." + }, + "language": "Language" + }, + "user_dm": { + "users": "Users", + "logs": "Logs", + "backups": "Backups", + "auth_providers": "Auth Providers", + "log_out": "Log Out" + }, + "themes": { + "toggle_theme": "Toggle theme", + "light": "Light", + "dark": "Dark", + "system": "System" + }, + "add_system": { + "add_new_system": "Add New System", + "binary": "Binary", + "dialog_des_1": "The agent must be running on the system to connect. Copy the", + "dialog_des_2": "for the agent below.", + "name": "Name", + "host_ip": "Host / IP", + "port": "Port", + "public_key": "Public Key", + "click_to_copy": "Click to copy", + "command": "command", + "add_system": "Add system" + } +} \ No newline at end of file diff --git a/beszel/site/src/locales/es/translation.json b/beszel/site/src/locales/es/translation.json new file mode 100644 index 0000000..63407ef --- /dev/null +++ b/beszel/site/src/locales/es/translation.json @@ -0,0 +1,94 @@ +{ + "all_systems": "Todos los sistemas", + "filter": "Filtrar...", + "copy": "Copiar", + "add": "Agregar", + "system": "Sistema", + "systems": "Sistemas", + "cancel": "Cancelar", + "continue": "Continuar", + "home": { + "active_alerts": "Alertas activas", + "active_des": "Excede el promedio de {{value}}{{unit}} en los últimos {{minutes}} minutos", + "subtitle_1": "Actualizado en tiempo real. Presione", + "subtitle_2": "para abrir la paleta de comandos." + }, + "systems_table": { + "system": "Sistema", + "memory": "Memoria", + "cpu": "CPU", + "disk": "Disco", + "net": "Red", + "agent": "Agente", + "no_systems_found": "No se encontraron sistemas.", + "open_menu": "Abrir menú", + "resume": "Reanudar", + "pause": "Pausar", + "copy_host": "Copiar host", + "delete": "Eliminar", + "delete_confirm": "¿Estás seguro de que quieres eliminar {{name}}?", + "delete_confirm_des_1": "Esta acción no se puede deshacer. Esto eliminará permanentemente todos los registros actuales para", + "delete_confirm_des_2": "de la base de datos." + }, + "alerts": { + "title": "Alertas", + "subtitle_1": "Ver", + "notification_settings": "configuración de notificaciones", + "subtitle_2": "para configurar cómo recibe las alertas.", + "overwrite_existing_alerts": "Sobrescribir alertas existentes" + }, + "settings": { + "settings": "Configuración", + "subtitle": "Administrar las preferencias de visualización y notificación.", + "save_settings": "Guardar configuración", + "general": { + "title": "General", + "subtitle": "Cambiar las opciones generales de la aplicación.", + "language": { + "title": "Idioma", + "subtitle_1": "¿Quieres ayudarnos a mejorar nuestras traducciones? Consulta", + "subtitle_2": "para más detalles.", + "preferred_language": "Idioma preferido" + }, + "chart_options": { + "title": "Opciones de gráfico", + "subtitle": "Ajustar las opciones de visualización para los gráficos.", + "default_time_period": "Periodo de tiempo predeterminado", + "default_time_period_des": "Establece el rango de tiempo predeterminado para los gráficos cuando se visualiza un sistema." + } + }, + "notifications": { + "title": "Notificaciones", + "subtitle_1": "Configure cómo recibe las notificaciones de alerta.", + "subtitle_2": "¿Busca dónde crear alertas? Haga clic en el icono de campana", + "subtitle_3": "en la tabla de sistemas." + }, + "language": "Idioma" + }, + "user_dm": { + "users": "Usuarios", + "logs": "Registros", + "backups": "Respaldos", + "auth_providers": "Proveedores de autenticación", + "log_out": "Cerrar sesión" + }, + "themes": { + "toggle_theme": "Alternar tema", + "light": "Claro", + "dark": "Oscuro", + "system": "Sistema" + }, + "add_system": { + "add_new_system": "Agregar nuevo sistema", + "binary": "Binario", + "dialog_des_1": "El agente debe estar ejecutándose en el sistema para conectarse. Copie el", + "dialog_des_2": "para el agente a continuación.", + "name": "Nombre", + "host_ip": "Host / IP", + "port": "Puerto", + "public_key": "Clave pública", + "click_to_copy": "Haga clic para copiar", + "command": "comando", + "add_system": "Agregar sistema" + } +} \ No newline at end of file diff --git a/beszel/site/src/main.tsx b/beszel/site/src/main.tsx index 89b6204..91d4efd 100644 --- a/beszel/site/src/main.tsx +++ b/beszel/site/src/main.tsx @@ -11,6 +11,7 @@ import { $hubVersion, $copyContent, } from './lib/stores.ts' +import { LangToggle } from './components/lang-toggle.tsx' import { ModeToggle } from './components/mode-toggle.tsx' import { cn, @@ -48,6 +49,9 @@ import { $router, Link } from './components/router.tsx' import SystemDetail from './components/routes/system.tsx' import { AddSystemButton } from './components/add-system.tsx' +import './lib/i18n.ts' +import { useTranslation } from 'react-i18next' + // const ServerDetail = lazy(() => import('./components/routes/system.tsx')) const CommandPalette = lazy(() => import('./components/command-palette.tsx')) const LoginPage = lazy(() => import('./components/login/login.tsx')) @@ -111,6 +115,8 @@ const App = () => { } const Layout = () => { + const { t } = useTranslation() + const authenticated = useStore($authenticated) const copyContent = useStore($copyContent) @@ -131,6 +137,7 @@ const Layout = () => {
+ { - Users + {t('user_dm.users')} - Systems + {t('systems')} - Logs + {t('user_dm.logs')} - Backups + {t('user_dm.backups')} - Auth providers + {t('user_dm.auth_providers')} @@ -190,7 +197,7 @@ const Layout = () => { pb.authStore.clear()}> - Log out + {t('user_dm.log_out')} From 5c2e2d7d36f128b68700752754dc8eb1ef9e9ee2 Mon Sep 17 00:00:00 2001 From: Arsfy Date: Mon, 28 Oct 2024 18:44:04 +0800 Subject: [PATCH 3/5] i18n --- beszel/site/bun.lockb | Bin 158101 -> 158101 bytes beszel/site/src/components/add-system.tsx | 193 +++++++----------- .../src/components/alerts/alerts-system.tsx | 12 +- .../site/src/components/command-palette.tsx | 43 ++-- .../routes/settings/config-yaml.tsx | 22 +- .../src/components/routes/settings/layout.tsx | 2 +- .../routes/settings/notifications.tsx | 26 ++- beszel/site/src/components/routes/system.tsx | 53 ++--- beszel/site/src/lib/i18n.ts | 14 +- beszel/site/src/lib/languages.json | 4 +- beszel/site/src/lib/utils.ts | 24 +-- beszel/site/src/locales/de/translation.json | 178 ++++++++++++++++ beszel/site/src/locales/en/translation.json | 88 +++++++- beszel/site/src/locales/es/translation.json | 118 +++++++++-- beszel/site/src/locales/fr/translation.json | 178 ++++++++++++++++ beszel/site/src/locales/ru/translation.json | 178 ++++++++++++++++ .../site/src/locales/zh-CN/translation.json | 178 ++++++++++++++++ .../site/src/locales/zh-HK/translation.json | 178 ++++++++++++++++ 18 files changed, 1261 insertions(+), 228 deletions(-) create mode 100644 beszel/site/src/locales/de/translation.json create mode 100644 beszel/site/src/locales/fr/translation.json create mode 100644 beszel/site/src/locales/ru/translation.json create mode 100644 beszel/site/src/locales/zh-CN/translation.json create mode 100644 beszel/site/src/locales/zh-HK/translation.json diff --git a/beszel/site/bun.lockb b/beszel/site/bun.lockb index d316e9006091c17741f49c2edfa7ad8b0dd36331..ccc65f336138d9e98defc92338fcb1916053c0a8 100755 GIT binary patch delta 65 zcmV-H0KWf~(+QQ+34pW#9g!gcUNSQ-F*YtVJmNlAmnw)e0s&q)E-)@I Xmp+jJl?VY|F*PnRE-<&-kpa5bg^v^F delta 65 zcmbPwnRDu8&W0_FvJ(}V7@*(@BLjm114Dz&BfIeFS{=qrP?_m=6B(y+z?5yjJ&|$O FbpQf$4_p8M diff --git a/beszel/site/src/components/add-system.tsx b/beszel/site/src/components/add-system.tsx index 0662670..9faab67 100644 --- a/beszel/site/src/components/add-system.tsx +++ b/beszel/site/src/components/add-system.tsx @@ -77,7 +77,7 @@ export function AddSystemButton({ className }: { className?: string }) { className={cn('flex gap-1 max-xs:h-[2.4rem]', className, isReadOnlyUser() && 'hidden')} > - {t('add')} {t('system')} + {t('add')}{t('system')} @@ -89,67 +89,78 @@ export function AddSystemButton({ className }: { className?: string }) { {t('add_system.binary')} + {/* Docker */} {t('add_system.dialog_des_1')}{' '} docker-compose.yml {t('add_system.dialog_des_2')} -
-
-
- - -
-
- - -
-
- - -
-
- - -
- - - - - - -

{t('add_system.click_to_copy')}

-
-
-
-
+ + {/* Binary */} + + + {t('add_system.dialog_des_1')}{' '} + install command {t('add_system.dialog_des_2')} + + + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+ + + + + + +

{t('add_system.click_to_copy')}

+
+
+
+
+
+ {/* Docker */} + - - - - - {t('add_system.dialog_des_1')}{' '} - install command {t('add_system.dialog_des_2')} - -
-
-
- - -
-
- - -
-
- - -
-
- - -
- - - - - - -

{t('add_system.click_to_copy')}

-
-
-
-
-
+ + {/* Binary */} + - -
+
+ diff --git a/beszel/site/src/components/alerts/alerts-system.tsx b/beszel/site/src/components/alerts/alerts-system.tsx index fa6ea91..cc8c30e 100644 --- a/beszel/site/src/components/alerts/alerts-system.tsx +++ b/beszel/site/src/components/alerts/alerts-system.tsx @@ -6,6 +6,7 @@ import { lazy, Suspense, useRef, useState } from 'react' import { toast } from '../ui/use-toast' import { RecordOptions } from 'pocketbase' import { newQueue, Queue } from '@henrygd/queue' +import { useTranslation } from 'react-i18next' interface AlertData { checked?: boolean @@ -157,6 +158,8 @@ export function SystemAlertGlobal({ } function AlertContent({ data }: { data: AlertData }) { + const { t } = useTranslation() + const { key } = data const hasSliders = !('single' in data.alert) @@ -185,10 +188,10 @@ function AlertContent({ data }: { data: AlertData }) { >

- {data.alert.name} + {t(data.alert.name)}

{!showSliders && ( - {data.alert.desc} + {t(data.alert.desc)} )}
}>

- Average exceeds{' '} + {t('alerts.average_exceeds')}{' '} {value} {data.alert.unit} @@ -224,8 +227,7 @@ function AlertContent({ data }: { data: AlertData }) {

- For {min} minute - {min > 1 && 's'} + {t('alerts.for')} {min} {min > 1 ? t('alerts.minutes') : t('alerts.minute')}

- + No results found. {systems.length > 0 && ( @@ -67,7 +70,7 @@ export default function CommandPalette() { )} - + { @@ -76,8 +79,8 @@ export default function CommandPalette() { }} > - Dashboard - Page + {t('command.dashboard')} + {t('command.page')} { @@ -86,8 +89,8 @@ export default function CommandPalette() { }} > - Settings - Settings + {t('settings.settings')} + {t('settings.settings')} - Notification settings - Settings + {t('settings.notifications.title')} + {t('settings.settings')} - Documentation + {t('command.documentation')} GitHub {isAdmin() && ( <> - + { @@ -123,8 +126,8 @@ export default function CommandPalette() { }} > - Users - Admin + {t('user_dm.users')} + {t("command.admin")} { @@ -133,8 +136,8 @@ export default function CommandPalette() { }} > - Logs - Admin + {t('user_dm.logs')} + {t("command.admin")} { @@ -143,8 +146,8 @@ export default function CommandPalette() { }} > - Backups - Admin + {t('user_dm.backups')} + {t("command.admin")} - Auth Providers - Admin + {t('user_dm.auth_providers')} + {t("command.admin")} - SMTP settings - Admin + {t('command.SMTP_settings')} + {t("command.admin")} diff --git a/beszel/site/src/components/routes/settings/config-yaml.tsx b/beszel/site/src/components/routes/settings/config-yaml.tsx index 18fbacf..078ae6a 100644 --- a/beszel/site/src/components/routes/settings/config-yaml.tsx +++ b/beszel/site/src/components/routes/settings/config-yaml.tsx @@ -10,8 +10,11 @@ import { useState } from 'react' import { Textarea } from '@/components/ui/textarea' import { toast } from '@/components/ui/use-toast' import clsx from 'clsx' +import { useTranslation } from 'react-i18next' export default function ConfigYaml() { + const { t } = useTranslation() + const [configContent, setConfigContent] = useState('') const [isLoading, setIsLoading] = useState(false) @@ -40,30 +43,27 @@ export default function ConfigYaml() { return (
-

YAML Configuration

+

{t('settings.yaml_config.title')}

- Export your current systems configuration. + {t('settings.yaml_config.subtitle')}

- Systems may be managed in a{' '} - config.yml file inside - your data directory. + {t('settings.yaml_config.des_1')}{' '} + config.yml {t('settings.yaml_config.des_2')}

- On each restart, systems in the database will be updated to match the systems defined in - the file. + {t('settings.yaml_config.des_3')}

- Caution - potential data loss + {t('settings.yaml_config.alert.title')}

- Existing systems not defined in config.yml will be deleted. Please make - regular backups. + {t('settings.yaml_config.alert.des_1')} config.yml {t('settings.yaml_config.alert.des_2')}

@@ -86,7 +86,7 @@ export default function ConfigYaml() { disabled={isLoading} > - Export configuration + {t('settings.export_configuration')}
) diff --git a/beszel/site/src/components/routes/settings/layout.tsx b/beszel/site/src/components/routes/settings/layout.tsx index 29b3736..b5cf630 100644 --- a/beszel/site/src/components/routes/settings/layout.tsx +++ b/beszel/site/src/components/routes/settings/layout.tsx @@ -61,7 +61,7 @@ export default function SettingsLayout() { if (isAdmin()) { sidebarNavItems.push({ - title: 'YAML Config', + title: t('settings.yaml_config.short_title'), href: '/settings/config', icon: FileSlidersIcon, }) diff --git a/beszel/site/src/components/routes/settings/notifications.tsx b/beszel/site/src/components/routes/settings/notifications.tsx index 372bbd6..feb0100 100644 --- a/beszel/site/src/components/routes/settings/notifications.tsx +++ b/beszel/site/src/components/routes/settings/notifications.tsx @@ -85,38 +85,42 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
-

Email notifications

+

+ {t('settings.notifications.email.title')} +

{isAdmin() && (

- Please{' '} + {t('settings.notifications.email.please')}{' '} - configure an SMTP server + {t('settings.notifications.email.configure_an_SMTP_server')} {' '} - to ensure alerts are delivered.{' '} + {t('settings.notifications.email.to_ensure_alerts_are_delivered')}{' '}

)}

- Save address using enter key or comma. Leave blank to disable email notifications. + {t('settings.notifications.email.des')}

-

Webhook / Push notifications

+

+ {t('settings.notifications.webhook_push.title')} +

- Beszel uses{' '} + {t('settings.notifications.webhook_push.des_1')}{' '} Shoutrrr {' '} - to integrate with popular notification services. + {t('settings.notifications.webhook_push.des_2')}

{webhooks.length > 0 && ( @@ -149,7 +153,7 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting onClick={addWebhook} > - Add URL + {t('settings.notifications.webhook_push.add_url')}
diff --git a/beszel/site/src/components/routes/system.tsx b/beszel/site/src/components/routes/system.tsx index ff41b31..56271d1 100644 --- a/beszel/site/src/components/routes/system.tsx +++ b/beszel/site/src/components/routes/system.tsx @@ -97,6 +97,8 @@ async function getStats( } export default function SystemDetail({ name }: { name: string }) { + const { t } = useTranslation() + const systems = useStore($systems) const chartTime = useStore($chartTime) /** Max CPU toggle value */ @@ -350,7 +352,7 @@ export default function SystemDetail({ name }: { name: string }) {
@@ -374,9 +376,8 @@ export default function SystemDetail({ name }: { name: string }) {
: null} > @@ -400,8 +401,8 @@ export default function SystemDetail({ name }: { name: string }) { @@ -409,15 +410,15 @@ export default function SystemDetail({ name }: { name: string }) { {containerFilterBar && ( )} - + : null} > : null} - description="Network traffic of public interfaces" + description={t('monitor.bandwidth_des')} > {/* @ts-ignore */} @@ -470,13 +471,13 @@ export default function SystemDetail({ name }: { name: string }) { )} {(systemStats.at(-1)?.stats.su ?? 0) > 0 && ( - + )} {systemStats.at(-1)?.stats.t && ( - + )} @@ -490,8 +491,8 @@ export default function SystemDetail({ name }: { name: string }) {
: null} > >] }) { + const { t } = useTranslation() + const [max, setMax] = store const Icon = max ? ChartMax : ChartAverage @@ -573,10 +576,10 @@ function SelectAvgMax({ - Average + {t('monitor.average')} - Max 1 min + {t('monitor.max_1_min')} diff --git a/beszel/site/src/lib/i18n.ts b/beszel/site/src/lib/i18n.ts index 3990a1f..ef4f6d4 100644 --- a/beszel/site/src/lib/i18n.ts +++ b/beszel/site/src/lib/i18n.ts @@ -4,6 +4,11 @@ import LanguageDetector from 'i18next-browser-languagedetector'; import en from '../locales/en/translation.json'; import es from '../locales/es/translation.json'; +import fr from '../locales/fr/translation.json'; +import de from '../locales/de/translation.json'; +import ru from '../locales/ru/translation.json'; +import zhHans from '../locales/zh-CN/translation.json'; +import zhHant from '../locales/zh-HK/translation.json'; i18n .use(LanguageDetector) @@ -11,7 +16,14 @@ i18n .init({ resources: { en: { translation: en }, - es: { translation: es } + es: { translation: es }, + fr: { translation: fr }, + de: { translation: de }, + ru: { translation: ru }, + // Chinese (Simplified) + 'zh-CN': { translation: zhHans }, + // Chinese (Traditional) + 'zh-HK': { translation: zhHant }, }, fallbackLng: 'en', interpolation: { diff --git a/beszel/site/src/lib/languages.json b/beszel/site/src/lib/languages.json index 4cb11f0..d4af14c 100644 --- a/beszel/site/src/lib/languages.json +++ b/beszel/site/src/lib/languages.json @@ -20,11 +20,11 @@ "label": "Русский" }, { - "lang": "zh-Hans", + "lang": "zh-CN", "label": "简体中文" }, { - "lang": "zh-Hant", + "lang": "zh-HK", "label": "繁體中文" } ] \ No newline at end of file diff --git a/beszel/site/src/lib/utils.ts b/beszel/site/src/lib/utils.ts index 8e133fe..6a25f4b 100644 --- a/beszel/site/src/lib/utils.ts +++ b/beszel/site/src/lib/utils.ts @@ -301,40 +301,40 @@ export const chartMargin = { top: 12 } export const alertInfo = { Status: { - name: 'Status', + name: 'alerts.info.status', unit: '', icon: ServerIcon, - desc: 'Triggers when status switches between up and down.', + desc: 'alerts.info.status_des', single: true, }, CPU: { - name: 'CPU usage', + name: 'alerts.info.cpu_usage', unit: '%', icon: CpuIcon, - desc: 'Triggers when CPU usage exceeds a threshold.', + desc: 'alerts.info.cpu_usage_des', }, Memory: { - name: 'memory usage', + name: 'alerts.info.memory_usage', unit: '%', icon: MemoryStickIcon, - desc: 'Triggers when memory usage exceeds a threshold.', + desc: 'alerts.info.memory_usage_des', }, Disk: { - name: 'disk usage', + name: 'alerts.info.disk_usage', unit: '%', icon: HardDriveIcon, - desc: 'Triggers when usage of any disk exceeds a threshold.', + desc: 'alerts.info.disk_usage_des', }, Bandwidth: { - name: 'bandwidth', + name: 'alerts.info.bandwidth', unit: ' MB/s', icon: EthernetIcon, - desc: 'Triggers when combined up/down exceeds a threshold.', + desc: 'alerts.info.bandwidth_des', }, Temperature: { - name: 'temperature', + name: 'alerts.info.temperature', unit: '°C', icon: ThermometerIcon, - desc: 'Triggers when any sensor exceeds a threshold.', + desc: 'alerts.info.temperature_des', }, } diff --git a/beszel/site/src/locales/de/translation.json b/beszel/site/src/locales/de/translation.json new file mode 100644 index 0000000..68a2876 --- /dev/null +++ b/beszel/site/src/locales/de/translation.json @@ -0,0 +1,178 @@ +{ + "all_systems": "Alle Systeme", + "filter": "Filtern...", + "copy": "Kopieren", + "add": "Hinzufügen", + "system": "System", + "systems": "Systeme", + "cancel": "Abbrechen", + "continue": "Fortsetzen", + "home": { + "active_alerts": "Aktive Warnungen", + "active_des": "Überschreitet {{value}}{{unit}} Durchschnitt in den letzten {{minutes}} Minuten", + "subtitle_1": "In Echtzeit aktualisiert. Drücken Sie", + "subtitle_2": "um die Befehlsübersicht zu öffnen." + }, + "systems_table": { + "system": "System", + "memory": "Speicher", + "cpu": "CPU", + "disk": "Festplatte", + "net": "Netzwerk", + "agent": "Agent", + "no_systems_found": "Keine Systeme gefunden.", + "open_menu": "Menü öffnen", + "resume": "Fortsetzen", + "pause": "Pause", + "copy_host": "Host kopieren", + "delete": "Löschen", + "delete_confirm": "Sind Sie sicher, dass Sie {{name}} löschen möchten?", + "delete_confirm_des_1": "Diese Aktion kann nicht rückgängig gemacht werden. Dies wird alle aktuellen Aufzeichnungen für", + "delete_confirm_des_2": "dauerhaft aus der Datenbank löschen." + }, + "alerts": { + "title": "Warnungen", + "subtitle_1": "Siehe", + "notification_settings": "Benachrichtigungseinstellungen", + "subtitle_2": "um zu konfigurieren, wie Sie Warnungen erhalten.", + "overwrite_existing_alerts": "Bestehende Warnungen überschreiben", + "info": { + "status": "Status", + "status_des": "Löst aus, wenn der Status zwischen oben und unten wechselt.", + "cpu_usage": "CPU-Auslastung", + "cpu_usage_des": "Löst aus, wenn die CPU-Auslastung einen Schwellenwert überschreitet.", + "memory_usage": "Speicherauslastung", + "memory_usage_des": "Löst aus, wenn die Speicherauslastung einen Schwellenwert überschreitet.", + "disk_usage": "Festplattennutzung", + "disk_usage_des": "Löst aus, wenn die Nutzung einer Festplatte einen Schwellenwert überschreitet.", + "bandwidth": "Bandbreite", + "bandwidth_des": "Löst aus, wenn die kombinierte Auf-/Abwärtsbandbreite einen Schwellenwert überschreitet.", + "temperature": "Temperatur", + "temperature_des": "Löst aus, wenn ein Sensor einen Schwellenwert überschreitet." + }, + "average_exceeds": "Durchschnitt überschreitet", + "for": "Für", + "minute": "Minute", + "minutes": "Minuten" + }, + "settings": { + "settings": "Einstellungen", + "subtitle": "Anzeige- und Benachrichtigungseinstellungen verwalten.", + "save_settings": "Einstellungen speichern", + "export_configuration": "Konfiguration exportieren", + "general": { + "title": "Allgemein", + "subtitle": "Allgemeine Anwendungsoptionen ändern.", + "language": { + "title": "Sprache", + "subtitle_1": "Möchten Sie uns helfen, unsere Übersetzungen noch besser zu machen? Schauen Sie sich", + "subtitle_2": "für weitere Details an.", + "preferred_language": "Bevorzugte Sprache" + }, + "chart_options": { + "title": "Diagrammoptionen", + "subtitle": "Anzeigeoptionen für Diagramme anpassen.", + "default_time_period": "Standardzeitraum", + "default_time_period_des": "Legt den Standardzeitraum für Diagramme fest, wenn ein System angezeigt wird." + } + }, + "notifications": { + "title": "Benachrichtigungen", + "subtitle_1": "Konfigurieren Sie, wie Sie Warnbenachrichtigungen erhalten.", + "subtitle_2": "Suchen Sie stattdessen nach dem Ort, an dem Sie Warnungen erstellen können? Klicken Sie auf die Glocke", + "subtitle_3": "Symbole in der Systemtabelle.", + "email": { + "title": "E-Mail-Benachrichtigungen", + "please": "Bitte", + "configure_an_SMTP_server": "konfigurieren Sie einen SMTP-Server", + "to_ensure_alerts_are_delivered": "um sicherzustellen, dass Warnungen zugestellt werden.", + "to_email_s": "An E-Mail(s)", + "enter_email_address": "E-Mail-Adresse eingeben...", + "des": "Adresse mit der Eingabetaste oder dem Komma speichern. Leer lassen, um E-Mail-Benachrichtigungen zu deaktivieren." + }, + "webhook_push": { + "title": "Webhook / Push-Benachrichtigungen", + "des_1": "Beszel verwendet", + "des_2": "um sich mit beliebten Benachrichtigungsdiensten zu integrieren.", + "add_url": "URL hinzufügen" + } + }, + "yaml_config": { + "short_title": "YAML-Konfig", + "title": "YAML-Konfiguration", + "subtitle": "Aktuelle Systemkonfiguration exportieren.", + "des_1": "Systeme können in einer", + "des_2": "Datei im Datenverzeichnis verwaltet werden.", + "des_3": "Bei jedem Neustart werden die Systeme in der Datenbank aktualisiert, um den in der Datei definierten Systemen zu entsprechen.", + "alert": { + "title": "Achtung - potenzieller Datenverlust", + "des_1": "Bestehende Systeme, die nicht in", + "des_2": "definiert sind, werden gelöscht. Bitte machen Sie regelmäßige Backups." + } + }, + "language": "Sprache" + }, + "user_dm": { + "users": "Benutzer", + "logs": "Protokolle", + "backups": "Backups", + "auth_providers": "Authentifizierungsanbieter", + "log_out": "Abmelden" + }, + "themes": { + "toggle_theme": "Thema wechseln", + "light": "Hell", + "dark": "Dunkel", + "system": "System" + }, + "add_system": { + "add_new_system": "Neues System hinzufügen", + "binary": "Binär", + "dialog_des_1": "Der Agent muss auf dem System laufen, um eine Verbindung herzustellen. Kopieren Sie den", + "dialog_des_2": "für den Agenten unten.", + "name": "Name", + "host_ip": "Host / IP", + "port": "Port", + "public_key": "Öffentlicher Schlüssel", + "click_to_copy": "Zum Kopieren klicken", + "command": "Befehl", + "add_system": "System hinzufügen" + }, + "command": { + "search": "Nach Systemen oder Einstellungen suchen...", + "pages_settings": "Seiten / Einstellungen", + "dashboard": "Dashboard", + "documentation": "Dokumentation", + "SMTP_settings": "SMTP-Einstellungen", + "page": "Seite", + "admin": "Admin" + }, + "monitor": { + "toggle_grid": "Raster umschalten", + "average": "Durchschnitt", + "max_1_min": "Max 1 Min", + "total_cpu_usage": "Gesamte CPU-Auslastung", + "cpu_des": "Systemweite CPU-Auslastung", + "docker_cpu_usage": "Docker-CPU-Auslastung", + "docker_cpu_des": "Durchschnittliche CPU-Auslastung der Container", + "total_memory_usage": "Gesamte Speicherauslastung", + "memory_des": "Genaue Nutzung zum aufgezeichneten Zeitpunkt", + "docker_memory_usage": "Docker-Speicherauslastung", + "docker_memory_des": "Speichernutzung der Docker-Container", + "disk_space": "Festplattenspeicher", + "disk_des": "Nutzung der Root-Partition", + "disk_io": "Festplatten-I/O", + "disk_io_des": "Durchsatz des Root-Dateisystems", + "bandwidth": "Bandbreite", + "bandwidth_des": "Netzwerkverkehr der öffentlichen Schnittstellen", + "docker_network_io": "Docker-Netzwerk-I/O", + "docker_network_io_des": "Netzwerkverkehr der Docker-Container", + "swap_usage": "Swap-Nutzung", + "swap_des": "Vom System genutzter Swap-Speicher", + "temperature": "Temperatur", + "temperature_des": "Temperaturen der System-Sensoren", + "usage": "Nutzung", + "disk_usage_of": "Festplattennutzung von", + "throughput_of": "Durchsatz von" + } +} \ No newline at end of file diff --git a/beszel/site/src/locales/en/translation.json b/beszel/site/src/locales/en/translation.json index b689e9d..3c5e822 100644 --- a/beszel/site/src/locales/en/translation.json +++ b/beszel/site/src/locales/en/translation.json @@ -35,12 +35,31 @@ "subtitle_1": "See", "notification_settings": "notification settings", "subtitle_2": "to configure how you receive alerts.", - "overwrite_existing_alerts": "Overwrite existing alerts" + "overwrite_existing_alerts": "Overwrite existing alerts", + "info": { + "status": "Status", + "status_des": "Triggers when status switches between up and down.", + "cpu_usage": "CPU Usage", + "cpu_usage_des": "Triggers when CPU usage exceeds a threshold.", + "memory_usage": "Memory Usage", + "memory_usage_des": "Triggers when memory usage exceeds a threshold.", + "disk_usage": "Disk Usage", + "disk_usage_des": "Triggers when usage of any disk exceeds a threshold.", + "bandwidth": "Bandwidth", + "bandwidth_des": "Triggers when combined up/down exceeds a threshold.", + "temperature": "Temperature", + "temperature_des": "Triggers when any sensor exceeds a threshold." + }, + "average_exceeds": "Average exceeds", + "for": "For", + "minute": "minute", + "minutes": "minutes" }, "settings": { "settings": "Settings", "subtitle": "Manage display and notification preferences.", "save_settings": "Save Settings", + "export_configuration": "Export configuration", "general": { "title": "General", "subtitle": "Change general application options.", @@ -61,7 +80,35 @@ "title": "Notifications", "subtitle_1": "Configure how you receive alert notifications.", "subtitle_2": "Looking instead for where to create alerts? Click the bell", - "subtitle_3": "icons in the systems table." + "subtitle_3": "icons in the systems table.", + "email": { + "title": "Email notifications", + "please": "Please", + "configure_an_SMTP_server": "configure an SMTP server", + "to_ensure_alerts_are_delivered": "to ensure alerts are delivered.", + "to_email_s": "To email(s)", + "enter_email_address": "Enter email address...", + "des": "Save address using enter key or comma. Leave blank to disable email notifications." + }, + "webhook_push": { + "title": "Webhook / Push notifications", + "des_1": "Beszel uses", + "des_2": "to integrate with popular notification services.", + "add_url": "Add URL" + } + }, + "yaml_config": { + "short_title": "YAML Config", + "title": "YAML Configuration", + "subtitle": "Export your current systems configuration.", + "des_1": "Systems may be managed in a", + "des_2": "file inside your data directory.", + "des_3": "On each restart, systems in the database will be updated to match the systems defined in the file.", + "alert": { + "title": "Caution - potential data loss", + "des_1": "Existing systems not defined in", + "des_2": "will be deleted. Please make regular backups." + } }, "language": "Language" }, @@ -90,5 +137,42 @@ "click_to_copy": "Click to copy", "command": "command", "add_system": "Add system" + }, + "command": { + "search": "Search for systems or settings...", + "pages_settings": "Pages / Settings", + "dashboard": "Dashboard", + "documentation": "Documentation", + "SMTP_settings": "SMTP settings", + "page": "Page", + "admin": "Admin" + }, + "monitor": { + "toggle_grid": "Toggle grid", + "average": "Average", + "max_1_min": "Max 1 min ", + "total_cpu_usage": "Total CPU Usage", + "cpu_des": "system-wide CPU utilization", + "docker_cpu_usage": "Docker CPU Usage", + "docker_cpu_des": "Average CPU utilization of containers", + "total_memory_usage": "Total Memory Usage", + "memory_des": "Precise utilization at the recorded time", + "docker_memory_usage": "Docker Memory Usage", + "docker_memory_des": "Memory usage of docker containers", + "disk_space": "Disk Space", + "disk_des": "Usage of root partition", + "disk_io": "Disk I/O", + "disk_io_des": "Throughput of root filesystem", + "bandwidth": "Bandwidth", + "bandwidth_des": "Network traffic of public interfaces", + "docker_network_io": "Docker Network I/O", + "docker_network_io_des": "Network traffic of docker containers", + "swap_usage": "Swap Usage", + "swap_des": "Swap space used by the system", + "temperature": "Temperature", + "temperature_des": "Temperatures of system sensors", + "usage": "Usage", + "disk_usage_of": "Disk usage of", + "throughput_of": "Throughput of" } } \ No newline at end of file diff --git a/beszel/site/src/locales/es/translation.json b/beszel/site/src/locales/es/translation.json index 63407ef..5ba9b64 100644 --- a/beszel/site/src/locales/es/translation.json +++ b/beszel/site/src/locales/es/translation.json @@ -33,35 +33,82 @@ "alerts": { "title": "Alertas", "subtitle_1": "Ver", - "notification_settings": "configuración de notificaciones", - "subtitle_2": "para configurar cómo recibe las alertas.", - "overwrite_existing_alerts": "Sobrescribir alertas existentes" + "notification_settings": "configuraciones de notificación", + "subtitle_2": "para configurar cómo recibes las alertas.", + "overwrite_existing_alerts": "Sobrescribir alertas existentes", + "info": { + "status": "Estado", + "status_des": "Se activa cuando el estado cambia entre arriba y abajo.", + "cpu_usage": "Uso de CPU", + "cpu_usage_des": "Se activa cuando el uso de la CPU supera un umbral.", + "memory_usage": "Uso de memoria", + "memory_usage_des": "Se activa cuando el uso de la memoria supera un umbral.", + "disk_usage": "Uso de disco", + "disk_usage_des": "Se activa cuando el uso de cualquier disco supera un umbral.", + "bandwidth": "Ancho de banda", + "bandwidth_des": "Se activa cuando el combinado arriba/abajo supera un umbral.", + "temperature": "Temperatura", + "temperature_des": "Se activa cuando cualquier sensor supera un umbral." + }, + "average_exceeds": "Promedio excede", + "for": "Por", + "minute": "minuto", + "minutes": "minutos" }, "settings": { - "settings": "Configuración", - "subtitle": "Administrar las preferencias de visualización y notificación.", - "save_settings": "Guardar configuración", + "settings": "Configuraciones", + "subtitle": "Administre las preferencias de visualización y notificación.", + "save_settings": "Guardar configuraciones", + "export_configuration": "Exportar configuración", "general": { "title": "General", - "subtitle": "Cambiar las opciones generales de la aplicación.", + "subtitle": "Cambie las opciones generales de la aplicación.", "language": { "title": "Idioma", - "subtitle_1": "¿Quieres ayudarnos a mejorar nuestras traducciones? Consulta", + "subtitle_1": "¿Quiere ayudarnos a mejorar nuestras traducciones? Consulte", "subtitle_2": "para más detalles.", "preferred_language": "Idioma preferido" }, "chart_options": { - "title": "Opciones de gráfico", - "subtitle": "Ajustar las opciones de visualización para los gráficos.", - "default_time_period": "Periodo de tiempo predeterminado", - "default_time_period_des": "Establece el rango de tiempo predeterminado para los gráficos cuando se visualiza un sistema." + "title": "Opciones de gráficos", + "subtitle": "Ajuste las opciones de visualización para los gráficos.", + "default_time_period": "Período de tiempo predeterminado", + "default_time_period_des": "Establezca el rango de tiempo predeterminado para los gráficos cuando se visualiza un sistema." } }, "notifications": { "title": "Notificaciones", "subtitle_1": "Configure cómo recibe las notificaciones de alerta.", - "subtitle_2": "¿Busca dónde crear alertas? Haga clic en el icono de campana", - "subtitle_3": "en la tabla de sistemas." + "subtitle_2": "¿Busca en su lugar dónde crear alertas? Haga clic en el icono de campana", + "subtitle_3": "en la tabla de sistemas.", + "email": { + "title": "Notificaciones por correo electrónico", + "please": "Por favor", + "configure_an_SMTP_server": "configure un servidor SMTP", + "to_ensure_alerts_are_delivered": "para asegurarse de que se entreguen las alertas.", + "to_email_s": "A correo electrónico(s)", + "enter_email_address": "Ingrese la dirección de correo electrónico...", + "des": "Guarde la dirección presionando Enter o usando una coma. Deje en blanco para desactivar las notificaciones por correo electrónico." + }, + "webhook_push": { + "title": "Notificaciones Webhook/Push", + "des_1": "Beszel utiliza", + "des_2": "para integrarse con populares servicios de notificación.", + "add_url": "Agregar URL" + } + }, + "yaml_config": { + "short_title": "Configuración YAML", + "title": "Configuración YAML", + "subtitle": "Exporta tu configuración actual de sistemas.", + "des_1": "Los sistemas pueden gestionarse en un", + "des_2": "archivo dentro de tu directorio de datos.", + "des_3": "En cada reinicio, los sistemas de la base de datos se actualizarán para coincidir con los sistemas definidos en el archivo.", + "alert": { + "title": "Advertencia - posible pérdida de datos", + "des_1": "Los sistemas existentes no definidos en", + "des_2": "serán eliminados. Por favor, haz copias de seguridad regulares." + } }, "language": "Idioma" }, @@ -81,14 +128,51 @@ "add_system": { "add_new_system": "Agregar nuevo sistema", "binary": "Binario", - "dialog_des_1": "El agente debe estar ejecutándose en el sistema para conectarse. Copie el", + "dialog_des_1": "El agente debe estar ejecutándose en el sistema para conectarse. Copia el", "dialog_des_2": "para el agente a continuación.", "name": "Nombre", - "host_ip": "Host / IP", + "host_ip": "Host/IP", "port": "Puerto", "public_key": "Clave pública", - "click_to_copy": "Haga clic para copiar", + "click_to_copy": "Haz clic para copiar", "command": "comando", "add_system": "Agregar sistema" + }, + "command": { + "search": "Buscar sistemas o configuraciones...", + "pages_settings": "Páginas/Configuraciones", + "dashboard": "Panel de control", + "documentation": "Documentación", + "SMTP_settings": "Configuraciones SMTP", + "page": "Página", + "admin": "Administrador" + }, + "monitor": { + "toggle_grid": "Alternar cuadrícula", + "average": "Promedio", + "max_1_min": "Máx. 1 min ", + "total_cpu_usage": "Uso total de CPU", + "cpu_des": "Utilización de CPU de todo el sistema", + "docker_cpu_usage": "Uso de CPU de Docker", + "docker_cpu_des": "Uso promedio de CPU de los contenedores", + "total_memory_usage": "Uso total de memoria", + "memory_des": "Utilización precisa en el momento registrado", + "docker_memory_usage": "Uso de memoria de Docker", + "docker_memory_des": "Uso de memoria de los contenedores de Docker", + "disk_space": "Espacio en disco", + "disk_des": "Uso de la partición raíz", + "disk_io": "E/S de disco", + "disk_io_des": "Rendimiento de la raíz del sistema de archivos", + "bandwidth": "Ancho de banda", + "bandwidth_des": "Tráfico de red de interfaces públicas", + "docker_network_io": "E/S de red de Docker", + "docker_network_io_des": "Tráfico de red de los contenedores de Docker", + "swap_usage": "Uso de intercambio", + "swap_des": "Espacio de intercambio utilizado por el sistema", + "temperature": "Temperatura", + "temperature_des": "Temperaturas de los sensores del sistema", + "usage": "Uso", + "disk_usage_of": "Uso de disco de", + "throughput_of": "Rendimiento de" } } \ No newline at end of file diff --git a/beszel/site/src/locales/fr/translation.json b/beszel/site/src/locales/fr/translation.json new file mode 100644 index 0000000..2116ba4 --- /dev/null +++ b/beszel/site/src/locales/fr/translation.json @@ -0,0 +1,178 @@ +{ + "all_systems": "Tous les systèmes", + "filter": "Filtrer...", + "copy": "Copier", + "add": "Ajouter", + "system": "Système", + "systems": "Systèmes", + "cancel": "Annuler", + "continue": "Continuer", + "home": { + "active_alerts": "Alertes actives", + "active_des": "Dépasse {{value}}{{unit}} en moyenne au cours des {{minutes}} dernières minutes", + "subtitle_1": "Mis à jour en temps réel. Appuyez sur", + "subtitle_2": "pour ouvrir la palette de commandes." + }, + "systems_table": { + "system": "Système", + "memory": "Mémoire", + "cpu": "CPU", + "disk": "Disque", + "net": "Réseau", + "agent": "Agent", + "no_systems_found": "Aucun système trouvé.", + "open_menu": "Ouvrir le menu", + "resume": "Reprendre", + "pause": "Pause", + "copy_host": "Copier l'hôte", + "delete": "Supprimer", + "delete_confirm": "Êtes-vous sûr de vouloir supprimer {{name}}?", + "delete_confirm_des_1": "Cette action est irréversible. Cela supprimera définitivement tous les enregistrements actuels de", + "delete_confirm_des_2": "de la base de données." + }, + "alerts": { + "title": "Alertes", + "subtitle_1": "Voir", + "notification_settings": "paramètres de notification", + "subtitle_2": "pour configurer comment vous recevez les alertes.", + "overwrite_existing_alerts": "Écraser les alertes existantes", + "info": { + "status": "Statut", + "status_des": "Déclenchement lorsque le statut passe de haut en bas.", + "cpu_usage": "Utilisation du CPU", + "cpu_usage_des": "Déclenchement lorsque l'utilisation du CPU dépasse un seuil.", + "memory_usage": "Utilisation de la mémoire", + "memory_usage_des": "Déclenchement lorsque l'utilisation de la mémoire dépasse un seuil.", + "disk_usage": "Utilisation du disque", + "disk_usage_des": "Déclenchement lorsque l'utilisation de n'importe quel disque dépasse un seuil.", + "bandwidth": "Bande passante", + "bandwidth_des": "Déclenchement lorsque le total montant/descendant dépasse un seuil.", + "temperature": "Température", + "temperature_des": "Déclenchement lorsque n'importe quel capteur dépasse un seuil." + }, + "average_exceeds": "La moyenne dépasse", + "for": "Pour", + "minute": "minute", + "minutes": "minutes" + }, + "settings": { + "settings": "Paramètres", + "subtitle": "Gérer les préférences d'affichage et de notification.", + "save_settings": "Enregistrer les paramètres", + "export_configuration": "Exporter la configuration", + "general": { + "title": "Général", + "subtitle": "Modifier les options générales de l'application.", + "language": { + "title": "Langue", + "subtitle_1": "Vous voulez nous aider à améliorer nos traductions? Consultez", + "subtitle_2": "pour plus de détails.", + "preferred_language": "Langue préférée" + }, + "chart_options": { + "title": "Options de graphique", + "subtitle": "Ajuster les options d'affichage pour les graphiques.", + "default_time_period": "Période de temps par défaut", + "default_time_period_des": "Définit la plage de temps par défaut pour les graphiques lorsqu'un système est consulté." + } + }, + "notifications": { + "title": "Notifications", + "subtitle_1": "Configurer comment vous recevez les notifications d'alerte.", + "subtitle_2": "Vous cherchez plutôt où créer des alertes? Cliquez sur la cloche", + "subtitle_3": "icônes dans le tableau des systèmes.", + "email": { + "title": "Notifications par email", + "please": "Veuillez", + "configure_an_SMTP_server": "configurer un serveur SMTP", + "to_ensure_alerts_are_delivered": "pour garantir la livraison des alertes.", + "to_email_s": "À email(s)", + "enter_email_address": "Entrez l'adresse email...", + "des": "Enregistrez l'adresse en utilisant la touche entrée ou la virgule. Laissez vide pour désactiver les notifications par email." + }, + "webhook_push": { + "title": "Notifications Webhook / Push", + "des_1": "Beszel utilise", + "des_2": "pour s'intégrer avec des services de notification populaires.", + "add_url": "Ajouter une URL" + } + }, + "yaml_config": { + "short_title": "Config YAML", + "title": "Configuration YAML", + "subtitle": "Exporter la configuration actuelle de vos systèmes.", + "des_1": "Les systèmes peuvent être gérés dans un fichier", + "des_2": "à l'intérieur de votre répertoire de données.", + "des_3": "À chaque redémarrage, les systèmes dans la base de données seront mis à jour pour correspondre aux systèmes définis dans le fichier.", + "alert": { + "title": "Attention - perte de données potentielle", + "des_1": "Les systèmes existants non définis dans", + "des_2": "seront supprimés. Veuillez faire des sauvegardes régulières." + } + }, + "language": "Langue" + }, + "user_dm": { + "users": "Utilisateurs", + "logs": "Journaux", + "backups": "Sauvegardes", + "auth_providers": "Fournisseurs d'authentification", + "log_out": "Déconnexion" + }, + "themes": { + "toggle_theme": "Changer de thème", + "light": "Clair", + "dark": "Sombre", + "system": "Système" + }, + "add_system": { + "add_new_system": "Ajouter un nouveau système", + "binary": "Binaire", + "dialog_des_1": "L'agent doit être en cours d'exécution sur le système pour se connecter. Copiez le", + "dialog_des_2": "pour l'agent ci-dessous.", + "name": "Nom", + "host_ip": "Hôte / IP", + "port": "Port", + "public_key": "Clé publique", + "click_to_copy": "Cliquez pour copier", + "command": "commande", + "add_system": "Ajouter un système" + }, + "command": { + "search": "Rechercher des systèmes ou des paramètres...", + "pages_settings": "Pages / Paramètres", + "dashboard": "Tableau de bord", + "documentation": "Documentation", + "SMTP_settings": "Paramètres SMTP", + "page": "Page", + "admin": "Admin" + }, + "monitor": { + "toggle_grid": "Changer de grille", + "average": "Moyenne", + "max_1_min": "Max 1 min", + "total_cpu_usage": "Utilisation totale du CPU", + "cpu_des": "utilisation du CPU à l'échelle du système", + "docker_cpu_usage": "Utilisation du CPU Docker", + "docker_cpu_des": "Utilisation moyenne du CPU des conteneurs", + "total_memory_usage": "Utilisation totale de la mémoire", + "memory_des": "Utilisation précise au moment enregistré", + "docker_memory_usage": "Utilisation de la mémoire Docker", + "docker_memory_des": "Utilisation de la mémoire des conteneurs Docker", + "disk_space": "Espace disque", + "disk_des": "Utilisation de la partition racine", + "disk_io": "E/S disque", + "disk_io_des": "Débit du système de fichiers racine", + "bandwidth": "Bande passante", + "bandwidth_des": "Trafic réseau des interfaces publiques", + "docker_network_io": "E/S réseau Docker", + "docker_network_io_des": "Trafic réseau des conteneurs Docker", + "swap_usage": "Utilisation du swap", + "swap_des": "Espace swap utilisé par le système", + "temperature": "Température", + "temperature_des": "Températures des capteurs du système", + "usage": "Utilisation", + "disk_usage_of": "Utilisation du disque de", + "throughput_of": "Débit de" + } +} \ No newline at end of file diff --git a/beszel/site/src/locales/ru/translation.json b/beszel/site/src/locales/ru/translation.json new file mode 100644 index 0000000..281b1f4 --- /dev/null +++ b/beszel/site/src/locales/ru/translation.json @@ -0,0 +1,178 @@ +{ + "all_systems": "Все системы", + "filter": "Фильтр...", + "copy": "Копировать", + "add": "Добавить", + "system": "Система", + "systems": "Системы", + "cancel": "Отмена", + "continue": "Продолжить", + "home": { + "active_alerts": "Активные предупреждения", + "active_des": "Превышает {{value}}{{unit}} в среднем за последние {{minutes}} минут", + "subtitle_1": "Обновляется в реальном времени. Нажмите", + "subtitle_2": "чтобы открыть палитру команд." + }, + "systems_table": { + "system": "Система", + "memory": "Память", + "cpu": "ЦП", + "disk": "Диск", + "net": "Сеть", + "agent": "Агент", + "no_systems_found": "Систем не найдено.", + "open_menu": "Открыть меню", + "resume": "Возобновить", + "pause": "Пауза", + "copy_host": "Копировать хост", + "delete": "Удалить", + "delete_confirm": "Вы уверены, что хотите удалить {{name}}?", + "delete_confirm_des_1": "Это действие нельзя отменить. Это навсегда удалит все текущие записи для", + "delete_confirm_des_2": "из базы данных." + }, + "alerts": { + "title": "Предупреждения", + "subtitle_1": "См.", + "notification_settings": "настройки уведомлений", + "subtitle_2": "чтобы настроить, как вы получаете предупреждения.", + "overwrite_existing_alerts": "Перезаписать существующие предупреждения", + "info": { + "status": "Статус", + "status_des": "Срабатывает, когда статус переключается между вверх и вниз.", + "cpu_usage": "Использование ЦП", + "cpu_usage_des": "Срабатывает, когда использование ЦП превышает порог.", + "memory_usage": "Использование памяти", + "memory_usage_des": "Срабатывает, когда использование памяти превышает порог.", + "disk_usage": "Использование диска", + "disk_usage_des": "Срабатывает, когда использование любого диска превышает порог.", + "bandwidth": "Пропускная способность", + "bandwidth_des": "Срабатывает, когда суммарная загрузка/выгрузка превышает порог.", + "temperature": "Температура", + "temperature_des": "Срабатывает, когда любой датчик превышает порог." + }, + "average_exceeds": "Среднее значение превышает", + "for": "За", + "minute": "минуту", + "minutes": "минут" + }, + "settings": { + "settings": "Настройки", + "subtitle": "Управление предпочтениями отображения и уведомлений.", + "save_settings": "Сохранить настройки", + "export_configuration": "Экспорт конфигурации", + "general": { + "title": "Общие", + "subtitle": "Изменить общие параметры приложения.", + "language": { + "title": "Язык", + "subtitle_1": "Хотите помочь нам улучшить наши переводы? Ознакомьтесь с", + "subtitle_2": "для получения дополнительной информации.", + "preferred_language": "Предпочитаемый язык" + }, + "chart_options": { + "title": "Параметры диаграммы", + "subtitle": "Настроить параметры отображения для диаграмм.", + "default_time_period": "Период по умолчанию", + "default_time_period_des": "Устанавливает диапазон времени по умолчанию для диаграмм при просмотре системы." + } + }, + "notifications": { + "title": "Уведомления", + "subtitle_1": "Настройте, как вы получаете уведомления о предупреждениях.", + "subtitle_2": "Ищете, где создать предупреждения? Нажмите на колокольчик", + "subtitle_3": "значки в таблице систем.", + "email": { + "title": "Уведомления по электронной почте", + "please": "Пожалуйста", + "configure_an_SMTP_server": "настройте SMTP-сервер", + "to_ensure_alerts_are_delivered": "чтобы гарантировать доставку предупреждений.", + "to_email_s": "На электронную почту(ы)", + "enter_email_address": "Введите адрес электронной почты...", + "des": "Сохраните адрес, используя клавишу ввода или запятую. Оставьте пустым, чтобы отключить уведомления по электронной почте." + }, + "webhook_push": { + "title": "Webhook / Push уведомления", + "des_1": "Beszel использует", + "des_2": "для интеграции с популярными сервисами уведомлений.", + "add_url": "Добавить URL" + } + }, + "yaml_config": { + "short_title": "YAML Конфиг", + "title": "YAML Конфигурация", + "subtitle": "Экспорт текущей конфигурации ваших систем.", + "des_1": "Системы могут управляться в", + "des_2": "файле в вашем каталоге данных.", + "des_3": "При каждом перезапуске системы в базе данных будут обновлены, чтобы соответствовать системам, определенным в файле.", + "alert": { + "title": "Внимание - возможная потеря данных", + "des_1": "Существующие системы, не определенные в", + "des_2": "будут удалены. Пожалуйста, делайте регулярные резервные копии." + } + }, + "language": "Язык" + }, + "user_dm": { + "users": "Пользователи", + "logs": "Журналы", + "backups": "Резервные копии", + "auth_providers": "Поставщики аутентификации", + "log_out": "Выйти" + }, + "themes": { + "toggle_theme": "Переключить тему", + "light": "Светлая", + "dark": "Темная", + "system": "Система" + }, + "add_system": { + "add_new_system": "Добавить новую систему", + "binary": "Бинарный", + "dialog_des_1": "Агент должен работать на системе для подключения. Скопируйте", + "dialog_des_2": "для агента ниже.", + "name": "Имя", + "host_ip": "Хост / IP", + "port": "Порт", + "public_key": "Публичный ключ", + "click_to_copy": "Нажмите, чтобы скопировать", + "command": "команда", + "add_system": "Добавить систему" + }, + "command": { + "search": "Поиск систем или настроек...", + "pages_settings": "Страницы / Настройки", + "dashboard": "Панель управления", + "documentation": "Документация", + "SMTP_settings": "Настройки SMTP", + "page": "Страница", + "admin": "Админ" + }, + "monitor": { + "toggle_grid": "Переключить сетку", + "average": "Среднее", + "max_1_min": "Макс 1 мин", + "total_cpu_usage": "Общее использование ЦП", + "cpu_des": "системное использование ЦП", + "docker_cpu_usage": "Использование ЦП Docker", + "docker_cpu_des": "Среднее использование ЦП контейнеров", + "total_memory_usage": "Общее использование памяти", + "memory_des": "Точное использование на момент записи", + "docker_memory_usage": "Использование памяти Docker", + "docker_memory_des": "Использование памяти контейнеров Docker", + "disk_space": "Место на диске", + "disk_des": "Использование корневого раздела", + "disk_io": "Дисковый ввод/вывод", + "disk_io_des": "Пропускная способность корневой файловой системы", + "bandwidth": "Пропускная способность", + "bandwidth_des": "Сетевой трафик публичных интерфейсов", + "docker_network_io": "Сетевой ввод/вывод Docker", + "docker_network_io_des": "Сетевой трафик контейнеров Docker", + "swap_usage": "Использование swap", + "swap_des": "Использование swap пространства системой", + "temperature": "Температура", + "temperature_des": "Температуры датчиков системы", + "usage": "Использование", + "disk_usage_of": "Использование диска", + "throughput_of": "Пропускная способность" + } +} \ No newline at end of file diff --git a/beszel/site/src/locales/zh-CN/translation.json b/beszel/site/src/locales/zh-CN/translation.json new file mode 100644 index 0000000..673f30c --- /dev/null +++ b/beszel/site/src/locales/zh-CN/translation.json @@ -0,0 +1,178 @@ +{ + "all_systems": "所有服务器", + "filter": "筛选...", + "copy": "复制", + "add": "添加", + "system": "服务器", + "systems": "服务器", + "cancel": "取消", + "continue": "继续", + "home": { + "active_alerts": "活动警报", + "active_des": "在过去 {{minutes}} 分钟内超过 {{value}}{{unit}} 平均值", + "subtitle_1": "实时更新。按", + "subtitle_2": "打开命令面板。" + }, + "systems_table": { + "system": "服务器", + "memory": "内存", + "cpu": "CPU", + "disk": "磁盘", + "net": "网络", + "agent": "客户端", + "no_systems_found": "未找到服务器。", + "open_menu": "打开菜单", + "resume": "恢复", + "pause": "暂停", + "copy_host": "复制主机", + "delete": "删除", + "delete_confirm": "您确定要删除 {{name}} 吗?", + "delete_confirm_des_1": "此操作无法撤消。这将永久从数据库中删除", + "delete_confirm_des_2": "的所有记录。" + }, + "alerts": { + "title": "警报", + "subtitle_1": "查看", + "notification_settings": "通知设置", + "subtitle_2": "配置如何接收警报。", + "overwrite_existing_alerts": "覆盖现有警报", + "info": { + "status": "状态", + "status_des": "状态在 在线/离线 之间切换时触发。", + "cpu_usage": "CPU 使用率", + "cpu_usage_des": "当 CPU 使用率超过阈值时触发。", + "memory_usage": "内存使用率", + "memory_usage_des": "当内存使用率超过阈值时触发。", + "disk_usage": "磁盘使用率", + "disk_usage_des": "当任何磁盘的使用率超过阈值时触发。", + "bandwidth": "带宽", + "bandwidth_des": "当组合的 上行/下行 流量超过阈值时触发。", + "temperature": "温度", + "temperature_des": "当任何传感器超过阈值时触发。" + }, + "average_exceeds": "平均值超过", + "for": "持续", + "minute": "分钟", + "minutes": "分钟" + }, + "settings": { + "settings": "设置", + "subtitle": "管理显示和通知偏好。", + "save_settings": "保存设置", + "export_configuration": "导出配置", + "general": { + "title": "常规", + "subtitle": "更改通用应用程序选项。", + "language": { + "title": "语言", + "subtitle_1": "想帮助我们改进翻译吗?查看", + "subtitle_2": "了解更多详情。", + "preferred_language": "首选语言" + }, + "chart_options": { + "title": "图表选项", + "subtitle": "调整图表的显示选项。", + "default_time_period": "默认时间段", + "default_time_period_des": "设置查看服务器时图表的默认时间范围。" + } + }, + "notifications": { + "title": "通知", + "subtitle_1": "设置如何接收警报通知。", + "subtitle_2": "正在寻找创建警报的位置?点击服务器列表中的", + "subtitle_3": "图标。", + "email": { + "title": "电子邮件通知", + "please": "配置", + "configure_an_SMTP_server": "SMTP 服务器", + "to_ensure_alerts_are_delivered": "以确保邮件可以成功发送。", + "to_email_s": "收件人邮箱", + "enter_email_address": "输入邮箱地址...", + "des": "使用 Enter 键或逗号保存地址。留空以禁用电子邮件通知。" + }, + "webhook_push": { + "title": "Webhook / 推送通知", + "des_1": "Beszel 使用", + "des_2": "与流行的通知服务集成。", + "add_url": "添加 URL" + } + }, + "yaml_config": { + "short_title": "YAML 配置", + "title": "YAML 配置", + "subtitle": "导出当前系统配置。", + "des_1": "系统使用", + "des_2": "配置文件储存管理数据", + "des_3": "每次重启时,数据库中的系统将更新以匹配文件中定义的系统。", + "alert": { + "title": "警告 - 潜在数据丢失", + "des_1": "未在", + "des_2": "中定义的现有系统将被删除。请定期备份。" + } + }, + "language": "语言" + }, + "user_dm": { + "users": "用户", + "logs": "日志", + "backups": "备份", + "auth_providers": "身份验证提供者", + "log_out": "登出" + }, + "themes": { + "toggle_theme": "切换主题", + "light": "浅色", + "dark": "深色", + "system": "系统" + }, + "add_system": { + "add_new_system": "添加新服务器", + "binary": "二进制文件", + "dialog_des_1": "客户端必须在服务器上运行才能连接。复制", + "dialog_des_2": "到目标服务器以安装。", + "name": "名称", + "host_ip": "主机/IP", + "port": "端口", + "public_key": "公钥", + "click_to_copy": "点击复制", + "command": "命令", + "add_system": "添加服务器" + }, + "command": { + "search": "在设置或系统中搜索...", + "pages_settings": "页面 / 设置", + "dashboard": "仪表盘", + "documentation": "文档", + "SMTP_settings": "SMTP 设置", + "page": "页面", + "admin": "管理员" + }, + "monitor": { + "toggle_grid": "切换布局", + "average": "平均值", + "max_1_min": "1 分钟最大", + "total_cpu_usage": "总 CPU 使用率", + "cpu_des": "系统范围的 CPU 利用率", + "docker_cpu_usage": "Docker CPU 使用率", + "docker_cpu_des": "Docker 的平均 CPU 利用率", + "total_memory_usage": "总内存使用率", + "memory_des": "记录时间点的精确利用率", + "docker_memory_usage": "Docker 内存使用率", + "docker_memory_des": "Docker 容器的内存使用率", + "disk_space": "磁盘空间", + "disk_des": "根分区的使用情况", + "disk_io": "磁盘 I/O", + "disk_io_des": "根文件系统的吞吐量", + "bandwidth": "带宽", + "bandwidth_des": "公共接口的网络流量", + "docker_network_io": "Docker 网络 I/O", + "docker_network_io_des": "Docker 容器的网络流量", + "swap_usage": "交换空间使用率", + "swap_des": "系统使用的交换空间", + "temperature": "温度", + "temperature_des": "系统传感器的温度", + "usage": "使用率", + "disk_usage_of": "的磁盘使用率", + "throughput_of": "的吞吐量" + } +} \ No newline at end of file diff --git a/beszel/site/src/locales/zh-HK/translation.json b/beszel/site/src/locales/zh-HK/translation.json new file mode 100644 index 0000000..b52984e --- /dev/null +++ b/beszel/site/src/locales/zh-HK/translation.json @@ -0,0 +1,178 @@ +{ + "all_systems": "所有伺服器", + "filter": "篩選...", + "copy": "複製", + "add": "新增", + "system": "伺服器", + "systems": "伺服器", + "cancel": "取消", + "continue": "繼續", + "home": { + "active_alerts": "活動警報", + "active_des": "在過去 {{minutes}} 分鐘內超過 {{value}}{{unit}} 平均值", + "subtitle_1": "即時更新。按", + "subtitle_2": "打開指令面板。" + }, + "systems_table": { + "system": "伺服器", + "memory": "記憶體", + "cpu": "CPU", + "disk": "磁碟", + "net": "網絡", + "agent": "客戶端", + "no_systems_found": "未找到伺服器。", + "open_menu": "打開選單", + "resume": "恢復", + "pause": "暫停", + "copy_host": "複製主機", + "delete": "刪除", + "delete_confirm": "您確定要刪除 {{name}} 嗎?", + "delete_confirm_des_1": "此操作無法撤銷。這將永久從資料庫中刪除", + "delete_confirm_des_2": "的所有記錄。" + }, + "alerts": { + "title": "警報", + "subtitle_1": "查看", + "notification_settings": "通知設定", + "subtitle_2": "配置如何接收警報。", + "overwrite_existing_alerts": "覆蓋現有警報", + "info": { + "status": "狀態", + "status_des": "狀態在 在線/離線 之間切換時觸發。", + "cpu_usage": "CPU 使用率", + "cpu_usage_des": "當 CPU 使用率超過閾值時觸發。", + "memory_usage": "記憶體使用率", + "memory_usage_des": "當記憶體使用率超過閾值時觸發。", + "disk_usage": "磁碟使用率", + "disk_usage_des": "當任何磁碟的使用率超過閾值時觸發。", + "bandwidth": "頻寬", + "bandwidth_des": "當組合的 上行/下行 流量超過閾值時觸發。", + "temperature": "溫度", + "temperature_des": "當任何感應器超過閾值時觸發。" + }, + "average_exceeds": "平均值超過", + "for": "持續", + "minute": "分鐘", + "minutes": "分鐘" + }, + "settings": { + "settings": "設定", + "subtitle": "管理顯示和通知偏好。", + "save_settings": "儲存設定", + "export_configuration": "匯出配置", + "general": { + "title": "一般", + "subtitle": "更改通用應用程式選項。", + "language": { + "title": "語言", + "subtitle_1": "想幫助我們改進翻譯嗎?查看", + "subtitle_2": "了解更多詳情。", + "preferred_language": "首選語言" + }, + "chart_options": { + "title": "圖表選項", + "subtitle": "調整圖表的顯示選項。", + "default_time_period": "預設時間段", + "default_time_period_des": "設定查看伺服器時圖表的預設時間範圍。" + } + }, + "notifications": { + "title": "通知", + "subtitle_1": "設定如何接收警報通知。", + "subtitle_2": "正在尋找建立警報的位置?點擊伺服器列表中的", + "subtitle_3": "圖示。", + "email": { + "title": "電郵通知", + "please": "配置", + "configure_an_SMTP_server": "SMTP 伺服器", + "to_ensure_alerts_are_delivered": "以確保郵件可以成功發送。", + "to_email_s": "收件人電郵", + "enter_email_address": "輸入電郵地址...", + "des": "使用 Enter 鍵或逗號儲存地址。留空以禁用電郵通知。" + }, + "webhook_push": { + "title": "Webhook / 推送通知", + "des_1": "Beszel 使用", + "des_2": "與流行的通知服務整合。", + "add_url": "新增 URL" + } + }, + "yaml_config": { + "short_title": "YAML 配置", + "title": "YAML 配置", + "subtitle": "匯出當前系統配置。", + "des_1": "系統使用", + "des_2": "配置檔案儲存管理數據", + "des_3": "每次重啟時,資料庫中的系統將更新以匹配檔案中定義的系統。", + "alert": { + "title": "警告 - 潛在數據丟失", + "des_1": "未在", + "des_2": "中定義的現有系統將被刪除。請定期備份。" + } + }, + "language": "語言" + }, + "user_dm": { + "users": "用戶", + "logs": "日誌", + "backups": "備份", + "auth_providers": "身份驗證提供者", + "log_out": "登出" + }, + "themes": { + "toggle_theme": "切換主題", + "light": "淺色", + "dark": "深色", + "system": "系統" + }, + "add_system": { + "add_new_system": "新增新伺服器", + "binary": "二進制檔案", + "dialog_des_1": "客戶端必須在伺服器上運行才能連接。複製", + "dialog_des_2": "到目標伺服器以安裝。", + "name": "名稱", + "host_ip": "主機/IP", + "port": "端口", + "public_key": "公鑰", + "click_to_copy": "點擊複製", + "command": "指令", + "add_system": "新增伺服器" + }, + "command": { + "search": "在設定或系統中搜尋...", + "pages_settings": "頁面 / 設定", + "dashboard": "儀表板", + "documentation": "文件", + "SMTP_settings": "SMTP 設定", + "page": "頁面", + "admin": "管理員" + }, + "monitor": { + "toggle_grid": "切換佈局", + "average": "平均值", + "max_1_min": "1 分鐘最大", + "total_cpu_usage": "總 CPU 使用率", + "cpu_des": "系統範圍的 CPU 利用率", + "docker_cpu_usage": "Docker CPU 使用率", + "docker_cpu_des": "Docker 的平均 CPU 利用率", + "total_memory_usage": "總記憶體使用率", + "memory_des": "記錄時間點的精確利用率", + "docker_memory_usage": "Docker 記憶體使用率", + "docker_memory_des": "Docker 容器的記憶體使用率", + "disk_space": "磁碟空間", + "disk_des": "根分區的使用情況", + "disk_io": "磁碟 I/O", + "disk_io_des": "根檔案系統的吞吐量", + "bandwidth": "頻寬", + "bandwidth_des": "公共介面的網絡流量", + "docker_network_io": "Docker 網絡 I/O", + "docker_network_io_des": "Docker 容器的網絡流量", + "swap_usage": "交換空間使用率", + "swap_des": "系統使用的交換空間", + "temperature": "溫度", + "temperature_des": "系統感應器的溫度", + "usage": "使用率", + "disk_usage_of": "的磁碟使用率", + "throughput_of": "的吞吐量" + } +} \ No newline at end of file From 2c66f9310142a7510bd2572ae24ab1e2f2086e5b Mon Sep 17 00:00:00 2001 From: Arsfy Date: Mon, 28 Oct 2024 18:53:55 +0800 Subject: [PATCH 4/5] crowdin --- beszel/site/src/components/routes/settings/general.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beszel/site/src/components/routes/settings/general.tsx b/beszel/site/src/components/routes/settings/general.tsx index f16eca7..1e31bbd 100644 --- a/beszel/site/src/components/routes/settings/general.tsx +++ b/beszel/site/src/components/routes/settings/general.tsx @@ -50,7 +50,7 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us

{t('settings.general.language.title')}

{t('settings.general.language.subtitle_1')}{' '} - + Crowdin {' '} {t('settings.general.language.subtitle_2')} From f6e391f8a97d78848c22241295446147e6cd2a2b Mon Sep 17 00:00:00 2001 From: Henry Dollman Date: Tue, 29 Oct 2024 18:08:55 -0400 Subject: [PATCH 5/5] i18n tweaks / layout fixes --- beszel/site/src/components/add-system.tsx | 26 +-- beszel/site/src/components/lang-toggle.tsx | 15 +- beszel/site/src/components/routes/home.tsx | 10 +- .../components/routes/settings/general.tsx | 18 +- beszel/site/src/locales/en/translation.json | 2 +- beszel/site/src/main.tsx | 155 +++++++++--------- beszel/site/tailwind.config.js | 1 + 7 files changed, 120 insertions(+), 107 deletions(-) diff --git a/beszel/site/src/components/add-system.tsx b/beszel/site/src/components/add-system.tsx index 9faab67..c0892ec 100644 --- a/beszel/site/src/components/add-system.tsx +++ b/beszel/site/src/components/add-system.tsx @@ -9,12 +9,7 @@ import { DialogTrigger, } from '@/components/ui/dialog' import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' -import { - Tabs, - TabsContent, - TabsList, - TabsTrigger, -} from "@/components/ui/tabs" +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' @@ -51,7 +46,9 @@ export function AddSystemButton({ className }: { className?: string }) { } function copyInstallCommand(port: string) { - copyToClipboard(`curl -sL https://raw.githubusercontent.com/henrygd/beszel/main/supplemental/scripts/install-agent.sh -o install-agent.sh && chmod +x install-agent.sh && ./install-agent.sh -p ${port} -k "${publicKey}"`) + copyToClipboard( + `curl -sL https://raw.githubusercontent.com/henrygd/beszel/main/supplemental/scripts/install-agent.sh -o install-agent.sh && chmod +x install-agent.sh && ./install-agent.sh -p ${port} -k "${publicKey}"` + ) } async function handleSubmit(e: SubmitEvent) { @@ -77,10 +74,11 @@ export function AddSystemButton({ className }: { className?: string }) { className={cn('flex gap-1 max-xs:h-[2.4rem]', className, isReadOnlyUser() && 'hidden')} > - {t('add')}{t('system')} + {t('add')} + {t('system')} - + {t('add_system.add_new_system')} @@ -93,14 +91,16 @@ export function AddSystemButton({ className }: { className?: string }) { {t('add_system.dialog_des_1')}{' '} - docker-compose.yml {t('add_system.dialog_des_2')} + docker-compose.yml{' '} + {t('add_system.dialog_des_2')} {/* Binary */} {t('add_system.dialog_des_1')}{' '} - install command {t('add_system.dialog_des_2')} + install command{' '} + {t('add_system.dialog_des_2')}

@@ -161,7 +161,7 @@ export function AddSystemButton({ className }: { className?: string }) {
{/* Docker */} - + @@ -30,7 +31,7 @@ export function LangToggle() { {languages.map(({ lang, label }) => ( i18n.changeLanguage(lang)} > {label} diff --git a/beszel/site/src/components/routes/home.tsx b/beszel/site/src/components/routes/home.tsx index 8bcca40..5bb9e11 100644 --- a/beszel/site/src/components/routes/home.tsx +++ b/beszel/site/src/components/routes/home.tsx @@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next' const SystemsTable = lazy(() => import('../systems-table/systems-table')) -const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; +const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0 export default function () { const { t } = useTranslation() @@ -78,13 +78,13 @@ export default function () { > - {alert.sysname} {info.name} + {alert.sysname} {t(info.name)} - {t('active_des', { + {t('home.active_des', { value: alert.value, unit: info.unit, - minutes: alert.min + minutes: alert.min, })} {t('home.subtitle_1')}{' '} - {isMac ? '⌘' : "Ctrl"}K + {isMac ? '⌘' : 'Ctrl'}K {' '} {t('home.subtitle_2')} diff --git a/beszel/site/src/components/routes/settings/general.tsx b/beszel/site/src/components/routes/settings/general.tsx index 1e31bbd..fc0eb37 100644 --- a/beszel/site/src/components/routes/settings/general.tsx +++ b/beszel/site/src/components/routes/settings/general.tsx @@ -9,7 +9,7 @@ import { } from '@/components/ui/select' import { chartTimeData } from '@/lib/utils' import { Separator } from '@/components/ui/separator' -import { LoaderCircleIcon, SaveIcon } from 'lucide-react' +import { LanguagesIcon, LoaderCircleIcon, SaveIcon } from 'lucide-react' import { UserSettings } from '@/types' import { saveSettings } from './layout' import { useState, useEffect } from 'react' @@ -21,8 +21,8 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us const { t, i18n } = useTranslation() useEffect(() => { - document.documentElement.lang = i18n.language; - }, [i18n.language]); + document.documentElement.lang = i18n.language + }, [i18n.language]) const [isLoading, setIsLoading] = useState(false) @@ -47,7 +47,10 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
+
-

{t('settings.general.chart_options.title')}

+

+ {t('settings.general.chart_options.title')} +

{t('settings.general.chart_options.subtitle')}

diff --git a/beszel/site/src/locales/en/translation.json b/beszel/site/src/locales/en/translation.json index 3c5e822..59fccd5 100644 --- a/beszel/site/src/locales/en/translation.json +++ b/beszel/site/src/locales/en/translation.json @@ -9,7 +9,7 @@ "continue": "Continue", "home": { "active_alerts": "Active Alerts", - "active_des": "Exceeds {{value}}{{unit}} average in last {{minutes}} minutes", + "active_des": "Exceeds {{value}}{{unit}} average in last {{minutes}} min", "subtitle_1": "Updated in real time. Press", "subtitle_2": "to open the command palette." }, diff --git a/beszel/site/src/main.tsx b/beszel/site/src/main.tsx index 91d4efd..5dfb2fb 100644 --- a/beszel/site/src/main.tsx +++ b/beszel/site/src/main.tsx @@ -51,6 +51,7 @@ import { AddSystemButton } from './components/add-system.tsx' import './lib/i18n.ts' import { useTranslation } from 'react-i18next' +import { TFunction } from 'i18next' // const ServerDetail = lazy(() => import('./components/routes/system.tsx')) const CommandPalette = lazy(() => import('./components/command-palette.tsx')) @@ -114,6 +115,84 @@ const App = () => { } } +const Navbar = (t: TFunction<'translation', undefined>) => { + return ( +
+ ) +} + const Layout = () => { const { t } = useTranslation() @@ -130,81 +209,7 @@ const Layout = () => { return ( <> -
-
- - - - -
- - - - - - - - - - - {pb.authStore.model?.email} - - - {isAdmin() && ( - <> - - - - {t('user_dm.users')} - - - - - - {t('systems')} - - - - - - {t('user_dm.logs')} - - - - - - {t('user_dm.backups')} - - - - - - {t('user_dm.auth_providers')} - - - - - )} - - pb.authStore.clear()}> - - {t('user_dm.log_out')} - - - - -
-
-
+
{Navbar(t)}
diff --git a/beszel/site/tailwind.config.js b/beszel/site/tailwind.config.js index 91935a5..f1cd6df 100644 --- a/beszel/site/tailwind.config.js +++ b/beszel/site/tailwind.config.js @@ -24,6 +24,7 @@ module.exports = { }, screens: { xs: '425px', + 450: '450px', }, colors: { green: {