diff --git a/beszel/site/bun.lockb b/beszel/site/bun.lockb index 32b1d2dd3..ccc65f336 100755 Binary files a/beszel/site/bun.lockb and b/beszel/site/bun.lockb differ diff --git a/beszel/site/package.json b/beszel/site/package.json index ef0ec635f..07634434e 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 85abeff41..9faab6705 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' @@ -18,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) @@ -41,6 +50,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) @@ -64,85 +77,116 @@ 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 - - The agent must be running on the system to connect. Copy the{' '} - docker-compose.yml for the agent - below. - - -
-
-
- - -
-
- - -
-
- - -
-
- - -
- - - - - - -

Click to copy

-
-
-
+ + + {t('add_system.add_new_system')} + + Docker + {t('add_system.binary')} + + + {/* Docker */} + + + {t('add_system.dialog_des_1')}{' '} + docker-compose.yml {t('add_system.dialog_des_2')} + + + {/* Binary */} + + + {t('add_system.dialog_des_1')}{' '} + install command {t('add_system.dialog_des_2')} + + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + + + + + +

{t('add_system.click_to_copy')}

+
+
+
+
-
- - - - - + {/* Docker */} + + + + + + + {/* Binary */} + + + + + + + + ) diff --git a/beszel/site/src/components/alerts/alert-button.tsx b/beszel/site/src/components/alerts/alert-button.tsx index 929e69a17..ecdc569fe 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/alerts/alerts-system.tsx b/beszel/site/src/components/alerts/alerts-system.tsx index fa6ea91b7..cc8c30ef3 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/lang-toggle.tsx b/beszel/site/src/components/lang-toggle.tsx new file mode 100644 index 000000000..46ffa806a --- /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 35e1bc3bc..257c70cf1 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 0a0f64639..8bcca400b 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/config-yaml.tsx b/beszel/site/src/components/routes/settings/config-yaml.tsx index 18fbacf18..078ae6a83 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/general.tsx b/beszel/site/src/components/routes/settings/general.tsx index 9cc331a05..1e31bbd78 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{' '} - - discussion on GitHub + {t('settings.general.language.subtitle_1')}{' '} + + 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 05d547bbb..b5cf630f1 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: t('settings.yaml_config.short_title'), + 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 503ccef07..feb01007c 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,51 +72,55 @@ 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')}

-

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 && ( @@ -146,7 +153,7 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting onClick={addWebhook} > - Add URL + {t('settings.notifications.webhook_push.add_url')}
@@ -161,7 +168,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 94b20e3e8..56271d1ad 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')) @@ -96,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 */ @@ -349,7 +352,7 @@ export default function SystemDetail({ name }: { name: string }) {
@@ -373,10 +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} > ) => { @@ -534,7 +537,7 @@ function ContainerFilterBar() { return ( <> >] }) { + const { t } = useTranslation() + const [max, setMax] = store const Icon = max ? ChartMax : ChartAverage @@ -571,10 +576,10 @@ function SelectAvgMax({ - Average + {t('monitor.average')} - Max 1 min + {t('monitor.max_1_min')} diff --git a/beszel/site/src/components/systems-table/systems-table.tsx b/beszel/site/src/components/systems-table/systems-table.tsx index c955e3af8..549ab7883 100644 --- a/beszel/site/src/components/systems-table/systems-table.tsx +++ b/beszel/site/src/components/systems-table/systems-table.tsx @@ -63,6 +63,7 @@ import { cn, copyToClipboard, decimalString, isReadOnlyUser } from '@/lib/utils' import AlertsButton from '../alerts/alert-button' import { navigate } from '../router' import { EthernetIcon } from '../ui/icons' +import { useTranslation } from 'react-i18next' function CellFormatter(info: CellContext) { 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 000000000..ef4f6d484 --- /dev/null +++ b/beszel/site/src/lib/i18n.ts @@ -0,0 +1,34 @@ +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'; +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) + .use(initReactI18next) + .init({ + resources: { + en: { translation: en }, + 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: { + 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 000000000..d4af14cb5 --- /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-CN", + "label": "简体中文" + }, + { + "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 8e133fe6f..6a25f4b5f 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 000000000..68a287671 --- /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 new file mode 100644 index 000000000..3c5e8229b --- /dev/null +++ b/beszel/site/src/locales/en/translation.json @@ -0,0 +1,178 @@ +{ + "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", + "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.", + "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.", + "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" + }, + "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" + }, + "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 new file mode 100644 index 000000000..5ba9b649c --- /dev/null +++ b/beszel/site/src/locales/es/translation.json @@ -0,0 +1,178 @@ +{ + "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": "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": "Configuraciones", + "subtitle": "Administre las preferencias de visualización y notificación.", + "save_settings": "Guardar configuraciones", + "export_configuration": "Exportar configuración", + "general": { + "title": "General", + "subtitle": "Cambie las opciones generales de la aplicación.", + "language": { + "title": "Idioma", + "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á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 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" + }, + "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. Copia 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": "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 000000000..2116ba48b --- /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 000000000..281b1f47e --- /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 000000000..673f30c31 --- /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 000000000..b52984e4b --- /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 diff --git a/beszel/site/src/main.tsx b/beszel/site/src/main.tsx index 89b620421..91d4efd5b 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 = () => {