mirror of
https://github.com/seriousm4x/UpSnap.git
synced 2026-08-03 10:58:44 -04:00
feat: i18n completed #71
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import LL, { locale } from '$lib/i18n/i18n-svelte';
|
||||
import { backendUrl, permission, pocketbase } from '$lib/stores/pocketbase';
|
||||
import type { Device } from '$lib/types/device';
|
||||
import {
|
||||
@@ -10,6 +11,7 @@
|
||||
faPen,
|
||||
faRotateLeft
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import type { Locale } from 'date-fns';
|
||||
import { formatDistance, parseISO } from 'date-fns';
|
||||
import Fa from 'svelte-fa';
|
||||
import toast from 'svelte-french-toast';
|
||||
@@ -20,13 +22,13 @@
|
||||
|
||||
let moreButtons = [
|
||||
{
|
||||
text: 'Edit',
|
||||
text: $LL.device.card_btn_more_edit(),
|
||||
icon: faPen,
|
||||
onClick: () => goto(`/device/${device.id}`),
|
||||
requires: $pocketbase.authStore.isAdmin || $permission.update?.includes(device.id)
|
||||
},
|
||||
{
|
||||
text: 'Sleep',
|
||||
text: $LL.device.card_btn_more_sleep(),
|
||||
icon: faBed,
|
||||
onClick: () => sleep(),
|
||||
requires:
|
||||
@@ -35,7 +37,7 @@
|
||||
device.sol_enabled
|
||||
},
|
||||
{
|
||||
text: 'Reboot',
|
||||
text: $LL.device.card_btn_more_reboot(),
|
||||
icon: faRotateLeft,
|
||||
onClick: () => reboot(),
|
||||
requires:
|
||||
@@ -55,6 +57,22 @@
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// dynamically import locales
|
||||
// it's ugly but the only working solution i found in ~ 3 hours
|
||||
var dateFnsLocale: Locale;
|
||||
$: if ($locale !== undefined) {
|
||||
(async () => {
|
||||
switch ($locale) {
|
||||
case 'de':
|
||||
dateFnsLocale = (await import('date-fns/locale/de/index.js')).default;
|
||||
break;
|
||||
case 'en-US':
|
||||
dateFnsLocale = (await import('date-fns/locale/en-US/index.js')).default;
|
||||
break;
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function sleep() {
|
||||
fetch(`${backendUrl}api/upsnap/sleep/${device.id}`, {
|
||||
headers: {
|
||||
@@ -92,36 +110,44 @@
|
||||
{#if device.wake_cron_enabled || device.shutdown_cron_enabled || device.password}
|
||||
<div class="flex flex-row flex-wrap gap-2">
|
||||
{#if device.wake_cron_enabled}
|
||||
<div class="tooltip" data-tip="Wake cron">
|
||||
<div class="tooltip" data-tip={$LL.device.card_tooltip_wake_cron()}>
|
||||
<span class="badge badge-success gap-1 p-3"
|
||||
><Fa icon={faCircleArrowUp} />{device.wake_cron}</span
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{#if device.shutdown_cron_enabled}
|
||||
<div class="tooltip" data-tip="Shutdown cron">
|
||||
<div class="tooltip" data-tip={$LL.device.card_tooltip_shutdown_cron()}>
|
||||
<span class="badge badge-error gap-1 p-3"
|
||||
><Fa icon={faCircleArrowDown} />{device.shutdown_cron}</span
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{#if device.password}
|
||||
<div class="tooltip" data-tip="Wake password">
|
||||
<span class="badge gap-1 p-3"><Fa icon={faLock} />Password</span>
|
||||
<div class="tooltip" data-tip={$LL.device.card_tooltip_wake_password()}>
|
||||
<span class="badge gap-1 p-3"><Fa icon={faLock} />{$LL.device.card_password()}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="card-actions mt-auto items-center">
|
||||
<span class="tooltip" data-tip="Last status change: {device.updated}">
|
||||
{formatDistance(parseISO(device.updated), now, {
|
||||
includeSeconds: true,
|
||||
addSuffix: true
|
||||
})}
|
||||
<span
|
||||
class="tooltip"
|
||||
data-tip="{$LL.device.card_tooltip_last_status_change()}: {device.updated}"
|
||||
>
|
||||
{#if dateFnsLocale !== undefined}
|
||||
{formatDistance(parseISO(device.updated), now, {
|
||||
includeSeconds: true,
|
||||
addSuffix: true,
|
||||
locale: dateFnsLocale
|
||||
})}
|
||||
{/if}
|
||||
</span>
|
||||
{#if moreButtons.filter((btn) => btn.requires).length > 0}
|
||||
<div class="dropdown dropdown-top dropdown-end bg-base-300 ms-auto">
|
||||
<label tabindex="-1" class="btn btn-sm m-1" for="more-{device.id}">More</label>
|
||||
<label tabindex="-1" class="btn btn-sm m-1" for="more-{device.id}"
|
||||
>{$LL.device.card_btn_more()}</label
|
||||
>
|
||||
<ul
|
||||
id="more-{device.id}"
|
||||
tabindex="-1"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import LL from '$lib/i18n/i18n-svelte';
|
||||
import { backendUrl, permission, pocketbase } from '$lib/stores/pocketbase';
|
||||
import type { Device } from '$lib/types/device';
|
||||
import { faCircle, faPowerOff } from '@fortawesome/free-solid-svg-icons';
|
||||
@@ -19,25 +20,25 @@
|
||||
$: seconds = timeout % 60;
|
||||
$: if (device.status === 'pending' || device.status === '') {
|
||||
disabled = true;
|
||||
hoverText = 'Pending';
|
||||
hoverText = $LL.device.card_nic_tooltip_pending();
|
||||
} else if (device.status === 'online') {
|
||||
if (device.shutdown_cmd === '') {
|
||||
disabled = true;
|
||||
hoverText = 'No shutdown command set';
|
||||
hoverText = $LL.device.card_nic_tooltip_shutdown_no_cmd();
|
||||
} else if (!$pocketbase.authStore.isAdmin && !$permission.power?.includes(device.id)) {
|
||||
disabled = true;
|
||||
hoverText = 'No permission to shut down this device';
|
||||
hoverText = $LL.device.card_nic_tooltip_shutdown_no_permission();
|
||||
} else {
|
||||
disabled = false;
|
||||
hoverText = 'Shut down';
|
||||
hoverText = $LL.device.card_nic_tooltip_shutdown();
|
||||
}
|
||||
} else if (device.status === 'offline') {
|
||||
if (!$pocketbase.authStore.isAdmin && !$permission.power?.includes(device.id)) {
|
||||
disabled = true;
|
||||
hoverText = 'No permission to power on this device';
|
||||
hoverText = $LL.device.card_nic_tooltip_power_no_permission();
|
||||
} else {
|
||||
disabled = false;
|
||||
hoverText = 'Power on';
|
||||
hoverText = $LL.device.card_nic_tooltip_power();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import DeviceFormPort from '$lib/components/DeviceFormPort.svelte';
|
||||
import LL from '$lib/i18n/i18n-svelte';
|
||||
import { permission, pocketbase } from '$lib/stores/pocketbase';
|
||||
import type { Device, Group, Port } from '$lib/types/device';
|
||||
import { faSave, faTrash, faX } from '@fortawesome/free-solid-svg-icons';
|
||||
@@ -43,7 +44,7 @@
|
||||
.collection('devices')
|
||||
.update(device.id, device)
|
||||
.then(() => {
|
||||
toast.success(`Updated ${device.name}`);
|
||||
toast.success($LL.toasts.device_updated({ device: device.name }));
|
||||
goto('/');
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -57,7 +58,7 @@
|
||||
.collection('devices')
|
||||
.create(device)
|
||||
.then(() => {
|
||||
toast.success(`Created ${device.name}`);
|
||||
toast.success($LL.toasts.device_created({ device: device.name }));
|
||||
goto('/');
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -75,7 +76,6 @@
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message);
|
||||
|
||||
newport = undefined;
|
||||
});
|
||||
return newport;
|
||||
@@ -91,7 +91,6 @@
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message);
|
||||
|
||||
newport = undefined;
|
||||
});
|
||||
return newport;
|
||||
@@ -102,7 +101,7 @@
|
||||
.collection('devices')
|
||||
.delete(device.id)
|
||||
.then(() => {
|
||||
toast.success(`Deleted ${device.name}`);
|
||||
toast.success($LL.toasts.device_deleted({ device: device.name }));
|
||||
goto('/');
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -133,7 +132,7 @@
|
||||
})
|
||||
.then((res) => {
|
||||
deviceGroups = [...deviceGroups, res as Group];
|
||||
toast.success(`Created group ${newGroup}`);
|
||||
toast.success($LL.toasts.group_created({ group: newGroup }));
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message);
|
||||
@@ -151,7 +150,7 @@
|
||||
.delete(group.id)
|
||||
.then(async () => {
|
||||
await getGroups();
|
||||
toast.success(`Deleted group ${group.name}`);
|
||||
toast.success($LL.toasts.group_deleted({ group: group.name }));
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message);
|
||||
@@ -172,19 +171,19 @@
|
||||
<form on:submit|preventDefault={save}>
|
||||
<div class="card w-full bg-base-300 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">General</h2>
|
||||
<h2 class="card-title">{$LL.device.general()}</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label" for="device-name">
|
||||
<div class="label-text">
|
||||
<span>Name</span>
|
||||
<span>{$LL.device.general_name()}</span>
|
||||
<span class="text-error">*</span>
|
||||
</div>
|
||||
</label>
|
||||
<input
|
||||
id="device-name"
|
||||
type="text"
|
||||
placeholder="Device name"
|
||||
placeholder={$LL.device.general_name_placeholder()}
|
||||
class="input w-full max-w-xs"
|
||||
bind:value={device.name}
|
||||
required
|
||||
@@ -193,7 +192,7 @@
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label" for="device-ip">
|
||||
<div class="label-text">
|
||||
<span>IP</span>
|
||||
<span>{$LL.device.general_ip()}</span>
|
||||
<span class="text-error">*</span>
|
||||
</div>
|
||||
</label>
|
||||
@@ -209,7 +208,7 @@
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label" for="device-mac">
|
||||
<div class="label-text">
|
||||
<span>Mac</span>
|
||||
<span>{$LL.device.general_mac()}</span>
|
||||
<span class="text-error">*</span>
|
||||
</div>
|
||||
</label>
|
||||
@@ -225,7 +224,7 @@
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label" for="device-netmask">
|
||||
<div class="label-text">
|
||||
<span>Netmask</span>
|
||||
<span>{$LL.device.general_netmask()}</span>
|
||||
<span class="text-error">*</span>
|
||||
</div>
|
||||
</label>
|
||||
@@ -238,14 +237,14 @@
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<span class="badge text-error self-center">* required field</span>
|
||||
<span class="badge text-error self-center">* {$LL.device.general_required_field()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card w-full bg-base-300 shadow-xl mt-6">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Ports</h2>
|
||||
<p class="my-2">UpSnap can also check if given ports are open. You can define them below.</p>
|
||||
<h2 class="card-title">{$LL.device.ports()}</h2>
|
||||
<p class="my-2">{$LL.device.ports_desc()}</p>
|
||||
<div class="form-control w-full">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
<!-- eslint-disable-next-line @typescript-eslint/no-unused-vars -->
|
||||
@@ -256,16 +255,16 @@
|
||||
<button
|
||||
class="btn btn-wide btn-primary mt-4"
|
||||
on:click={() => createEmptyPort()}
|
||||
type="button">Add new port</button
|
||||
type="button">{$LL.device.ports_add_new()}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card w-full bg-base-300 shadow-xl mt-6">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Link</h2>
|
||||
<h2 class="card-title">{$LL.device.link()}</h2>
|
||||
<p class="my-2">
|
||||
Makes your device name a clickable link, perfect for linking a dashboard for example.
|
||||
{$LL.device.link_desc()}
|
||||
</p>
|
||||
<div class="form-control w-full">
|
||||
<input
|
||||
@@ -279,23 +278,17 @@
|
||||
</div>
|
||||
<div class="card w-full bg-base-300 shadow-xl mt-6">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Wake</h2>
|
||||
<h2 class="card-title">{$LL.device.wake()}</h2>
|
||||
<p class="my-2">
|
||||
You can power this device using a scheduled cron job. If you are not familiar with cron, you
|
||||
can read about it <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank"
|
||||
>here</a
|
||||
>
|
||||
or refer to the
|
||||
<a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank"
|
||||
>package documentation</a
|
||||
>
|
||||
for more information.
|
||||
{$LL.device.wake_desc()}
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html $LL.settings.ping_interval_desc2()}
|
||||
</p>
|
||||
<div class="form-control flex flex-row flex-wrap gap-4">
|
||||
<div>
|
||||
<label class="label" for="wake-cron">
|
||||
<span class="label-text"
|
||||
>Wake cron
|
||||
>{$LL.device.wake_cron()}
|
||||
{#if device.wake_cron_enabled}
|
||||
<span class="text-error">*</span>
|
||||
{/if}
|
||||
@@ -313,7 +306,7 @@
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<label class="label cursor-pointer" for="wake-cron-enable">
|
||||
<span class="label-text">Enable wake cron</span>
|
||||
<span class="label-text">{$LL.device.wake_cron_enable()}</span>
|
||||
</label>
|
||||
<input
|
||||
id="wake-cron-enable"
|
||||
@@ -330,26 +323,15 @@
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Sleep-On-LAN</h2>
|
||||
<p class="mt-2">
|
||||
You can put computers to sleep using the <a
|
||||
class="link"
|
||||
href="https://github.com/SR-G/sleep-on-lan"
|
||||
target="_blank">Sleep-On-LAN</a
|
||||
>
|
||||
tool. Sleep-On-LAN (SOL) is an external tool/daemon that operates on the PCs you want to put
|
||||
to sleep, providing a REST endpoint. For instructions on setting up Sleep-On-LAN, please refer
|
||||
to the
|
||||
<a href="https://github.com/SR-G/sleep-on-lan#usage" class="link" target="_blank">Usage</a> section.
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html $LL.device.sol_desc1()}
|
||||
</p>
|
||||
<p>
|
||||
SOL is confiugred to send requests over HTTP instead of UDP to enable authorization and make
|
||||
requests more reliable.
|
||||
{$LL.device.sol_desc2()}
|
||||
</p>
|
||||
<p class="font-bold">
|
||||
Therefore, please ensure that you include <span class="badge">HTTP:<YOURPORT></span>
|
||||
in the <span class="badge">Listeners</span> section of the
|
||||
<a href="https://github.com/SR-G/sleep-on-lan#configuration" class="link" target="_blank"
|
||||
>SOL configuration</a
|
||||
>.
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html $LL.device.sol_desc3()}
|
||||
</p>
|
||||
<div class="flex flex-row flex-wrap gap-4 items-end mt-4">
|
||||
<div>
|
||||
@@ -362,14 +344,14 @@
|
||||
bind:checked={device.sol_enabled}
|
||||
/>
|
||||
<label class="label cursor-pointer" for="sol-enable">
|
||||
<span class="label-text">Enable Sleep-On-LAN</span>
|
||||
<span class="label-text">{$LL.device.sol_enable()}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control flex flex-col">
|
||||
<label class="label" for="sol-port">
|
||||
<span class="label-text"
|
||||
>SOL Port
|
||||
>{$LL.device.sol_port()}
|
||||
{#if device.sol_enabled}
|
||||
<span class="text-error">*</span>
|
||||
{/if}
|
||||
@@ -380,7 +362,6 @@
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
placeholder="Default: 8009"
|
||||
class="input w-80"
|
||||
bind:value={device.sol_port}
|
||||
disabled={!device.sol_enabled}
|
||||
@@ -399,14 +380,14 @@
|
||||
bind:checked={device.sol_auth}
|
||||
/>
|
||||
<label class="label cursor-pointer" for="sol-auth">
|
||||
<span class="label-text">Authorization</span>
|
||||
<span class="label-text">{$LL.device.sol_authorization()}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control flex flex-col">
|
||||
<label class="label" for="sol-user">
|
||||
<span class="label-text"
|
||||
>SOL User
|
||||
>{$LL.device.sol_user()}
|
||||
{#if device.sol_auth}
|
||||
<span class="text-error">*</span>
|
||||
{/if}
|
||||
@@ -415,7 +396,7 @@
|
||||
<input
|
||||
id="sol-user"
|
||||
type="text"
|
||||
placeholder="username"
|
||||
placeholder={$LL.device.sol_user()}
|
||||
class="input w-80"
|
||||
bind:value={device.sol_user}
|
||||
disabled={!device.sol_auth}
|
||||
@@ -426,7 +407,7 @@
|
||||
<div class="form-control flex flex-col">
|
||||
<label class="label" for="sol-password">
|
||||
<span class="label-text"
|
||||
>SOL Password
|
||||
>{$LL.device.sol_password()}
|
||||
{#if device.sol_auth}
|
||||
<span class="text-error">*</span>
|
||||
{/if}
|
||||
@@ -435,7 +416,7 @@
|
||||
<input
|
||||
id="sol-password"
|
||||
type="password"
|
||||
placeholder="password"
|
||||
placeholder={$LL.device.sol_password()}
|
||||
class="input w-80"
|
||||
bind:value={device.sol_password}
|
||||
disabled={!device.sol_auth}
|
||||
@@ -449,31 +430,27 @@
|
||||
|
||||
<div class="card w-full bg-base-300 shadow-xl mt-6">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Shutdown</h2>
|
||||
<h2 class="card-title">{$LL.device.shutdown()}</h2>
|
||||
<p class="my-2">
|
||||
This <strong>shell command</strong> will run inside your container (if you use docker) or on
|
||||
your host (if you use the binary). To verify that works you can run the command inside the
|
||||
container or on your host shell first. Common commands are
|
||||
<span class="badge">net rpc</span> for windows, <span class="badge">sshpass</span> for linux
|
||||
or
|
||||
<span class="badge">curl</span> in general to make web requests.
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html $LL.device.shutdown_desc()}
|
||||
</p>
|
||||
<p class="my-2 font-bold">Examples:</p>
|
||||
<p class="my-2 font-bold">{$LL.device.shutdown_examples()}</p>
|
||||
<div class="mockup-code text-sm max-w-fit">
|
||||
<pre data-prefix="#"><code>Shutdown remote windows machine:</code></pre>
|
||||
<pre data-prefix="#"><code>{$LL.device.shutdown_examples_windows()}</code></pre>
|
||||
<pre data-prefix="$" class="text-warning"><code
|
||||
>net rpc shutdown -I 192.168.1.13 -U "user%password"</code
|
||||
></pre>
|
||||
</div>
|
||||
<div class="mockup-code text-sm max-w-fit">
|
||||
<pre data-prefix="#"><code>Shutdown remote linux machine:</code></pre>
|
||||
<pre data-prefix="#"><code>{$LL.device.shutdown_examples_linux()}</code></pre>
|
||||
<pre data-prefix="$" class="text-warning"><code
|
||||
>sshpass -p password ssh -o "StrictHostKeyChecking=no" user@192.168.1.13 "sudo poweroff"</code
|
||||
></pre>
|
||||
</div>
|
||||
<div class="form-control w-full">
|
||||
<label class="label cursor-pointer" for="shutdown-cmd">
|
||||
<span class="label-text">Shutdown command</span>
|
||||
<span class="label-text">{$LL.device.shutdown_cmd()}</span>
|
||||
</label>
|
||||
<input
|
||||
id="shutdown-cmd"
|
||||
@@ -484,14 +461,13 @@
|
||||
/>
|
||||
</div>
|
||||
<p class="my-2">
|
||||
Just like setting a cron to wake the device, you can also schedule a cron job to shut down
|
||||
this device.
|
||||
{$LL.device.shutdown_cron_desc()}
|
||||
</p>
|
||||
<div class="form-control flex flex-row flex-wrap gap-4">
|
||||
<div>
|
||||
<label class="label" for="shutdown-cron">
|
||||
<span class="label-text"
|
||||
>Shutdown cron
|
||||
>{$LL.device.shutdown_cron()}
|
||||
{#if device.shutdown_cron_enabled}
|
||||
<span class="text-error">*</span>
|
||||
{/if}
|
||||
@@ -509,7 +485,7 @@
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<label class="label cursor-pointer" for="shutdown-cron-enable">
|
||||
<span class="label-text">Enable shutdown cron</span>
|
||||
<span class="label-text">{$LL.device.shutdown_cron_enable()}</span>
|
||||
</label>
|
||||
<input
|
||||
id="shutdown-cron-enable"
|
||||
@@ -523,11 +499,10 @@
|
||||
</div>
|
||||
<div class="card w-full bg-base-300 shadow-xl mt-6">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Password</h2>
|
||||
<h2 class="card-title">{$LL.device.password()}</h2>
|
||||
<p class="my-2">
|
||||
Some network cards have the option to set a password for magic packets, also called <span
|
||||
class="badge">SecureON</span
|
||||
>. Password can only be 0, 4 or 6 characters in length.
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html $LL.device.password_desc()}
|
||||
</p>
|
||||
<div class="form-control w-full">
|
||||
<input
|
||||
@@ -542,9 +517,9 @@
|
||||
</div>
|
||||
<div class="card w-full bg-base-300 shadow-xl mt-6">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Groups</h2>
|
||||
<h2 class="card-title">{$LL.device.groups()}</h2>
|
||||
<p class="my-2">
|
||||
You can add devices to a group to have them sorted by group on the dashboard.
|
||||
{$LL.device.groups_desc()}
|
||||
</p>
|
||||
<div class="flex flex-row flex-wrap gap-2">
|
||||
{#each deviceGroups as group}
|
||||
@@ -574,12 +549,12 @@
|
||||
<div class="join">
|
||||
<input
|
||||
class="input input-bordered join-item"
|
||||
placeholder="e.g. 'Basement' or 'Office'"
|
||||
placeholder={$LL.device.groups_placeholder()}
|
||||
type="text"
|
||||
bind:value={newGroup}
|
||||
/>
|
||||
<button class="btn btn-primary join-item" type="button" on:click={() => addGroup()}
|
||||
>Create</button
|
||||
>{$LL.buttons.add()}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -587,19 +562,21 @@
|
||||
<div class="card-actions mt-6 justify-end gap-4">
|
||||
{#if $pocketbase.authStore.isAdmin || $permission.delete?.includes(device.id)}
|
||||
<button class="btn btn-error" type="button" on:click={() => deleteModal.showModal()}
|
||||
><Fa icon={faTrash} />Delete</button
|
||||
><Fa icon={faTrash} />{$LL.buttons.delete()}</button
|
||||
>
|
||||
<dialog class="modal" bind:this={deleteModal}>
|
||||
<form method="dialog" class="modal-box">
|
||||
<h3 class="font-bold text-lg">Confirm delete</h3>
|
||||
<p class="py-4">Are you sure you want to delete <strong>{device.name}</strong>?</p>
|
||||
<h3 class="font-bold text-lg">{$LL.users.confirm_delete_title()}</h3>
|
||||
<p class="py-4">{$LL.users.confirm_delete_desc({ username: device.name })}</p>
|
||||
<div class="modal-action">
|
||||
<button class="btn">Cancle</button>
|
||||
<button class="btn btn-error" on:click={() => deleteDevice()}>Delete</button>
|
||||
<button class="btn">{$LL.buttons.cancle()}</button>
|
||||
<button class="btn btn-error" on:click={() => deleteDevice()}
|
||||
>{$LL.buttons.delete()}</button
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
{/if}
|
||||
<button class="btn btn-success" type="submit"><Fa icon={faSave} />Save</button>
|
||||
<button class="btn btn-success" type="submit"><Fa icon={faSave} />{$LL.buttons.save()}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import LL from '$lib/i18n/i18n-svelte';
|
||||
import { pocketbase } from '$lib/stores/pocketbase';
|
||||
import type { Device, Port } from '$lib/types/device';
|
||||
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||
@@ -32,7 +33,7 @@
|
||||
<div class="flex flex-row gap-2">
|
||||
<div>
|
||||
<label class="label p-0" for="port-name-{index}">
|
||||
<span class="label-text">Name</span>
|
||||
<span class="label-text">{$LL.device.ports_name()}</span>
|
||||
</label>
|
||||
<input
|
||||
id="port-name-{index}"
|
||||
@@ -45,7 +46,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<label class="label p-0" for="port-number-{index}">
|
||||
<span class="label-text">Number</span>
|
||||
<span class="label-text">{$LL.device.ports_number()}</span>
|
||||
</label>
|
||||
<input
|
||||
id="port-number-{index}"
|
||||
@@ -63,7 +64,7 @@
|
||||
<button
|
||||
class="btn btn-xs btn-outline btn-error"
|
||||
on:click={() => deletePort(device.expand.ports[index])}
|
||||
type="button"><Fa icon={faTrash} />Delete</button
|
||||
type="button"><Fa icon={faTrash} />{$LL.buttons.delete()}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import LL from '$lib/i18n/i18n-svelte';
|
||||
import { backendUrl, permission, pocketbase } from '$lib/stores/pocketbase';
|
||||
import { settingsPub } from '$lib/stores/settings';
|
||||
import {
|
||||
@@ -100,7 +101,7 @@
|
||||
{/if}
|
||||
<li>
|
||||
<a href="/" class="px-4 py-2" class:active={$page.url.pathname === '/'}
|
||||
><Fa icon={faHome} />Home</a
|
||||
><Fa icon={faHome} />{$LL.home.page_title()}</a
|
||||
>
|
||||
</li>
|
||||
{#if $pocketbase.authStore.isAdmin}
|
||||
@@ -109,7 +110,7 @@
|
||||
href="/users"
|
||||
class="px-4 py-2"
|
||||
class:active={$page.url.pathname.startsWith('/users')}
|
||||
><Fa icon={faUsersGear} />Users</a
|
||||
><Fa icon={faUsersGear} />{$LL.users.page_title()}</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
@@ -117,7 +118,7 @@
|
||||
href="/settings/"
|
||||
class="px-4 py-2"
|
||||
class:active={$page.url.pathname.startsWith('/settings')}
|
||||
><Fa icon={faCog} />Settings</a
|
||||
><Fa icon={faCog} />{$LL.settings.settings_title()}</a
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
@@ -141,18 +142,18 @@
|
||||
<ul class="menu menu-horizontal px-1 gap-1">
|
||||
<li>
|
||||
<a href="/" class="p-2" class:active={$page.url.pathname === '/'}
|
||||
><Fa icon={faHome} />Home</a
|
||||
><Fa icon={faHome} />{$LL.home.page_title()}</a
|
||||
>
|
||||
</li>
|
||||
{#if $pocketbase.authStore.isAdmin}
|
||||
<li>
|
||||
<a href="/users" class="p-2" class:active={$page.url.pathname.startsWith('/users')}
|
||||
><Fa icon={faUsersGear} />Users</a
|
||||
><Fa icon={faUsersGear} />{$LL.users.page_title()}</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/settings/" class="p-2" class:active={$page.url.pathname.startsWith('/settings')}
|
||||
><Fa icon={faCog} />Settings</a
|
||||
><Fa icon={faCog} />{$LL.settings.settings_title()}</a
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
@@ -161,7 +162,7 @@
|
||||
<div class="dropdown md:dropdown-end">
|
||||
<div tabindex="-1" class="btn normal-case btn-ghost">
|
||||
<Fa icon={faSwatchbook} />
|
||||
<span class="hidden font-normal md:inline">Theme</span>
|
||||
<span class="hidden font-normal md:inline">{$LL.navbar.theme()}</span>
|
||||
<Fa icon={faChevronDown} />
|
||||
</div>
|
||||
<div
|
||||
@@ -202,7 +203,7 @@
|
||||
{#if $pocketbase.authStore.isAdmin || $permission.create}
|
||||
<a class="btn btn-success me-4" href="/device/new">
|
||||
<Fa icon={faPlus} />
|
||||
New
|
||||
{$LL.navbar.new()}
|
||||
</a>
|
||||
{/if}
|
||||
<div class="dropdown dropdown-end">
|
||||
@@ -221,11 +222,11 @@
|
||||
: $pocketbase.authStore.model.username}
|
||||
</li>
|
||||
<li>
|
||||
<a href="/account"><Fa icon={faUserGear} />Edit account</a>
|
||||
<a href="/account"><Fa icon={faUserGear} />{$LL.navbar.edit_account()}</a>
|
||||
</li>
|
||||
<li>
|
||||
<div on:click={async () => logout()} on:keydown={async () => logout()} role="none">
|
||||
<Fa icon={faDoorOpen} />Logout
|
||||
<Fa icon={faDoorOpen} />{$LL.navbar.logout()}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import PageLoading from '$lib/components/PageLoading.svelte';
|
||||
import LL from '$lib/i18n/i18n-svelte';
|
||||
import { backendUrl, pocketbase } from '$lib/stores/pocketbase';
|
||||
import { settingsPriv } from '$lib/stores/settings';
|
||||
import type { Device } from '$lib/types/device';
|
||||
@@ -46,7 +47,7 @@
|
||||
})
|
||||
.then((res) => {
|
||||
settingsPriv.set(res as SettingsPrivate);
|
||||
toast.success('Scan range saved');
|
||||
toast.success($LL.device.network_scan_range_saved());
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message);
|
||||
@@ -88,7 +89,7 @@
|
||||
async function addSingle(device: ScannedDevice) {
|
||||
await createDevice(device)
|
||||
.then(() => {
|
||||
toast.success(`Added ${device.name}`);
|
||||
toast.success($LL.toasts.device_created({ device: device.name }));
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message);
|
||||
@@ -109,7 +110,7 @@
|
||||
});
|
||||
})
|
||||
);
|
||||
toast.success(`Added ${count} devices`);
|
||||
toast.success($LL.toasts.devices_created_multiple({ count: count }));
|
||||
goto('/');
|
||||
}
|
||||
</script>
|
||||
@@ -117,16 +118,14 @@
|
||||
{#if $settingsPriv}
|
||||
<div class="card w-full bg-base-300 shadow-xl mt-6">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Network scan</h2>
|
||||
<h2 class="card-title">{$LL.device.tabs[1]()}</h2>
|
||||
<p class="my-2">
|
||||
Automatically scan your network for devices. For this to work, you need to run UpSnap as
|
||||
root/admin and have nmap installed and available in your $PATH (For docker users, thats
|
||||
already the case and you don't need to do anything). Scanning might take some seconds.
|
||||
{$LL.device.network_scan_desc()}
|
||||
</p>
|
||||
<div class="flex flex-row flex-wrap gap-4 items-end">
|
||||
<form on:submit|preventDefault={saveSettings}>
|
||||
<label class="label" for="scan-range">
|
||||
<span class="label-text">IP range</span>
|
||||
<span class="label-text">{$LL.device.network_scan_ip_range()}</span>
|
||||
</label>
|
||||
<div class="join">
|
||||
<input
|
||||
@@ -136,27 +135,30 @@
|
||||
placeholder="192.168.1.0/24"
|
||||
bind:value={scanRange}
|
||||
/>
|
||||
<button class="btn btn-primary join-item" type="submit">Save</button>
|
||||
<button class="btn btn-primary join-item" type="submit">{$LL.buttons.save()}</button>
|
||||
</div>
|
||||
</form>
|
||||
<div>
|
||||
<div>
|
||||
{#if !$settingsPriv.scan_range}
|
||||
<button class="btn btn-error" disabled>
|
||||
<Fa icon={faX} /> No scan range
|
||||
<Fa icon={faX} />
|
||||
{$LL.device.network_scan_no_range()}
|
||||
</button>
|
||||
{:else if scanRange !== $settingsPriv.scan_range}
|
||||
<button class="btn btn-error" disabled>
|
||||
<Fa icon={faX} /> Unsaved changes
|
||||
<Fa icon={faX} />
|
||||
{$LL.device.network_scan_unsaved_changes()}
|
||||
</button>
|
||||
{:else if scanRunning}
|
||||
<button class="btn no-animation">
|
||||
<span class="loading loading-spinner" />
|
||||
Scan running
|
||||
{$LL.device.network_scan_running()}
|
||||
</button>
|
||||
{:else}
|
||||
<button class="btn btn-success" on:click={() => scan()}>
|
||||
<Fa icon={faMagnifyingGlass} /> Scan
|
||||
<Fa icon={faMagnifyingGlass} />
|
||||
{$LL.device.network_scan()}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -172,20 +174,20 @@
|
||||
<div class="collapse-content">
|
||||
<div class="flex flex-row flex-wrap gap-4">
|
||||
<div>
|
||||
<strong>IP:</strong><br />
|
||||
<strong>{$LL.device.network_scan_ip()}</strong><br />
|
||||
{device.ip}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Mac:</strong><br />
|
||||
<strong>{$LL.device.network_scan_mac()}</strong><br />
|
||||
{device.mac}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Mac vendor:</strong><br />
|
||||
<strong>{$LL.device.network_scan_mac_vendor()}</strong><br />
|
||||
{device.mac_vendor}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong>Netmask:</strong><br />
|
||||
<strong>{$LL.device.network_scan_netmask()}</strong><br />
|
||||
{scanResponse.netmask}
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
@@ -194,24 +196,24 @@
|
||||
on:click={(e) => {
|
||||
addSingle(device);
|
||||
e.currentTarget.disabled = true;
|
||||
}}><Fa icon={faPlus} />Add</button
|
||||
}}><Fa icon={faPlus} />{$LL.buttons.add()}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
<h2 class="card-title mt-4">Add all devices</h2>
|
||||
<h2 class="card-title mt-4">{$LL.device.network_scan_add_all()}</h2>
|
||||
<div class="form-control max-w-fit">
|
||||
<label class="label cursor-pointer">
|
||||
<input type="checkbox" class="checkbox" bind:checked={replaceNetmaskCheckbox} />
|
||||
<span class="label-text ms-2">Replace netmask for all devices?</span>
|
||||
<span class="label-text ms-2">{$LL.device.network_scan_replace_netmask()}</span>
|
||||
</label>
|
||||
</div>
|
||||
{#if replaceNetmaskCheckbox}
|
||||
<div class="form-control max-w-fit">
|
||||
<label class="label cursor-pointer" for="replaceNetmaskInput">
|
||||
<span class="label-text ms-2">New netmask</span>
|
||||
<span class="label-text ms-2">{$LL.device.network_scan_new_netmask()}</span>
|
||||
</label>
|
||||
<input
|
||||
id="replaceNetmaskInput"
|
||||
@@ -226,7 +228,7 @@
|
||||
<div class="form-control max-w-fit">
|
||||
<label class="label cursor-pointer">
|
||||
<input type="checkbox" class="checkbox" bind:checked={addAllCheckbox} />
|
||||
<span class="label-text ms-2">Include devices where name is "Unknown"</span>
|
||||
<span class="label-text ms-2">{$LL.device.network_scan_include_unknown()}</span>
|
||||
</label>
|
||||
{#if addAllCheckbox}
|
||||
<button
|
||||
@@ -234,7 +236,8 @@
|
||||
on:click={() => addAll()}
|
||||
disabled={scanResponse.devices.length === 0}
|
||||
>
|
||||
<Fa icon={faPlus} /> Add all ({scanResponse.devices.length})
|
||||
<Fa icon={faPlus} />
|
||||
{$LL.device.network_scan_add_all()} ({scanResponse.devices.length})
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
@@ -242,7 +245,8 @@
|
||||
on:click={() => addAll()}
|
||||
disabled={scanResponse.devices.filter((dev) => dev.name !== 'Unknown').length === 0}
|
||||
>
|
||||
<Fa icon={faPlus} /> Add all ({scanResponse.devices.filter(
|
||||
<Fa icon={faPlus} />
|
||||
{$LL.device.network_scan_add_all()} ({scanResponse.devices.filter(
|
||||
(dev) => dev.name !== 'Unknown'
|
||||
).length})
|
||||
</button>
|
||||
@@ -255,7 +259,8 @@
|
||||
on:click={() => addAll()}
|
||||
disabled={scanResponse.devices.length === 0}
|
||||
>
|
||||
<Fa icon={faPlus} /> Add all ({scanResponse.devices.length})
|
||||
<Fa icon={faPlus} />
|
||||
{$LL.device.network_scan_add_all()} ({scanResponse.devices.length})
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
import en_US from '../en-US';
|
||||
import type { Translation } from '../i18n-types';
|
||||
|
||||
const de_DE = {
|
||||
...(en_US as unknown as Translation),
|
||||
home: {
|
||||
order_groups: 'Gruppen',
|
||||
order_name: 'Name',
|
||||
order_ip: 'IP',
|
||||
order_tooltip: 'Sortieren',
|
||||
message_no_devices: 'Keine Geräte.',
|
||||
message_add_first_device: 'Füge dein erstes Gerät hinzu',
|
||||
message_grant_permissions:
|
||||
'Bitte frag den Administrator, dir Berechtigungen für bestehende Geräte zu erteilen oder neue Geräte zu erstellen.'
|
||||
},
|
||||
account: {
|
||||
page_title: 'Account',
|
||||
account_type_admin: 'Admin',
|
||||
account_type_user: 'Benutzer',
|
||||
avatar_title: 'Avatar',
|
||||
language_title: 'Sprache',
|
||||
language_option_auto: 'Automatisch',
|
||||
change_password_title: 'Passwort ändern',
|
||||
change_password_body: 'Nachdem das Passwort geändert wurde, musst du dich neu einloggen.',
|
||||
change_password_label: 'Altes Passwort',
|
||||
change_password_new: 'Neues Password',
|
||||
change_password_confirm: 'Password bestätigen'
|
||||
},
|
||||
device: {
|
||||
page_title: 'Neues Gerät',
|
||||
tabs: ['Manuell', 'Netzwerkscan']
|
||||
},
|
||||
login: {
|
||||
welcome: 'Willkommen',
|
||||
email_label: 'Email oder Nutzername:',
|
||||
password_label: 'Passwort:',
|
||||
btn_more: 'Mehr',
|
||||
menu_title_auth_providers: 'Andere Login Provider',
|
||||
btn_login: 'Login'
|
||||
},
|
||||
settings: {
|
||||
settings_title: 'Einstellungen',
|
||||
ping_interval_title: 'Ping Intervall',
|
||||
ping_interval_desc1:
|
||||
'Legt den Intervall fest, in dem Geräte gepingt werden. Leer lassen um den Standardwert von <span class="badge">@every 3s</span> zu verwenden.',
|
||||
ping_interval_desc2:
|
||||
'Lerne mehr über die korrekte Cron Syntax auf <a class="link" href="https://de.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> oder sieh dir die <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">Paketdokumentation</a> an.',
|
||||
lazy_ping_title: 'Lazy Ping',
|
||||
lazy_ping_desc:
|
||||
'Wenn Lazy Ping aktiviert ist, pingt UpSnap die Geräte nur an, wenn ein aktiver Benutzer die Website besucht. Wenn es ausgeschaltet ist, pingt UpSnap die Geräte immer an.',
|
||||
lazy_ping_enable: 'Aktivieren',
|
||||
website_title_title: 'Seitentitel',
|
||||
website_title_desc: 'Setzt den Seiten- und den Tabtitel.',
|
||||
icon_title: 'Icon',
|
||||
icon_desc: 'Eigenes Favicon nutzen. Unterstützte Dateitypen sind:',
|
||||
upsnap_version: 'UpSnap Version'
|
||||
},
|
||||
users: {
|
||||
page_title: 'Benutzer',
|
||||
allow_create_devices:
|
||||
'{username} erlauben neue Geräte zu erstellen und Gerätegruppen zu bearbeiten',
|
||||
device_permissions: 'Geräteberechtigungen',
|
||||
create_new_device: 'Neues Gerät erstellen',
|
||||
read: 'Lesen',
|
||||
update: 'Ändern',
|
||||
delete: 'Löschen',
|
||||
power: 'Power',
|
||||
toggle: 'Toggle',
|
||||
confirm_delete_title: 'Löschen bestätigen',
|
||||
confirm_delete_desc: 'Soll {username} wirklich gelöscht werden?',
|
||||
create_new_user: 'Neuen Benutzer erstellen',
|
||||
username: 'Benutzername',
|
||||
password: 'Passwort',
|
||||
password_confirm: 'Passwort bestätigen',
|
||||
required_field: 'Pflichtfeld'
|
||||
},
|
||||
buttons: {
|
||||
save: 'Speichern',
|
||||
delete: 'Löschen',
|
||||
change: 'Ändern',
|
||||
reset: 'Zurücksetzen',
|
||||
cancle: 'Abbrechen',
|
||||
add: 'Hinzufügen'
|
||||
},
|
||||
welcome: {
|
||||
step1_page_title: 'Willkommen bei UpSnap 🥳',
|
||||
step1_setup_desc:
|
||||
'Bitte schließe die nachfolgenden Schritte ab, um die Einrichtung abzuschließen.',
|
||||
step1_setup_btn_next: 'Weiter',
|
||||
step2_page_title: 'Erstelle einen Admin Account',
|
||||
step2_label_email: 'Email:',
|
||||
step2_label_password: 'Passwort:',
|
||||
step2_label_min_chars: 'min. 10 Zeichen',
|
||||
step2_label_password_confirm: 'Passwort bestätigen:',
|
||||
step2_btn_create: 'Erstellen',
|
||||
step3_page_title: 'Du bist fertig! 🎉',
|
||||
step3_page_desc: 'Füge Geräte zu deinem Dashboard hinzu.',
|
||||
step3_btn_done: 'Lets go!',
|
||||
not_expected_title: 'Ich hab dich hier nicht erwartet! 🧐',
|
||||
not_expected_desc: 'Du hast die Einrichtung bereits abgeschlossen! Hier gibts nichts zu tun.',
|
||||
not_expected_back: 'Bring mich zurück',
|
||||
progress_step1: 'Willkommen',
|
||||
progress_step2: 'Account erstellen',
|
||||
progress_step3: 'Fertig'
|
||||
},
|
||||
toasts: {
|
||||
admin_saved: 'Admin gespeichert',
|
||||
user_saved: 'Benutzer gespeichert',
|
||||
user_created: 'Benutzer {username} erstellt',
|
||||
user_deleted: 'Benutzer {username} gelöscht',
|
||||
settings_saved: 'Einstellungen gespeichert',
|
||||
password_changed: 'Passwort geändert. Bitte neu einloggen.',
|
||||
passwords_missmatch: 'Passwörter stimmen nicht überein',
|
||||
permissions_created: 'Berechtigungen für {username} erstellt',
|
||||
permissions_deleted: 'Berechtigungen für {username} gelöscht',
|
||||
permissions_updated: 'Berechtigungen für {username} geändert',
|
||||
no_permission: 'Du hast keine Berechtigung für {url}'
|
||||
}
|
||||
} satisfies Translation;
|
||||
|
||||
export default de_DE;
|
||||
214
frontend/src/lib/i18n/de/index.ts
Normal file
214
frontend/src/lib/i18n/de/index.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import en_US from '../en-US';
|
||||
import type { Translation } from '../i18n-types';
|
||||
|
||||
const de = {
|
||||
...(en_US as unknown as Translation),
|
||||
home: {
|
||||
page_title: 'Home',
|
||||
order_groups: 'Gruppen',
|
||||
order_name: 'Name',
|
||||
order_ip: 'IP',
|
||||
order_tooltip: 'Sortieren',
|
||||
message_no_devices: 'Keine Geräte.',
|
||||
message_add_first_device: 'Füge dein erstes Gerät hinzu',
|
||||
message_grant_permissions:
|
||||
'Bitte frag den Administrator, dir Berechtigungen für bestehende Geräte zu erteilen oder neue Geräte zu erstellen.'
|
||||
},
|
||||
account: {
|
||||
page_title: 'Account',
|
||||
account_type_admin: 'Admin',
|
||||
account_type_user: 'Benutzer',
|
||||
avatar_title: 'Avatar',
|
||||
language_title: 'Sprache',
|
||||
language_option_auto: 'Automatisch',
|
||||
change_password_title: 'Passwort ändern',
|
||||
change_password_body: 'Nachdem das Passwort geändert wurde, musst du dich neu einloggen.',
|
||||
change_password_label: 'Altes Passwort',
|
||||
change_password_new: 'Neues Password',
|
||||
change_password_confirm: 'Password bestätigen'
|
||||
},
|
||||
device: {
|
||||
page_title: 'Neues Gerät',
|
||||
tabs: ['Manuell', 'Netzwerkscan'],
|
||||
card_btn_more: 'Mehr',
|
||||
card_btn_more_edit: 'Bearbeiten',
|
||||
card_btn_more_sleep: 'Ruhezustand',
|
||||
card_btn_more_reboot: 'Neustart',
|
||||
card_tooltip_wake_cron: 'Einschalten Cron',
|
||||
card_tooltip_shutdown_cron: 'Ausschalten Cron',
|
||||
card_tooltip_wake_password: 'Einschalten Passwort',
|
||||
card_tooltip_last_status_change: 'Letzte Statusänderung',
|
||||
card_password: 'Passwort',
|
||||
card_nic_tooltip_pending: 'Warten',
|
||||
card_nic_tooltip_shutdown: 'Ausschalten',
|
||||
card_nic_tooltip_shutdown_no_cmd: 'Kein Ausschaltbefehl gesetzt',
|
||||
card_nic_tooltip_shutdown_no_permission: 'Keine Berechtigung zum ausschalten',
|
||||
card_nic_tooltip_power: 'Einschalten',
|
||||
card_nic_tooltip_power_no_permission: 'Keine Berechtigung zum einschalten',
|
||||
general: 'Allgemein',
|
||||
general_name: 'Name',
|
||||
general_name_placeholder: 'Gerätename',
|
||||
general_ip: 'IP',
|
||||
general_mac: 'Mac',
|
||||
general_netmask: 'Netmask',
|
||||
general_required_field: 'Pflichtfeld',
|
||||
ports: 'Ports',
|
||||
ports_desc: 'UpSnap kann auch prüfen, ob bestimmte Ports offen sind.',
|
||||
ports_add_new: 'Port hinzufügen',
|
||||
ports_name: 'Name',
|
||||
ports_number: 'Nummer',
|
||||
link: 'Link',
|
||||
link_desc:
|
||||
'Macht Ihren Gerätenamen zu einem anklickbaren Link, ideal zum Beispiel für die Verknüpfung eines Dashboards.',
|
||||
wake: 'Einschalten',
|
||||
wake_desc: 'Du kannst das Gerät mit einem Cron-Job einschalten.',
|
||||
wake_cron: 'Cron',
|
||||
wake_cron_enable: 'Aktivieren',
|
||||
sol: 'Sleep-On-LAN',
|
||||
sol_desc1:
|
||||
'Du kannst Computer mithilfe des Tools <a class="link" href="https://github.com/SR-G/sleep-on-lan" target="_blank">Sleep-On-LAN</a> in den Ruhezustand versetzen. Sleep-On-LAN (SOL) ist ein externes Tool/Daemon, das auf den PCs arbeitet, die du in den Ruhezustand versetzen möchtest, und stellt einen REST-Endpunkt bereit. Für Anweisungen zur Einrichtung von Sleep-On-LAN verweise bitte auf den Abschnitt <a href="https://github.com/SR-G/sleep-on-lan#usage" class="link" target="_blank">Usage</a>.',
|
||||
sol_desc2:
|
||||
'SOL ist so konfiguriert, dass es Anfragen über HTTP statt über UDP sendet, um eine Autorisierung zu ermöglichen und die Zuverlässigkeit der Anfragen zu erhöhen.',
|
||||
sol_desc3:
|
||||
'Daher stellen Sie bitte sicher, dass Sie <span class="badge">HTTP:<DEINPORT></span> im Abschnitt <span class="badge">Listener</span> der <a href="https://github.com/SR-G/sleep-on-lan#configuration" class="link" target="_blank">SOL-Konfiguration</a> hinzufügen.',
|
||||
sol_enable: 'Sleep-On-LAN aktivieren',
|
||||
sol_port: 'SOL Port',
|
||||
sol_authorization: 'Authorisierung',
|
||||
sol_user: 'SOL Benutzer',
|
||||
sol_password: 'SOL Passwort',
|
||||
shutdown: 'Ausschalten',
|
||||
shutdown_desc:
|
||||
'Dieser <strong>Shell-Befehl</strong> wird in deinem Container ausgeführt (wenn du Docker verwendest) oder auf deinem Host (wenn du die Binärdatei verwendest). Um zu überprüfen, ob das funktioniert, kannst du den Befehl zuerst im Container oder auf deiner Host-Shell ausführen. Übliche Befehle sind <span class="badge">net rpc</span> für Windows, <span class="badge">sshpass</span> für Linux oder <span class="badge">curl</span> im Allgemeinen, um Webanfragen durchzuführen.',
|
||||
shutdown_examples: 'Beispiele:',
|
||||
shutdown_examples_windows: 'Entfernen Windows PC ausschalten:',
|
||||
shutdown_examples_linux: 'Entfernen Linux PC ausschalten:',
|
||||
shutdown_cmd: 'Befehl zum ausschalten',
|
||||
shutdown_cron_desc:
|
||||
'Genau wie das Einrichten eines Cron-Jobs, um das Gerät aufzuwecken, kannst du auch einen Cron-Job planen, um dieses Gerät herunterzufahren.',
|
||||
shutdown_cron: 'Cron',
|
||||
shutdown_cron_enable: 'Aktivieren',
|
||||
password: 'Password',
|
||||
password_desc:
|
||||
'Einige Netzwerkkarten bieten die Möglichkeit, ein Passwort für Magic Packets zu setzen, auch <span class="badge">SecureON</span> genannt. Das Passwort kann nur 0, 4 oder 6 Zeichen lang sein.',
|
||||
groups: 'Gruppen',
|
||||
groups_desc:
|
||||
'Du kannst Geräte einer Gruppe hinzufügen, um sie auf dem Dashboard nach Gruppen sortiert anzuzeigen.',
|
||||
groups_placeholder: "z.B. 'Keller' or 'Büro'",
|
||||
network_scan_range_saved: 'Scan-Bereich gespeichert',
|
||||
network_scan_desc:
|
||||
'Automatisches Scannen deines Netzwerks nach Geräten. Damit dies funktioniert, musst du UpSnap als Administrator/root ausführen und nmap installiert und im $PATH verfügbar haben (Für Docker-Benutzer ist das bereits der Fall, und du musst nichts weiter tun). Das Scannen kann einige Sekunden dauern.',
|
||||
network_scan_ip_range: 'IP-Bereich',
|
||||
network_scan_no_range: 'Kein Scan-Bereich',
|
||||
network_scan_unsaved_changes: 'Ungespeicherte Änderungen',
|
||||
network_scan_running: 'Scan läuft',
|
||||
network_scan: 'Scan',
|
||||
network_scan_ip: 'IP:',
|
||||
network_scan_mac: 'Mac:',
|
||||
network_scan_mac_vendor: 'Mac Hersteller:',
|
||||
network_scan_netmask: 'Netmask:',
|
||||
network_scan_add_all: 'Alle Geräte hinzufügen',
|
||||
network_scan_replace_netmask: 'Netmask für alle Geräte ändern?',
|
||||
network_scan_new_netmask: 'Neue Netmask',
|
||||
network_scan_include_unknown: 'Schließe Geräte ein, bei denen der Name "Unknown" ist.'
|
||||
},
|
||||
login: {
|
||||
welcome: 'Willkommen',
|
||||
email_label: 'Email oder Nutzername:',
|
||||
password_label: 'Passwort:',
|
||||
btn_more: 'Mehr',
|
||||
menu_title_auth_providers: 'Andere Login Provider',
|
||||
btn_login: 'Login'
|
||||
},
|
||||
settings: {
|
||||
settings_title: 'Einstellungen',
|
||||
ping_interval_title: 'Ping Intervall',
|
||||
ping_interval_desc1:
|
||||
'Legt den Intervall fest, in dem Geräte gepingt werden. Leer lassen um den Standardwert von <span class="badge">@every 3s</span> zu verwenden.',
|
||||
ping_interval_desc2:
|
||||
'Lerne mehr über die korrekte Cron Syntax auf <a class="link" href="https://de.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> oder sieh dir die <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">Paketdokumentation</a> an.',
|
||||
lazy_ping_title: 'Lazy Ping',
|
||||
lazy_ping_desc:
|
||||
'Wenn Lazy Ping aktiviert ist, pingt UpSnap die Geräte nur an, wenn ein aktiver Benutzer die Website besucht. Wenn es ausgeschaltet ist, pingt UpSnap die Geräte immer an.',
|
||||
lazy_ping_enable: 'Aktivieren',
|
||||
website_title_title: 'Seitentitel',
|
||||
website_title_desc: 'Setzt den Seiten- und den Tabtitel.',
|
||||
icon_title: 'Icon',
|
||||
icon_desc: 'Eigenes Favicon nutzen. Unterstützte Dateitypen sind:',
|
||||
upsnap_version: 'UpSnap Version'
|
||||
},
|
||||
users: {
|
||||
page_title: 'Benutzer',
|
||||
allow_create_devices:
|
||||
'{username} erlauben neue Geräte zu erstellen und Gerätegruppen zu bearbeiten',
|
||||
device_permissions: 'Geräteberechtigungen',
|
||||
create_new_device: 'Neues Gerät erstellen',
|
||||
read: 'Lesen',
|
||||
update: 'Ändern',
|
||||
delete: 'Löschen',
|
||||
power: 'Power',
|
||||
toggle: 'Toggle',
|
||||
confirm_delete_title: 'Löschen bestätigen',
|
||||
confirm_delete_desc: 'Soll {username} wirklich gelöscht werden?',
|
||||
create_new_user: 'Neuen Benutzer erstellen',
|
||||
username: 'Benutzername',
|
||||
password: 'Passwort',
|
||||
password_confirm: 'Passwort bestätigen',
|
||||
required_field: 'Pflichtfeld'
|
||||
},
|
||||
buttons: {
|
||||
save: 'Speichern',
|
||||
delete: 'Löschen',
|
||||
change: 'Ändern',
|
||||
reset: 'Zurücksetzen',
|
||||
cancle: 'Abbrechen',
|
||||
add: 'Hinzufügen'
|
||||
},
|
||||
welcome: {
|
||||
step1_page_title: 'Willkommen bei UpSnap 🥳',
|
||||
step1_setup_desc:
|
||||
'Bitte schließe die nachfolgenden Schritte ab, um die Einrichtung abzuschließen.',
|
||||
step1_setup_btn_next: 'Weiter',
|
||||
step2_page_title: 'Erstelle einen Admin Account',
|
||||
step2_label_email: 'Email:',
|
||||
step2_label_password: 'Passwort:',
|
||||
step2_label_min_chars: 'min. 10 Zeichen',
|
||||
step2_label_password_confirm: 'Passwort bestätigen:',
|
||||
step2_btn_create: 'Erstellen',
|
||||
step3_page_title: 'Du bist fertig! 🎉',
|
||||
step3_page_desc: 'Füge Geräte zu deinem Dashboard hinzu.',
|
||||
step3_btn_done: 'Lets go!',
|
||||
not_expected_title: 'Ich hab dich hier nicht erwartet! 🧐',
|
||||
not_expected_desc: 'Du hast die Einrichtung bereits abgeschlossen! Hier gibts nichts zu tun.',
|
||||
not_expected_back: 'Bring mich zurück',
|
||||
progress_step1: 'Willkommen',
|
||||
progress_step2: 'Account erstellen',
|
||||
progress_step3: 'Fertig'
|
||||
},
|
||||
toasts: {
|
||||
admin_saved: 'Admin gespeichert',
|
||||
user_saved: 'Benutzer gespeichert',
|
||||
user_created: 'Benutzer {username} erstellt',
|
||||
user_deleted: 'Benutzer {username} gelöscht',
|
||||
settings_saved: 'Einstellungen gespeichert',
|
||||
password_changed: 'Passwort geändert. Bitte neu einloggen.',
|
||||
passwords_missmatch: 'Passwörter stimmen nicht überein',
|
||||
permissions_created: 'Berechtigungen für {username} erstellt',
|
||||
permissions_deleted: 'Berechtigungen für {username} gelöscht',
|
||||
permissions_updated: 'Berechtigungen für {username} geändert',
|
||||
no_permission: 'Du hast keine Berechtigung für {url}',
|
||||
device_created: '{device} erstellt',
|
||||
devices_created_multiple: '{count|int} Geräte erstellt',
|
||||
device_updated: '{device} geändert',
|
||||
device_deleted: '{device} gelöscht',
|
||||
group_created: 'Gruppe {group} erstellt',
|
||||
group_deleted: 'Gruppe {group} gelöscht'
|
||||
},
|
||||
navbar: {
|
||||
theme: 'Theme',
|
||||
new: 'Neu',
|
||||
edit_account: 'Account bearbeiten',
|
||||
logout: 'Abmelden'
|
||||
}
|
||||
} satisfies Translation;
|
||||
|
||||
export default de;
|
||||
@@ -2,6 +2,7 @@ import type { BaseTranslation } from '../i18n-types';
|
||||
|
||||
const en_US = {
|
||||
home: {
|
||||
page_title: 'Home',
|
||||
order_groups: 'Groups',
|
||||
order_name: 'Name',
|
||||
order_ip: 'IP',
|
||||
@@ -26,7 +27,86 @@ const en_US = {
|
||||
},
|
||||
device: {
|
||||
page_title: 'New device',
|
||||
tabs: ['Manual', 'Network Scan']
|
||||
tabs: ['Manual', 'Network Scan'],
|
||||
card_btn_more: 'More',
|
||||
card_btn_more_edit: 'Edit',
|
||||
card_btn_more_sleep: 'Sleep',
|
||||
card_btn_more_reboot: 'Reboot',
|
||||
card_tooltip_wake_cron: 'Wake cron',
|
||||
card_tooltip_shutdown_cron: 'Shutdown cron',
|
||||
card_tooltip_wake_password: 'Wake password',
|
||||
card_tooltip_last_status_change: 'Last status change',
|
||||
card_password: 'Password',
|
||||
card_nic_tooltip_pending: 'Pending',
|
||||
card_nic_tooltip_shutdown: 'Shut down',
|
||||
card_nic_tooltip_shutdown_no_cmd: 'No shutdown command set',
|
||||
card_nic_tooltip_shutdown_no_permission: 'No permission to shut down this device',
|
||||
card_nic_tooltip_power: 'Power on',
|
||||
card_nic_tooltip_power_no_permission: 'No permission to power on this device',
|
||||
general: 'General',
|
||||
general_name: 'Name',
|
||||
general_name_placeholder: 'Device name',
|
||||
general_ip: 'IP',
|
||||
general_mac: 'Mac',
|
||||
general_netmask: 'Netmask',
|
||||
general_required_field: 'required field',
|
||||
ports: 'Ports',
|
||||
ports_desc: 'UpSnap can also check if given ports are open.',
|
||||
ports_add_new: 'Add new port',
|
||||
ports_name: 'Name',
|
||||
ports_number: 'Number',
|
||||
link: 'Link',
|
||||
link_desc:
|
||||
'Makes your device name a clickable link, perfect for linking a dashboard for example.',
|
||||
wake: 'Wake',
|
||||
wake_desc: 'You can power this device using a scheduled cron job.',
|
||||
wake_cron: 'Wake cron',
|
||||
wake_cron_enable: 'Enable wake cron',
|
||||
sol: 'Sleep-On-LAN',
|
||||
sol_desc1:
|
||||
'You can put computers to sleep using the <a class="link" href="https://github.com/SR-G/sleep-on-lan" target="_blank">Sleep-On-LAN</a> tool. Sleep-On-LAN (SOL) is an external tool/daemon that operates on the PCs you want to put to sleep, providing a REST endpoint. For instructions on setting up Sleep-On-LAN, please refer to the <a href="https://github.com/SR-G/sleep-on-lan#usage" class="link" target="_blank">Usage</a> section.',
|
||||
sol_desc2:
|
||||
'SOL is configured to send requests over HTTP instead of UDP to enable authorization and make requests more reliable.',
|
||||
sol_desc3:
|
||||
'Therefore, please ensure that you include <span class="badge">HTTP:<YOURPORT></span> in the <span class="badge">Listeners</span> section of the <a href="https://github.com/SR-G/sleep-on-lan#configuration" class="link" target="_blank">SOL configuration</a>.',
|
||||
sol_enable: 'Enable Sleep-On-LAN',
|
||||
sol_port: 'SOL Port',
|
||||
sol_authorization: 'Authorization',
|
||||
sol_user: 'SOL User',
|
||||
sol_password: 'SOL Password',
|
||||
shutdown: 'Shutdown',
|
||||
shutdown_desc:
|
||||
'This <strong>shell command</strong> will run inside your container (if you use docker) or on your host (if you use the binary). To verify that works you can run the command inside the container or on your host shell first. Common commands are <span class="badge">net rpc</span> for windows, <span class="badge">sshpass</span> for linux or <span class="badge">curl</span> in general to make web requests.',
|
||||
shutdown_examples: 'Examples:',
|
||||
shutdown_examples_windows: 'Shutdown remote windows machine:',
|
||||
shutdown_examples_linux: 'Shutdown remote linux machine:',
|
||||
shutdown_cmd: 'Shutdown command',
|
||||
shutdown_cron_desc:
|
||||
'Just like setting a cron to wake the device, you can also schedule a cron job to shut down this device.',
|
||||
shutdown_cron: 'Shutdown cron',
|
||||
shutdown_cron_enable: 'Enable shutdown cron',
|
||||
password: 'Password',
|
||||
password_desc:
|
||||
'Some network cards have the option to set a password for magic packets, also called <span class="badge">SecureON</span>. Password can only be 0, 4 or 6 characters in length.',
|
||||
groups: 'Groups',
|
||||
groups_desc: 'You can add devices to a group to have them sorted by group on the dashboard.',
|
||||
groups_placeholder: "e.g. 'Basement' or 'Office'",
|
||||
network_scan_range_saved: 'Scan range saved',
|
||||
network_scan_desc:
|
||||
"Automatically scan your network for devices. For this to work, you need to run UpSnap as root/admin and have nmap installed and available in your $PATH (For docker users, thats already the case and you don't need to do anything). Scanning might take some seconds.",
|
||||
network_scan_ip_range: 'IP range',
|
||||
network_scan_no_range: 'No scan range',
|
||||
network_scan_unsaved_changes: 'Unsaved changes',
|
||||
network_scan_running: 'Scan running',
|
||||
network_scan: 'Scan',
|
||||
network_scan_ip: 'IP:',
|
||||
network_scan_mac: 'Mac:',
|
||||
network_scan_mac_vendor: 'Mac vendor:',
|
||||
network_scan_netmask: 'Netmask:',
|
||||
network_scan_add_all: 'Add all devices',
|
||||
network_scan_replace_netmask: 'Replace netmask for all devices?',
|
||||
network_scan_new_netmask: 'New netmask',
|
||||
network_scan_include_unknown: 'Include devices where name is "Unknown"'
|
||||
},
|
||||
login: {
|
||||
welcome: 'Welcome',
|
||||
@@ -110,7 +190,19 @@ const en_US = {
|
||||
permissions_created: 'Permissions for {username} created',
|
||||
permissions_deleted: 'Permissions for {username} deleted',
|
||||
permissions_updated: 'Permissions for {username} updated',
|
||||
no_permission: "You don't have permission to visit {url}"
|
||||
no_permission: "You don't have permission to visit {url}",
|
||||
device_created: 'Created {device}',
|
||||
devices_created_multiple: 'Created {count|int} devices',
|
||||
device_updated: 'Updated {device}',
|
||||
device_deleted: 'Deleted {device}',
|
||||
group_created: 'Created group {group}',
|
||||
group_deleted: 'Deleted group {group}'
|
||||
},
|
||||
navbar: {
|
||||
theme: 'Theme',
|
||||
new: 'New',
|
||||
edit_account: 'Edit account',
|
||||
logout: 'Logout'
|
||||
}
|
||||
} satisfies BaseTranslation;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,27 @@
|
||||
// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
|
||||
/* eslint-disable */
|
||||
|
||||
import { initFormatters } from './formatters'
|
||||
import type { Locales, Translations } from './i18n-types'
|
||||
import { loadedFormatters, loadedLocales, locales } from './i18n-util'
|
||||
import { initFormatters } from './formatters';
|
||||
import type { Locales, Translations } from './i18n-types';
|
||||
import { loadedFormatters, loadedLocales, locales } from './i18n-util';
|
||||
|
||||
const localeTranslationLoaders = {
|
||||
'de-DE': () => import('./de-DE'),
|
||||
'en-US': () => import('./en-US'),
|
||||
}
|
||||
de: () => import('./de'),
|
||||
'en-US': () => import('./en-US')
|
||||
};
|
||||
|
||||
const updateDictionary = (locale: Locales, dictionary: Partial<Translations>): Translations =>
|
||||
loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary }
|
||||
(loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary });
|
||||
|
||||
export const importLocaleAsync = async (locale: Locales): Promise<Translations> =>
|
||||
(await localeTranslationLoaders[locale]()).default as unknown as Translations
|
||||
(await localeTranslationLoaders[locale]()).default as unknown as Translations;
|
||||
|
||||
export const loadLocaleAsync = async (locale: Locales): Promise<void> => {
|
||||
updateDictionary(locale, await importLocaleAsync(locale))
|
||||
loadFormatters(locale)
|
||||
}
|
||||
updateDictionary(locale, await importLocaleAsync(locale));
|
||||
loadFormatters(locale);
|
||||
};
|
||||
|
||||
export const loadAllLocalesAsync = (): Promise<void[]> => Promise.all(locales.map(loadLocaleAsync))
|
||||
export const loadAllLocalesAsync = (): Promise<void[]> => Promise.all(locales.map(loadLocaleAsync));
|
||||
|
||||
export const loadFormatters = (locale: Locales): void =>
|
||||
void (loadedFormatters[locale] = initFormatters(locale))
|
||||
void (loadedFormatters[locale] = initFormatters(locale));
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
|
||||
/* eslint-disable */
|
||||
|
||||
import { initFormatters } from './formatters'
|
||||
import type { Locales, Translations } from './i18n-types'
|
||||
import { loadedFormatters, loadedLocales, locales } from './i18n-util'
|
||||
import { initFormatters } from './formatters';
|
||||
import type { Locales, Translations } from './i18n-types';
|
||||
import { loadedFormatters, loadedLocales, locales } from './i18n-util';
|
||||
|
||||
import de_DE from './de-DE'
|
||||
import en_US from './en-US'
|
||||
import de from './de';
|
||||
import en_US from './en-US';
|
||||
|
||||
const localeTranslations = {
|
||||
'de-DE': de_DE,
|
||||
'en-US': en_US,
|
||||
}
|
||||
de,
|
||||
'en-US': en_US
|
||||
};
|
||||
|
||||
export const loadLocale = (locale: Locales): void => {
|
||||
if (loadedLocales[locale]) return
|
||||
if (loadedLocales[locale]) return;
|
||||
|
||||
loadedLocales[locale] = localeTranslations[locale] as unknown as Translations
|
||||
loadFormatters(locale)
|
||||
}
|
||||
loadedLocales[locale] = localeTranslations[locale] as unknown as Translations;
|
||||
loadFormatters(locale);
|
||||
};
|
||||
|
||||
export const loadAllLocales = (): void => locales.forEach(loadLocale)
|
||||
export const loadAllLocales = (): void => locales.forEach(loadLocale);
|
||||
|
||||
export const loadFormatters = (locale: Locales): void =>
|
||||
void (loadedFormatters[locale] = initFormatters(locale))
|
||||
void (loadedFormatters[locale] = initFormatters(locale));
|
||||
|
||||
@@ -1,38 +1,44 @@
|
||||
// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
|
||||
/* eslint-disable */
|
||||
|
||||
import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n'
|
||||
import type { LocaleDetector } from 'typesafe-i18n/detectors'
|
||||
import type { LocaleTranslationFunctions, TranslateByString } from 'typesafe-i18n'
|
||||
import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors'
|
||||
import { initExtendDictionary } from 'typesafe-i18n/utils'
|
||||
import type { Formatters, Locales, Translations, TranslationFunctions } from './i18n-types'
|
||||
import {
|
||||
i18n as initI18n,
|
||||
i18nObject as initI18nObject,
|
||||
i18nString as initI18nString
|
||||
} from 'typesafe-i18n';
|
||||
import type { LocaleDetector } from 'typesafe-i18n/detectors';
|
||||
import type { LocaleTranslationFunctions, TranslateByString } from 'typesafe-i18n';
|
||||
import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors';
|
||||
import { initExtendDictionary } from 'typesafe-i18n/utils';
|
||||
import type { Formatters, Locales, Translations, TranslationFunctions } from './i18n-types';
|
||||
|
||||
export const baseLocale: Locales = 'en-US'
|
||||
export const baseLocale: Locales = 'en-US';
|
||||
|
||||
export const locales: Locales[] = [
|
||||
'de-DE',
|
||||
'en-US'
|
||||
]
|
||||
export const locales: Locales[] = ['de', 'en-US'];
|
||||
|
||||
export const isLocale = (locale: string): locale is Locales => locales.includes(locale as Locales)
|
||||
export const isLocale = (locale: string): locale is Locales => locales.includes(locale as Locales);
|
||||
|
||||
export const loadedLocales: Record<Locales, Translations> = {} as Record<Locales, Translations>
|
||||
export const loadedLocales: Record<Locales, Translations> = {} as Record<Locales, Translations>;
|
||||
|
||||
export const loadedFormatters: Record<Locales, Formatters> = {} as Record<Locales, Formatters>
|
||||
export const loadedFormatters: Record<Locales, Formatters> = {} as Record<Locales, Formatters>;
|
||||
|
||||
export const extendDictionary = initExtendDictionary<Translations>()
|
||||
export const extendDictionary = initExtendDictionary<Translations>();
|
||||
|
||||
export const i18nString = (locale: Locales): TranslateByString => initI18nString<Locales, Formatters>(locale, loadedFormatters[locale])
|
||||
export const i18nString = (locale: Locales): TranslateByString =>
|
||||
initI18nString<Locales, Formatters>(locale, loadedFormatters[locale]);
|
||||
|
||||
export const i18nObject = (locale: Locales): TranslationFunctions =>
|
||||
initI18nObject<Locales, Translations, TranslationFunctions, Formatters>(
|
||||
locale,
|
||||
loadedLocales[locale],
|
||||
loadedFormatters[locale]
|
||||
)
|
||||
);
|
||||
|
||||
export const i18n = (): LocaleTranslationFunctions<Locales, Translations, TranslationFunctions> =>
|
||||
initI18n<Locales, Translations, TranslationFunctions, Formatters>(loadedLocales, loadedFormatters)
|
||||
initI18n<Locales, Translations, TranslationFunctions, Formatters>(
|
||||
loadedLocales,
|
||||
loadedFormatters
|
||||
);
|
||||
|
||||
export const detectLocale = (...detectors: LocaleDetector[]): Locales => detectLocaleFn<Locales>(baseLocale, locales, ...detectors)
|
||||
export const detectLocale = (...detectors: LocaleDetector[]): Locales =>
|
||||
detectLocaleFn<Locales>(baseLocale, locales, ...detectors);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
// locales
|
||||
const languageEmojis = {
|
||||
'en-US': '🇺🇸',
|
||||
'de-DE': '🇩🇪'
|
||||
de: '🇩🇪'
|
||||
};
|
||||
let localStorageLang: Locales | 'auto' = 'auto';
|
||||
let selectedLanguage: Locales | 'auto' = localStorageLang;
|
||||
|
||||
Reference in New Issue
Block a user