mirror of
https://github.com/LogicLabs-OU/OpenArchiver.git
synced 2026-04-06 00:31:57 +02:00
feat: Add internationalization (i18n) support to frontend
This commit introduces internationalization (i18n) to the frontend using the `sveltekit-i18n` library, allowing the user interface to be translated into multiple languages. Key changes: - Added translation files for 10 languages (en, de, es, fr, etc.). - Replaced hardcoded text strings throughout the frontend components and pages with translation keys. - Added a language selector to the system settings page, allowing administrators to set the default application language. - Updated the backend settings API to store and expose the new language configuration.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { Request, Response } from 'express';
|
||||
import { SettingsService } from '../../services/SettingsService';
|
||||
import { SystemSettings } from '@open-archiver/types';
|
||||
|
||||
const settingsService = new SettingsService();
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ export const createSettingsRouter = (authService: AuthService): Router => {
|
||||
const router = Router();
|
||||
|
||||
// Public route to get non-sensitive settings. settings read should not be scoped with a permission because all end users need the settings data in the frontend. However, for sensitive settings data, we need to add a new permission subject to limit access. So this route should only expose non-sensitive settings data.
|
||||
/**
|
||||
* @returns SystemSettings
|
||||
*/
|
||||
router.get('/', settingsController.getSettings);
|
||||
|
||||
// Protected route to update settings
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"lucide-svelte": "^0.525.0",
|
||||
"postal-mime": "^2.4.4",
|
||||
"svelte-persisted-store": "^0.12.0",
|
||||
"sveltekit-i18n": "^2.4.2",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwind-variants": "^1.0.0"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import PostalMime, { type Email } from 'postal-mime';
|
||||
import type { Buffer } from 'buffer';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let {
|
||||
raw,
|
||||
@@ -51,13 +52,16 @@
|
||||
|
||||
<div class="mt-2 rounded-md border bg-white p-4">
|
||||
{#if isLoading}
|
||||
<p>Loading email preview...</p>
|
||||
<p>{$t('components.email_preview.loading')}</p>
|
||||
{:else if emailHtml}
|
||||
<iframe title="Email Preview" srcdoc={emailHtml()} class="h-[600px] w-full border-none"
|
||||
<iframe
|
||||
title={$t('archive.email_preview')}
|
||||
srcdoc={emailHtml()}
|
||||
class="h-[600px] w-full border-none"
|
||||
></iframe>
|
||||
{:else if raw}
|
||||
<p>Could not render email preview.</p>
|
||||
<p>{$t('components.email_preview.render_error')}</p>
|
||||
{:else}
|
||||
<p class="text-gray-500">Raw .eml file not available for this email.</p>
|
||||
<p class="text-gray-500">{$t('components.email_preview.not_available')}</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import type { ArchivedEmail } from '@open-archiver/types';
|
||||
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let {
|
||||
thread,
|
||||
@@ -47,16 +48,16 @@
|
||||
goto(`/dashboard/archived-emails/${item.id}`, {
|
||||
invalidateAll: true,
|
||||
});
|
||||
}}>{item.subject || 'No Subject'}</a
|
||||
}}>{item.subject || $t('archive.no_subject')}</a
|
||||
>
|
||||
{:else}
|
||||
{item.subject || 'No Subject'}
|
||||
{item.subject || $t('archive.no_subject')}
|
||||
{/if}
|
||||
</h4>
|
||||
<div
|
||||
class="flex flex-col space-y-2 text-sm font-normal leading-none text-gray-400"
|
||||
>
|
||||
<span>From: {item.senderEmail}</span>
|
||||
<span>{$t('archive.from')}: {item.senderEmail}</span>
|
||||
<time class="">{new Date(item.sentAt).toLocaleString()}</time>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
<footer class=" bg-muted py-6 md:py-0">
|
||||
<script lang="ts">
|
||||
import { t } from '$lib/translations';
|
||||
</script>
|
||||
|
||||
<footer class="bg-muted py-6 md:py-0">
|
||||
<div
|
||||
class="container mx-auto flex flex-col items-center justify-center gap-4 md:h-24 md:flex-row"
|
||||
>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<p class=" text-balance text-center text-xs font-medium leading-loose">
|
||||
<p class="text-balance text-center text-xs font-medium leading-loose">
|
||||
© {new Date().getFullYear()}
|
||||
<a href="https://openarchiver.com/" target="_blank">Open Archiver</a>. All rights
|
||||
reserved.
|
||||
<a href="https://openarchiver.com/" target="_blank">Open Archiver</a>. {$t(
|
||||
'app.components.footer.all_rights_reserved'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import { setAlert } from '$lib/components/custom/alert/alert-state.svelte';
|
||||
import { api } from '$lib/api.client';
|
||||
import { Loader2 } from 'lucide-svelte';
|
||||
import { t } from '$lib/translations';
|
||||
let {
|
||||
source = null,
|
||||
onSubmit,
|
||||
@@ -20,11 +21,20 @@
|
||||
} = $props();
|
||||
|
||||
const providerOptions = [
|
||||
{ value: 'generic_imap', label: 'Generic IMAP' },
|
||||
{ value: 'google_workspace', label: 'Google Workspace' },
|
||||
{ value: 'microsoft_365', label: 'Microsoft 365' },
|
||||
{ value: 'pst_import', label: 'PST Import' },
|
||||
{ value: 'eml_import', label: 'EML Import' },
|
||||
{
|
||||
value: 'generic_imap',
|
||||
label: $t('components.ingestion_source_form.provider_generic_imap'),
|
||||
},
|
||||
{
|
||||
value: 'google_workspace',
|
||||
label: $t('components.ingestion_source_form.provider_google_workspace'),
|
||||
},
|
||||
{
|
||||
value: 'microsoft_365',
|
||||
label: $t('components.ingestion_source_form.provider_microsoft_365'),
|
||||
},
|
||||
{ value: 'pst_import', label: $t('components.ingestion_source_form.provider_pst_import') },
|
||||
{ value: 'eml_import', label: $t('components.ingestion_source_form.provider_eml_import') },
|
||||
];
|
||||
|
||||
let formData: CreateIngestionSourceDto = $state({
|
||||
@@ -42,7 +52,8 @@
|
||||
});
|
||||
|
||||
const triggerContent = $derived(
|
||||
providerOptions.find((p) => p.value === formData.provider)?.label ?? 'Select a provider'
|
||||
providerOptions.find((p) => p.value === formData.provider)?.label ??
|
||||
$t('components.ingestion_source_form.select_provider')
|
||||
);
|
||||
|
||||
let isSubmitting = $state(false);
|
||||
@@ -89,7 +100,7 @@
|
||||
fileUploading = false;
|
||||
setAlert({
|
||||
type: 'error',
|
||||
title: 'Upload Failed, please try again',
|
||||
title: $t('components.ingestion_source_form.upload_failed'),
|
||||
message: JSON.stringify(error),
|
||||
duration: 5000,
|
||||
show: true,
|
||||
@@ -100,11 +111,11 @@
|
||||
|
||||
<form onsubmit={handleSubmit} class="grid gap-4 py-4">
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="name" class="text-left">Name</Label>
|
||||
<Label for="name" class="text-left">{$t('ingestions.name')}</Label>
|
||||
<Input id="name" bind:value={formData.name} class="col-span-3" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="provider" class="text-left">Provider</Label>
|
||||
<Label for="provider" class="text-left">{$t('ingestions.provider')}</Label>
|
||||
<Select.Root name="provider" bind:value={formData.provider} type="single">
|
||||
<Select.Trigger class="col-span-3">
|
||||
{triggerContent}
|
||||
@@ -119,16 +130,20 @@
|
||||
|
||||
{#if formData.provider === 'google_workspace'}
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="serviceAccountKeyJson" class="text-left">Service Account Key (JSON)</Label>
|
||||
<Label for="serviceAccountKeyJson" class="text-left"
|
||||
>{$t('components.ingestion_source_form.service_account_key')}</Label
|
||||
>
|
||||
<Textarea
|
||||
placeholder="Paste your service account key JSON content"
|
||||
placeholder={$t('components.ingestion_source_form.service_account_key_placeholder')}
|
||||
id="serviceAccountKeyJson"
|
||||
bind:value={formData.providerConfig.serviceAccountKeyJson}
|
||||
class="col-span-3 max-h-32"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="impersonatedAdminEmail" class="text-left">Impersonated Admin Email</Label>
|
||||
<Label for="impersonatedAdminEmail" class="text-left"
|
||||
>{$t('components.ingestion_source_form.impersonated_admin_email')}</Label
|
||||
>
|
||||
<Input
|
||||
id="impersonatedAdminEmail"
|
||||
bind:value={formData.providerConfig.impersonatedAdminEmail}
|
||||
@@ -137,30 +152,38 @@
|
||||
</div>
|
||||
{:else if formData.provider === 'microsoft_365'}
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="clientId" class="text-left">Application (Client) ID</Label>
|
||||
<Label for="clientId" class="text-left"
|
||||
>{$t('components.ingestion_source_form.client_id')}</Label
|
||||
>
|
||||
<Input id="clientId" bind:value={formData.providerConfig.clientId} class="col-span-3" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="clientSecret" class="text-left">Client Secret Value</Label>
|
||||
<Label for="clientSecret" class="text-left"
|
||||
>{$t('components.ingestion_source_form.client_secret')}</Label
|
||||
>
|
||||
<Input
|
||||
id="clientSecret"
|
||||
type="password"
|
||||
placeholder="Enter the secret Value, not the Secret ID"
|
||||
placeholder={$t('components.ingestion_source_form.client_secret_placeholder')}
|
||||
bind:value={formData.providerConfig.clientSecret}
|
||||
class="col-span-3"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="tenantId" class="text-left">Directory (Tenant) ID</Label>
|
||||
<Label for="tenantId" class="text-left"
|
||||
>{$t('components.ingestion_source_form.tenant_id')}</Label
|
||||
>
|
||||
<Input id="tenantId" bind:value={formData.providerConfig.tenantId} class="col-span-3" />
|
||||
</div>
|
||||
{:else if formData.provider === 'generic_imap'}
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="host" class="text-left">Host</Label>
|
||||
<Label for="host" class="text-left">{$t('components.ingestion_source_form.host')}</Label
|
||||
>
|
||||
<Input id="host" bind:value={formData.providerConfig.host} class="col-span-3" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="port" class="text-left">Port</Label>
|
||||
<Label for="port" class="text-left">{$t('components.ingestion_source_form.port')}</Label
|
||||
>
|
||||
<Input
|
||||
id="port"
|
||||
type="number"
|
||||
@@ -169,11 +192,13 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="username" class="text-left">Username</Label>
|
||||
<Label for="username" class="text-left"
|
||||
>{$t('components.ingestion_source_form.username')}</Label
|
||||
>
|
||||
<Input id="username" bind:value={formData.providerConfig.username} class="col-span-3" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="password" class="text-left">Password</Label>
|
||||
<Label for="password" class="text-left">{$t('auth.password')}</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
@@ -182,12 +207,16 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="secure" class="text-left">Use TLS</Label>
|
||||
<Label for="secure" class="text-left"
|
||||
>{$t('components.ingestion_source_form.use_tls')}</Label
|
||||
>
|
||||
<Checkbox id="secure" bind:checked={formData.providerConfig.secure} />
|
||||
</div>
|
||||
{:else if formData.provider === 'pst_import'}
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="pst-file" class="text-left">PST File</Label>
|
||||
<Label for="pst-file" class="text-left"
|
||||
>{$t('components.ingestion_source_form.pst_file')}</Label
|
||||
>
|
||||
<div class="col-span-3 flex flex-row items-center space-x-2">
|
||||
<Input
|
||||
id="pst-file"
|
||||
@@ -203,7 +232,9 @@
|
||||
</div>
|
||||
{:else if formData.provider === 'eml_import'}
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="eml-file" class="text-left">EML File</Label>
|
||||
<Label for="eml-file" class="text-left"
|
||||
>{$t('components.ingestion_source_form.eml_file')}</Label
|
||||
>
|
||||
<div class="col-span-3 flex flex-row items-center space-x-2">
|
||||
<Input
|
||||
id="eml-file"
|
||||
@@ -220,12 +251,10 @@
|
||||
{/if}
|
||||
{#if formData.provider === 'google_workspace' || formData.provider === 'microsoft_365'}
|
||||
<Alert.Root>
|
||||
<Alert.Title>Heads up!</Alert.Title>
|
||||
<Alert.Title>{$t('components.ingestion_source_form.heads_up')}</Alert.Title>
|
||||
<Alert.Description>
|
||||
<div class="my-1">
|
||||
Please note that this is an organization-wide operation. This kind of ingestions
|
||||
will import and index <b>all</b> email inboxes in your organization. If you want
|
||||
to import only specific email inboxes, use the IMAP connector.
|
||||
{@html $t('components.ingestion_source_form.org_wide_warning')}
|
||||
</div>
|
||||
</Alert.Description>
|
||||
</Alert.Root>
|
||||
@@ -233,9 +262,9 @@
|
||||
<Dialog.Footer>
|
||||
<Button type="submit" disabled={isSubmitting || fileUploading}>
|
||||
{#if isSubmitting}
|
||||
Submitting...
|
||||
{$t('components.common.submitting')}
|
||||
{:else}
|
||||
Submit
|
||||
{$t('components.common.submit')}
|
||||
{/if}
|
||||
</Button>
|
||||
</Dialog.Footer>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Textarea } from '$lib/components/ui/textarea';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let { role, onSubmit }: { role: Role | null; onSubmit: (formData: Partial<Role>) => void } =
|
||||
$props();
|
||||
@@ -16,7 +17,7 @@
|
||||
const parsedPolicies: CaslPolicy[] = JSON.parse(policies);
|
||||
onSubmit({ name, policies: parsedPolicies });
|
||||
} catch (error) {
|
||||
alert('Invalid JSON format for policies.');
|
||||
alert($t('components.role_form.invalid_json'));
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -29,11 +30,11 @@
|
||||
class="grid gap-4 py-4"
|
||||
>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="name" class="text-right">Name</Label>
|
||||
<Label for="name" class="text-right">{$t('roles.name')}</Label>
|
||||
<Input id="name" bind:value={name} class="col-span-3" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="policies" class="text-right">Policies (JSON)</Label>
|
||||
<Label for="policies" class="text-right">{$t('components.role_form.policies_json')}</Label>
|
||||
<Textarea
|
||||
id="policies"
|
||||
bind:value={policies}
|
||||
@@ -42,6 +43,6 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<Button type="submit">Save</Button>
|
||||
<Button type="submit">{$t('components.common.save')}</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
import { Sun, Moon, Laptop } from 'lucide-svelte';
|
||||
import { t } from '$lib/translations';
|
||||
</script>
|
||||
|
||||
<DropdownMenu.Root>
|
||||
@@ -14,21 +15,21 @@
|
||||
<Moon
|
||||
class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"
|
||||
/>
|
||||
<span class="sr-only">Toggle theme</span>
|
||||
<span class="sr-only">{$t('app.components.theme_switcher.toggle_theme')}</span>
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="end">
|
||||
<DropdownMenu.Item onclick={() => ($theme = 'light')}>
|
||||
<Sun class="mr-2 h-4 w-4" />
|
||||
<span>Light</span>
|
||||
<span>{$t('app.system_settings.light')}</span>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={() => ($theme = 'dark')}>
|
||||
<Moon class="mr-2 h-4 w-4" />
|
||||
<span>Dark</span>
|
||||
<span>{$t('app.system_settings.dark')}</span>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={() => ($theme = 'system')}>
|
||||
<Laptop class="mr-2 h-4 w-4" />
|
||||
<span>System</span>
|
||||
<span>{$t('app.system_settings.system')}</span>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import * as Dialog from '$lib/components/ui/dialog';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let {
|
||||
user = null,
|
||||
@@ -25,7 +26,7 @@
|
||||
});
|
||||
|
||||
const triggerContent = $derived(
|
||||
roles.find((r) => r.id === formData.roleId)?.name ?? 'Select a role'
|
||||
roles.find((r) => r.id === formData.roleId)?.name ?? $t('components.user_form.select_role')
|
||||
);
|
||||
|
||||
let isSubmitting = $state(false);
|
||||
@@ -53,20 +54,20 @@
|
||||
|
||||
<form onsubmit={handleSubmit} class="grid gap-4 py-4">
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="first_name" class="text-left">First Name</Label>
|
||||
<Label for="first_name" class="text-left">{$t('setup.first_name')}</Label>
|
||||
<Input id="first_name" bind:value={formData.first_name} class="col-span-3" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="last_name" class="text-left">Last Name</Label>
|
||||
<Label for="last_name" class="text-left">{$t('setup.last_name')}</Label>
|
||||
<Input id="last_name" bind:value={formData.last_name} class="col-span-3" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="email" class="text-left">Email</Label>
|
||||
<Label for="email" class="text-left">{$t('users.email')}</Label>
|
||||
<Input id="email" type="email" bind:value={formData.email} class="col-span-3" />
|
||||
</div>
|
||||
{#if !user}
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="password" class="text-left">Password</Label>
|
||||
<Label for="password" class="text-left">{$t('auth.password')}</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
@@ -76,7 +77,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="role" class="text-left">Role</Label>
|
||||
<Label for="role" class="text-left">{$t('users.role')}</Label>
|
||||
<Select.Root name="role" bind:value={formData.roleId} type="single">
|
||||
<Select.Trigger class="col-span-3">
|
||||
{triggerContent}
|
||||
@@ -92,9 +93,9 @@
|
||||
<Dialog.Footer>
|
||||
<Button type="submit" disabled={isSubmitting}>
|
||||
{#if isSubmitting}
|
||||
Submitting...
|
||||
{$t('components.common.submitting')}
|
||||
{:else}
|
||||
Submit
|
||||
{$t('components.common.submit')}
|
||||
{/if}
|
||||
</Button>
|
||||
</Dialog.Footer>
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
import { AreaChart } from 'layerchart';
|
||||
import { curveMonotoneX } from 'd3-shape';
|
||||
import type { ChartConfig } from '$lib/components/ui/chart';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
export let data: { date: Date; count: number }[];
|
||||
|
||||
const chartConfig = {
|
||||
count: {
|
||||
label: 'Emails Ingested',
|
||||
label: $t('app.components.charts.emails_ingested'),
|
||||
color: 'var(--chart-1)',
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
import type { IngestionSourceStats } from '@open-archiver/types';
|
||||
import type { ChartConfig } from '$lib/components/ui/chart';
|
||||
import { formatBytes } from '$lib/utils';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
export let data: IngestionSourceStats[];
|
||||
|
||||
const chartConfig = {
|
||||
storageUsed: {
|
||||
label: 'Storage Used',
|
||||
label: $t('app.components.charts.storage_used'),
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
</script>
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
import { BarChart } from 'layerchart';
|
||||
import type { TopSender } from '@open-archiver/types';
|
||||
import type { ChartConfig } from '$lib/components/ui/chart';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
export let data: TopSender[];
|
||||
|
||||
const chartConfig = {
|
||||
count: {
|
||||
label: 'Emails',
|
||||
label: $t('app.components.charts.emails'),
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
</script>
|
||||
|
||||
245
packages/frontend/src/lib/translations/de.json
Normal file
245
packages/frontend/src/lib/translations/de.json
Normal file
@@ -0,0 +1,245 @@
|
||||
{
|
||||
"app": {
|
||||
"auth": {
|
||||
"login": "Anmelden",
|
||||
"login_tip": "Geben Sie unten Ihre E-Mail-Adresse ein, um sich bei Ihrem Konto anzumelden.",
|
||||
"email": "Email",
|
||||
"password": "Passwort"
|
||||
},
|
||||
"common": {
|
||||
"working": "Arbeiten"
|
||||
},
|
||||
"archive": {
|
||||
"title": "Archiv",
|
||||
"no_subject": "Kein Betreff",
|
||||
"from": "Von",
|
||||
"sent": "Gesendet",
|
||||
"recipients": "Empfänger",
|
||||
"to": "An",
|
||||
"meta_data": "Metadaten",
|
||||
"folder": "Ordner",
|
||||
"tags": "Tags",
|
||||
"size": "Größe",
|
||||
"email_preview": "E-Mail-Vorschau",
|
||||
"attachments": "Anhänge",
|
||||
"download": "Herunterladen",
|
||||
"actions": "Aktionen",
|
||||
"download_eml": "E-Mail herunterladen (.eml)",
|
||||
"delete_email": "E-Mail löschen",
|
||||
"email_thread": "E-Mail-Thread",
|
||||
"delete_confirmation_title": "Möchten Sie diese E-Mail wirklich löschen?",
|
||||
"delete_confirmation_description": "Diese Aktion kann nicht rückgängig gemacht werden und entfernt die E-Mail und ihre Anhänge dauerhaft.",
|
||||
"deleting": "Löschen",
|
||||
"confirm": "Bestätigen",
|
||||
"cancel": "Abbrechen",
|
||||
"not_found": "E-Mail nicht gefunden."
|
||||
},
|
||||
"ingestions": {
|
||||
"title": "Erfassungsquellen",
|
||||
"ingestion_sources": "Erfassungsquellen",
|
||||
"bulk_actions": "Massenaktionen",
|
||||
"force_sync": "Synchronisierung erzwingen",
|
||||
"delete": "Löschen",
|
||||
"create_new": "Neu erstellen",
|
||||
"name": "Name",
|
||||
"provider": "Anbieter",
|
||||
"status": "Status",
|
||||
"active": "Aktiv",
|
||||
"created_at": "Erstellt am",
|
||||
"actions": "Aktionen",
|
||||
"last_sync_message": "Letzte Synchronisierungsnachricht",
|
||||
"empty": "Leer",
|
||||
"open_menu": "Menü öffnen",
|
||||
"edit": "Bearbeiten",
|
||||
"create": "Erstellen",
|
||||
"ingestion_source": "Erfassungsquelle",
|
||||
"edit_description": "Nehmen Sie hier Änderungen an Ihrer Erfassungsquelle vor.",
|
||||
"create_description": "Fügen Sie eine neue Erfassungsquelle hinzu, um mit der Archivierung von E-Mails zu beginnen.",
|
||||
"read": "Lesen",
|
||||
"docs_here": "Dokumente hier",
|
||||
"delete_confirmation_title": "Möchten Sie diese Erfassung wirklich löschen?",
|
||||
"delete_confirmation_description": "Dadurch werden alle archivierten E-Mails, Anhänge, Indizierungen und Dateien, die mit dieser Erfassung verknüpft sind, gelöscht. Wenn Sie nur die Synchronisierung neuer E-Mails beenden möchten, können Sie stattdessen die Erfassung anhalten.",
|
||||
"deleting": "Löschen",
|
||||
"confirm": "Bestätigen",
|
||||
"cancel": "Abbrechen",
|
||||
"bulk_delete_confirmation_title": "Möchten Sie wirklich {{count}} ausgewählte Erfassungen löschen?",
|
||||
"bulk_delete_confirmation_description": "Dadurch werden alle archivierten E-Mails, Anhänge, Indizierungen und Dateien, die mit diesen Erfassungen verknüpft sind, gelöscht. Wenn Sie nur die Synchronisierung neuer E-Mails beenden möchten, können Sie stattdessen die Erfassungen anhalten."
|
||||
},
|
||||
"search": {
|
||||
"title": "Suche",
|
||||
"description": "Suchen Sie nach archivierten E-Mails.",
|
||||
"email_search": "E-Mail-Suche",
|
||||
"placeholder": "Suche nach Stichwort, Absender, Empfänger...",
|
||||
"search_button": "Suche",
|
||||
"search_options": "Suchoptionen",
|
||||
"strategy_fuzzy": "Fuzzy",
|
||||
"strategy_verbatim": "Wörtlich",
|
||||
"strategy_frequency": "Frequenz",
|
||||
"select_strategy": "Wählen Sie eine Strategie",
|
||||
"error": "Fehler",
|
||||
"found_results_in": "{{total}} Ergebnisse in {{seconds}}s gefunden",
|
||||
"found_results": "{{total}} Ergebnisse gefunden",
|
||||
"from": "Von",
|
||||
"to": "An",
|
||||
"in_email_body": "Im E-Mail-Text",
|
||||
"in_attachment": "Im Anhang: {{filename}}",
|
||||
"prev": "Zurück",
|
||||
"next": "Weiter"
|
||||
},
|
||||
"roles": {
|
||||
"title": "Rollenverwaltung",
|
||||
"role_management": "Rollenverwaltung",
|
||||
"create_new": "Neu erstellen",
|
||||
"name": "Name",
|
||||
"created_at": "Erstellt am",
|
||||
"actions": "Aktionen",
|
||||
"open_menu": "Menü öffnen",
|
||||
"view_policy": "Richtlinie anzeigen",
|
||||
"edit": "Bearbeiten",
|
||||
"delete": "Löschen",
|
||||
"no_roles_found": "Keine Rollen gefunden.",
|
||||
"role_policy": "Rollenrichtlinie",
|
||||
"viewing_policy_for_role": "Richtlinie für Rolle anzeigen: {{name}}",
|
||||
"create": "Erstellen",
|
||||
"role": "Rolle",
|
||||
"edit_description": "Nehmen Sie hier Änderungen an der Rolle vor.",
|
||||
"create_description": "Fügen Sie dem System eine neue Rolle hinzu.",
|
||||
"delete_confirmation_title": "Möchten Sie diese Rolle wirklich löschen?",
|
||||
"delete_confirmation_description": "Diese Aktion kann nicht rückgängig gemacht werden. Dadurch wird die Rolle dauerhaft gelöscht.",
|
||||
"deleting": "Löschen",
|
||||
"confirm": "Bestätigen",
|
||||
"cancel": "Abbrechen"
|
||||
},
|
||||
"system_settings": {
|
||||
"title": "Systemeinstellungen",
|
||||
"system_settings": "Systemeinstellungen",
|
||||
"description": "Globale Anwendungseinstellungen verwalten.",
|
||||
"language": "Sprache",
|
||||
"default_theme": "Standardthema",
|
||||
"light": "Hell",
|
||||
"dark": "Dunkel",
|
||||
"system": "System",
|
||||
"support_email": "Support-E-Mail",
|
||||
"saving": "Speichern",
|
||||
"save_changes": "Änderungen speichern"
|
||||
},
|
||||
"users": {
|
||||
"title": "Benutzerverwaltung",
|
||||
"user_management": "Benutzerverwaltung",
|
||||
"create_new": "Neu erstellen",
|
||||
"name": "Name",
|
||||
"email": "Email",
|
||||
"role": "Rolle",
|
||||
"created_at": "Erstellt am",
|
||||
"actions": "Aktionen",
|
||||
"open_menu": "Menü öffnen",
|
||||
"edit": "Bearbeiten",
|
||||
"delete": "Löschen",
|
||||
"no_users_found": "Keine Benutzer gefunden.",
|
||||
"create": "Erstellen",
|
||||
"user": "Benutzer",
|
||||
"edit_description": "Nehmen Sie hier Änderungen am Benutzer vor.",
|
||||
"create_description": "Fügen Sie dem System einen neuen Benutzer hinzu.",
|
||||
"delete_confirmation_title": "Möchten Sie diesen Benutzer wirklich löschen?",
|
||||
"delete_confirmation_description": "Diese Aktion kann nicht rückgängig gemacht werden. Dadurch wird der Benutzer dauerhaft gelöscht und seine Daten von unseren Servern entfernt.",
|
||||
"deleting": "Löschen",
|
||||
"confirm": "Bestätigen",
|
||||
"cancel": "Abbrechen"
|
||||
},
|
||||
"setup": {
|
||||
"title": "Einrichtung",
|
||||
"description": "Richten Sie das anfängliche Administratorkonto für Open Archiver ein.",
|
||||
"welcome": "Willkommen",
|
||||
"create_admin_account": "Erstellen Sie das erste Administratorkonto, um loszulegen.",
|
||||
"first_name": "Vorname",
|
||||
"last_name": "Nachname",
|
||||
"email": "Email",
|
||||
"password": "Passwort",
|
||||
"creating_account": "Konto wird erstellt",
|
||||
"create_account": "Konto erstellen"
|
||||
},
|
||||
"layout": {
|
||||
"dashboard": "Dashboard",
|
||||
"ingestions": "Erfassungen",
|
||||
"archived_emails": "Archivierte E-Mails",
|
||||
"search": "Suche",
|
||||
"settings": "Einstellungen",
|
||||
"system": "System",
|
||||
"users": "Benutzer",
|
||||
"roles": "Rollen",
|
||||
"logout": "Abmelden"
|
||||
},
|
||||
"components": {
|
||||
"charts": {
|
||||
"emails_ingested": "E-Mails aufgenommen",
|
||||
"storage_used": "Speicher verwendet",
|
||||
"emails": "E-Mails"
|
||||
},
|
||||
"common": {
|
||||
"submitting": "Übermittlung...",
|
||||
"submit": "Übermitteln",
|
||||
"save": "Speichern"
|
||||
},
|
||||
"email_preview": {
|
||||
"loading": "E-Mail-Vorschau wird geladen...",
|
||||
"render_error": "E-Mail-Vorschau konnte nicht gerendert werden.",
|
||||
"not_available": "Rohe .eml-Datei für diese E-Mail nicht verfügbar."
|
||||
},
|
||||
"footer": {
|
||||
"all_rights_reserved": "Alle Rechte vorbehalten."
|
||||
},
|
||||
"ingestion_source_form": {
|
||||
"provider_generic_imap": "Generisches IMAP",
|
||||
"provider_google_workspace": "Google Workspace",
|
||||
"provider_microsoft_365": "Microsoft 365",
|
||||
"provider_pst_import": "PST-Import",
|
||||
"provider_eml_import": "EML-Import",
|
||||
"select_provider": "Wählen Sie einen Anbieter",
|
||||
"service_account_key": "Dienstkontoschlüssel (JSON)",
|
||||
"service_account_key_placeholder": "Fügen Sie den JSON-Inhalt Ihres Dienstkontoschlüssels ein",
|
||||
"impersonated_admin_email": "Impersonierte Admin-E-Mail",
|
||||
"client_id": "Anwendungs-(Client-)ID",
|
||||
"client_secret": "Client-Geheimniswert",
|
||||
"client_secret_placeholder": "Geben Sie den Geheimniswert ein, nicht die Geheimnis-ID",
|
||||
"tenant_id": "Verzeichnis-(Mandanten-)ID",
|
||||
"host": "Host",
|
||||
"port": "Port",
|
||||
"username": "Benutzername",
|
||||
"use_tls": "TLS verwenden",
|
||||
"pst_file": "PST-Datei",
|
||||
"eml_file": "EML-Datei",
|
||||
"heads_up": "Achtung!",
|
||||
"org_wide_warning": "Bitte beachten Sie, dass dies ein organisationsweiter Vorgang ist. Diese Art von Erfassungen importiert und indiziert <b>alle</b> E-Mail-Postfächer in Ihrer Organisation. Wenn Sie nur bestimmte E-Mail-Postfächer importieren möchten, verwenden Sie den IMAP-Connector.",
|
||||
"upload_failed": "Hochladen fehlgeschlagen, bitte versuchen Sie es erneut"
|
||||
},
|
||||
"role_form": {
|
||||
"policies_json": "Richtlinien (JSON)",
|
||||
"invalid_json": "Ungültiges JSON-Format für Richtlinien."
|
||||
},
|
||||
"theme_switcher": {
|
||||
"toggle_theme": "Thema umschalten"
|
||||
},
|
||||
"user_form": {
|
||||
"select_role": "Wählen Sie eine Rolle aus"
|
||||
}
|
||||
},
|
||||
"dashboard_page": {
|
||||
"title": "Dashboard",
|
||||
"meta_description": "Übersicht über Ihr E-Mail-Archiv.",
|
||||
"header": "Dashboard",
|
||||
"create_ingestion": "Erfassung erstellen",
|
||||
"no_ingestion_header": "Sie haben keine Erfassungsquelle eingerichtet.",
|
||||
"no_ingestion_text": "Fügen Sie eine Erfassungsquelle hinzu, um mit der Archivierung Ihrer Posteingänge zu beginnen.",
|
||||
"total_emails_archived": "Insgesamt archivierte E-Mails",
|
||||
"total_storage_used": "Insgesamt genutzter Speicherplatz",
|
||||
"failed_ingestions": "Fehlgeschlagene Erfassungen (letzte 7 Tage)",
|
||||
"ingestion_history": "Erfassungsverlauf",
|
||||
"no_ingestion_history": "Kein Erfassungsverlauf verfügbar.",
|
||||
"storage_by_source": "Speicher nach Erfassungsquelle",
|
||||
"no_ingestion_sources": "Keine Erfassungsquellen verfügbar.",
|
||||
"indexed_insights": "Indizierte Einblicke",
|
||||
"top_10_senders": "Top 10 Absender",
|
||||
"no_indexed_insights": "Keine indizierten Einblicke verfügbar."
|
||||
}
|
||||
}
|
||||
}
|
||||
260
packages/frontend/src/lib/translations/en.json
Normal file
260
packages/frontend/src/lib/translations/en.json
Normal file
@@ -0,0 +1,260 @@
|
||||
{
|
||||
"app": {
|
||||
"auth": {
|
||||
"login": "Login",
|
||||
"login_tip": "Enter your email below to login to your account.",
|
||||
"email": "Email",
|
||||
"password": "Password"
|
||||
},
|
||||
"common": {
|
||||
"working": "Working"
|
||||
},
|
||||
"archive": {
|
||||
"title": "Archive",
|
||||
"no_subject": "No Subject",
|
||||
"from": "From",
|
||||
"sent": "Sent",
|
||||
"recipients": "Recipients",
|
||||
"to": "To",
|
||||
"meta_data": "Meta Data",
|
||||
"folder": "Folder",
|
||||
"tags": "Tags",
|
||||
"size": "Size",
|
||||
"email_preview": "Email Preview",
|
||||
"attachments": "Attachments",
|
||||
"download": "Download",
|
||||
"actions": "Actions",
|
||||
"download_eml": "Download Email (.eml)",
|
||||
"delete_email": "Delete Email",
|
||||
"email_thread": "Email Thread",
|
||||
"delete_confirmation_title": "Are you sure you want to delete this email?",
|
||||
"delete_confirmation_description": "This action cannot be undone and will permanently remove the email and its attachments.",
|
||||
"deleting": "Deleting",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel",
|
||||
"not_found": "Email not found."
|
||||
},
|
||||
"ingestions": {
|
||||
"title": "Ingestion Sources",
|
||||
"ingestion_sources": "Ingestion Sources",
|
||||
"bulk_actions": "Bulk Actions",
|
||||
"force_sync": "Force Sync",
|
||||
"delete": "Delete",
|
||||
"create_new": "Create New",
|
||||
"name": "Name",
|
||||
"provider": "Provider",
|
||||
"status": "Status",
|
||||
"active": "Active",
|
||||
"created_at": "Created At",
|
||||
"actions": "Actions",
|
||||
"last_sync_message": "Last sync message",
|
||||
"empty": "Empty",
|
||||
"open_menu": "Open menu",
|
||||
"edit": "Edit",
|
||||
"create": "Create",
|
||||
"ingestion_source": "Ingestion Source",
|
||||
"edit_description": "Make changes to your ingestion source here.",
|
||||
"create_description": "Add a new ingestion source to start archiving emails.",
|
||||
"read": "Read",
|
||||
"docs_here": "docs here",
|
||||
"delete_confirmation_title": "Are you sure you want to delete this ingestion?",
|
||||
"delete_confirmation_description": "This will delete all archived emails, attachments, indexing, and files associated with this ingestion. If you only want to stop syncing new emails, you can pause the ingestion instead.",
|
||||
"deleting": "Deleting",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel",
|
||||
"bulk_delete_confirmation_title": "Are you sure you want to delete {{count}} selected ingestions?",
|
||||
"bulk_delete_confirmation_description": "This will delete all archived emails, attachments, indexing, and files associated with these ingestions. If you only want to stop syncing new emails, you can pause the ingestions instead."
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"description": "Search for archived emails.",
|
||||
"email_search": "Email Search",
|
||||
"placeholder": "Search by keyword, sender, recipient...",
|
||||
"search_button": "Search",
|
||||
"search_options": "Search options",
|
||||
"strategy_fuzzy": "Fuzzy",
|
||||
"strategy_verbatim": "Verbatim",
|
||||
"strategy_frequency": "Frequency",
|
||||
"select_strategy": "Select a strategy",
|
||||
"error": "Error",
|
||||
"found_results_in": "Found {{total}} results in {{seconds}}s",
|
||||
"found_results": "Found {{total}} results",
|
||||
"from": "From",
|
||||
"to": "To",
|
||||
"in_email_body": "In email body",
|
||||
"in_attachment": "In attachment: {{filename}}",
|
||||
"prev": "Prev",
|
||||
"next": "Next"
|
||||
},
|
||||
"roles": {
|
||||
"title": "Role Management",
|
||||
"role_management": "Role Management",
|
||||
"create_new": "Create New",
|
||||
"name": "Name",
|
||||
"created_at": "Created At",
|
||||
"actions": "Actions",
|
||||
"open_menu": "Open menu",
|
||||
"view_policy": "View Policy",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"no_roles_found": "No roles found.",
|
||||
"role_policy": "Role Policy",
|
||||
"viewing_policy_for_role": "Viewing policy for role: {{name}}",
|
||||
"create": "Create",
|
||||
"role": "Role",
|
||||
"edit_description": "Make changes to the role here.",
|
||||
"create_description": "Add a new role to the system.",
|
||||
"delete_confirmation_title": "Are you sure you want to delete this role?",
|
||||
"delete_confirmation_description": "This action cannot be undone. This will permanently delete the role.",
|
||||
"deleting": "Deleting",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"system_settings": {
|
||||
"title": "System Settings",
|
||||
"system_settings": "System Settings",
|
||||
"description": "Manage global application settings.",
|
||||
"language": "Language",
|
||||
"default_theme": "Default theme",
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"system": "System",
|
||||
"support_email": "Support Email",
|
||||
"saving": "Saving",
|
||||
"save_changes": "Save Changes"
|
||||
},
|
||||
"users": {
|
||||
"title": "User Management",
|
||||
"user_management": "User Management",
|
||||
"create_new": "Create New",
|
||||
"name": "Name",
|
||||
"email": "Email",
|
||||
"role": "Role",
|
||||
"created_at": "Created At",
|
||||
"actions": "Actions",
|
||||
"open_menu": "Open menu",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"no_users_found": "No users found.",
|
||||
"create": "Create",
|
||||
"user": "User",
|
||||
"edit_description": "Make changes to the user here.",
|
||||
"create_description": "Add a new user to the system.",
|
||||
"delete_confirmation_title": "Are you sure you want to delete this user?",
|
||||
"delete_confirmation_description": "This action cannot be undone. This will permanently delete the user and remove their data from our servers.",
|
||||
"deleting": "Deleting",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"components": {
|
||||
"charts": {
|
||||
"emails_ingested": "Emails Ingested",
|
||||
"storage_used": "Storage Used",
|
||||
"emails": "Emails"
|
||||
},
|
||||
"common": {
|
||||
"submitting": "Submitting...",
|
||||
"submit": "Submit",
|
||||
"save": "Save"
|
||||
},
|
||||
"email_preview": {
|
||||
"loading": "Loading email preview...",
|
||||
"render_error": "Could not render email preview.",
|
||||
"not_available": "Raw .eml file not available for this email."
|
||||
},
|
||||
"footer": {
|
||||
"all_rights_reserved": "All rights reserved."
|
||||
},
|
||||
"ingestion_source_form": {
|
||||
"provider_generic_imap": "Generic IMAP",
|
||||
"provider_google_workspace": "Google Workspace",
|
||||
"provider_microsoft_365": "Microsoft 365",
|
||||
"provider_pst_import": "PST Import",
|
||||
"provider_eml_import": "EML Import",
|
||||
"select_provider": "Select a provider",
|
||||
"service_account_key": "Service Account Key (JSON)",
|
||||
"service_account_key_placeholder": "Paste your service account key JSON content",
|
||||
"impersonated_admin_email": "Impersonated Admin Email",
|
||||
"client_id": "Application (Client) ID",
|
||||
"client_secret": "Client Secret Value",
|
||||
"client_secret_placeholder": "Enter the secret Value, not the Secret ID",
|
||||
"tenant_id": "Directory (Tenant) ID",
|
||||
"host": "Host",
|
||||
"port": "Port",
|
||||
"username": "Username",
|
||||
"use_tls": "Use TLS",
|
||||
"pst_file": "PST File",
|
||||
"eml_file": "EML File",
|
||||
"heads_up": "Heads up!",
|
||||
"org_wide_warning": "Please note that this is an organization-wide operation. This kind of ingestions will import and index <b>all</b> email inboxes in your organization. If you want to import only specific email inboxes, use the IMAP connector.",
|
||||
"upload_failed": "Upload Failed, please try again"
|
||||
},
|
||||
"role_form": {
|
||||
"policies_json": "Policies (JSON)",
|
||||
"invalid_json": "Invalid JSON format for policies."
|
||||
},
|
||||
"theme_switcher": {
|
||||
"toggle_theme": "Toggle theme"
|
||||
},
|
||||
"user_form": {
|
||||
"select_role": "Select a role"
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"title": "Setup",
|
||||
"description": "Set up the initial administrator account for Open Archiver.",
|
||||
"welcome": "Welcome",
|
||||
"create_admin_account": "Create the first administrator account to get started.",
|
||||
"first_name": "First name",
|
||||
"last_name": "Last name",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"creating_account": "Creating Account",
|
||||
"create_account": "Create Account"
|
||||
},
|
||||
"layout": {
|
||||
"dashboard": "Dashboard",
|
||||
"ingestions": "Ingestions",
|
||||
"archived_emails": "Archived emails",
|
||||
"search": "Search",
|
||||
"settings": "Settings",
|
||||
"system": "System",
|
||||
"users": "Users",
|
||||
"roles": "Roles",
|
||||
"logout": "Logout"
|
||||
},
|
||||
"archived_emails_page": {
|
||||
"title": "Archived emails",
|
||||
"header": "Archived Emails",
|
||||
"select_ingestion_source": "Select an ingestion source",
|
||||
"date": "Date",
|
||||
"subject": "Subject",
|
||||
"sender": "Sender",
|
||||
"inbox": "Inbox",
|
||||
"path": "Path",
|
||||
"actions": "Actions",
|
||||
"view": "View",
|
||||
"no_emails_found": "No archived emails found.",
|
||||
"prev": "Prev",
|
||||
"next": "Next"
|
||||
},
|
||||
"dashboard_page": {
|
||||
"title": "Dashboard",
|
||||
"meta_description": "Overview of your email archive.",
|
||||
"header": "Dashboard",
|
||||
"create_ingestion": "Create an ingestion",
|
||||
"no_ingestion_header": "You don't have any ingestion source set up.",
|
||||
"no_ingestion_text": "Add an ingestion source to start archiving your inboxes.",
|
||||
"total_emails_archived": "Total Emails Archived",
|
||||
"total_storage_used": "Total Storage Used",
|
||||
"failed_ingestions": "Failed Ingestions (Last 7 Days)",
|
||||
"ingestion_history": "Ingestion History",
|
||||
"no_ingestion_history": "No ingestion history available.",
|
||||
"storage_by_source": "Storage by Ingestion Source",
|
||||
"no_ingestion_sources": "No ingestion sources available.",
|
||||
"indexed_insights": "Indexed insights",
|
||||
"top_10_senders": "Top 10 Senders",
|
||||
"no_indexed_insights": "No indexed insights available."
|
||||
}
|
||||
}
|
||||
}
|
||||
245
packages/frontend/src/lib/translations/es.json
Normal file
245
packages/frontend/src/lib/translations/es.json
Normal file
@@ -0,0 +1,245 @@
|
||||
{
|
||||
"app": {
|
||||
"auth": {
|
||||
"login": "Iniciar sesión",
|
||||
"login_tip": "Ingrese su correo electrónico a continuación para iniciar sesión en su cuenta.",
|
||||
"email": "Correo electrónico",
|
||||
"password": "Contraseña"
|
||||
},
|
||||
"common": {
|
||||
"working": "Trabajando"
|
||||
},
|
||||
"archive": {
|
||||
"title": "Archivo",
|
||||
"no_subject": "Sin asunto",
|
||||
"from": "De",
|
||||
"sent": "Enviado",
|
||||
"recipients": "Destinatarios",
|
||||
"to": "Para",
|
||||
"meta_data": "Metadatos",
|
||||
"folder": "Carpeta",
|
||||
"tags": "Etiquetas",
|
||||
"size": "Tamaño",
|
||||
"email_preview": "Vista previa del correo electrónico",
|
||||
"attachments": "Archivos adjuntos",
|
||||
"download": "Descargar",
|
||||
"actions": "Acciones",
|
||||
"download_eml": "Descargar correo electrónico (.eml)",
|
||||
"delete_email": "Eliminar correo electrónico",
|
||||
"email_thread": "Hilo de correo electrónico",
|
||||
"delete_confirmation_title": "¿Está seguro de que desea eliminar este correo electrónico?",
|
||||
"delete_confirmation_description": "Esta acción no se puede deshacer y eliminará permanentemente el correo electrónico y sus archivos adjuntos.",
|
||||
"deleting": "Eliminando",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar",
|
||||
"not_found": "Correo electrónico no encontrado."
|
||||
},
|
||||
"ingestions": {
|
||||
"title": "Fuentes de ingesta",
|
||||
"ingestion_sources": "Fuentes de ingesta",
|
||||
"bulk_actions": "Acciones masivas",
|
||||
"force_sync": "Forzar sincronización",
|
||||
"delete": "Eliminar",
|
||||
"create_new": "Crear nuevo",
|
||||
"name": "Nombre",
|
||||
"provider": "Proveedor",
|
||||
"status": "Estado",
|
||||
"active": "Activo",
|
||||
"created_at": "Creado el",
|
||||
"actions": "Acciones",
|
||||
"last_sync_message": "Último mensaje de sincronización",
|
||||
"empty": "Vacío",
|
||||
"open_menu": "Abrir menú",
|
||||
"edit": "Editar",
|
||||
"create": "Crear",
|
||||
"ingestion_source": "Fuente de ingesta",
|
||||
"edit_description": "Realice cambios en su fuente de ingesta aquí.",
|
||||
"create_description": "Agregue una nueva fuente de ingesta para comenzar a archivar correos electrónicos.",
|
||||
"read": "Leer",
|
||||
"docs_here": "documentos aquí",
|
||||
"delete_confirmation_title": "¿Está seguro de que desea eliminar esta ingesta?",
|
||||
"delete_confirmation_description": "Esto eliminará todos los correos electrónicos archivados, archivos adjuntos, indexación y archivos asociados con esta ingesta. Si solo desea dejar de sincronizar nuevos correos electrónicos, puede pausar la ingesta en su lugar.",
|
||||
"deleting": "Eliminando",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar",
|
||||
"bulk_delete_confirmation_title": "¿Está seguro de que desea eliminar {{count}} ingestas seleccionadas?",
|
||||
"bulk_delete_confirmation_description": "Esto eliminará todos los correos electrónicos archivados, archivos adjuntos, indexación y archivos asociados con estas ingestas. Si solo desea dejar de sincronizar nuevos correos electrónicos, puede pausar las ingestas en su lugar."
|
||||
},
|
||||
"search": {
|
||||
"title": "Buscar",
|
||||
"description": "Buscar correos electrónicos archivados.",
|
||||
"email_search": "Búsqueda de correo electrónico",
|
||||
"placeholder": "Buscar por palabra clave, remitente, destinatario...",
|
||||
"search_button": "Buscar",
|
||||
"search_options": "Opciones de búsqueda",
|
||||
"strategy_fuzzy": "Difuso",
|
||||
"strategy_verbatim": "Literal",
|
||||
"strategy_frequency": "Frecuencia",
|
||||
"select_strategy": "Seleccione una estrategia",
|
||||
"error": "Error",
|
||||
"found_results_in": "Se encontraron {{total}} resultados en {{seconds}}s",
|
||||
"found_results": "Se encontraron {{total}} resultados",
|
||||
"from": "De",
|
||||
"to": "Para",
|
||||
"in_email_body": "En el cuerpo del correo electrónico",
|
||||
"in_attachment": "En el archivo adjunto: {{filename}}",
|
||||
"prev": "Anterior",
|
||||
"next": "Siguiente"
|
||||
},
|
||||
"roles": {
|
||||
"title": "Gestión de roles",
|
||||
"role_management": "Gestión de roles",
|
||||
"create_new": "Crear nuevo",
|
||||
"name": "Nombre",
|
||||
"created_at": "Creado el",
|
||||
"actions": "Acciones",
|
||||
"open_menu": "Abrir menú",
|
||||
"view_policy": "Ver política",
|
||||
"edit": "Editar",
|
||||
"delete": "Eliminar",
|
||||
"no_roles_found": "No se encontraron roles.",
|
||||
"role_policy": "Política de roles",
|
||||
"viewing_policy_for_role": "Viendo la política para el rol: {{name}}",
|
||||
"create": "Crear",
|
||||
"role": "Rol",
|
||||
"edit_description": "Realice cambios en el rol aquí.",
|
||||
"create_description": "Agregue un nuevo rol al sistema.",
|
||||
"delete_confirmation_title": "¿Está seguro de que desea eliminar este rol?",
|
||||
"delete_confirmation_description": "Esta acción no se puede deshacer. Esto eliminará permanentemente el rol.",
|
||||
"deleting": "Eliminando",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar"
|
||||
},
|
||||
"system_settings": {
|
||||
"title": "Configuración del sistema",
|
||||
"system_settings": "Configuración del sistema",
|
||||
"description": "Administrar la configuración global de la aplicación.",
|
||||
"language": "Idioma",
|
||||
"default_theme": "Tema predeterminado",
|
||||
"light": "Claro",
|
||||
"dark": "Oscuro",
|
||||
"system": "Sistema",
|
||||
"support_email": "Correo electrónico de soporte",
|
||||
"saving": "Guardando",
|
||||
"save_changes": "Guardar cambios"
|
||||
},
|
||||
"users": {
|
||||
"title": "Gestión de usuarios",
|
||||
"user_management": "Gestión de usuarios",
|
||||
"create_new": "Crear nuevo",
|
||||
"name": "Nombre",
|
||||
"email": "Correo electrónico",
|
||||
"role": "Rol",
|
||||
"created_at": "Creado el",
|
||||
"actions": "Acciones",
|
||||
"open_menu": "Abrir menú",
|
||||
"edit": "Editar",
|
||||
"delete": "Eliminar",
|
||||
"no_users_found": "No se encontraron usuarios.",
|
||||
"create": "Crear",
|
||||
"user": "Usuario",
|
||||
"edit_description": "Realice cambios en el usuario aquí.",
|
||||
"create_description": "Agregue un nuevo usuario al sistema.",
|
||||
"delete_confirmation_title": "¿Está seguro de que desea eliminar este usuario?",
|
||||
"delete_confirmation_description": "Esta acción no se puede deshacer. Esto eliminará permanentemente al usuario y eliminará sus datos de nuestros servidores.",
|
||||
"deleting": "Eliminando",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar"
|
||||
},
|
||||
"setup": {
|
||||
"title": "Configuración",
|
||||
"description": "Configure la cuenta de administrador inicial para Open Archiver.",
|
||||
"welcome": "Bienvenido",
|
||||
"create_admin_account": "Cree la primera cuenta de administrador para comenzar.",
|
||||
"first_name": "Nombre",
|
||||
"last_name": "Apellido",
|
||||
"email": "Correo electrónico",
|
||||
"password": "Contraseña",
|
||||
"creating_account": "Creando cuenta",
|
||||
"create_account": "Crear cuenta"
|
||||
},
|
||||
"layout": {
|
||||
"dashboard": "Tablero",
|
||||
"ingestions": "Ingestas",
|
||||
"archived_emails": "Correos electrónicos archivados",
|
||||
"search": "Buscar",
|
||||
"settings": "Configuración",
|
||||
"system": "Sistema",
|
||||
"users": "Usuarios",
|
||||
"roles": "Roles",
|
||||
"logout": "Cerrar sesión"
|
||||
},
|
||||
"components": {
|
||||
"charts": {
|
||||
"emails_ingested": "Correos electrónicos ingeridos",
|
||||
"storage_used": "Almacenamiento utilizado",
|
||||
"emails": "Correos electrónicos"
|
||||
},
|
||||
"common": {
|
||||
"submitting": "Enviando...",
|
||||
"submit": "Enviar",
|
||||
"save": "Guardar"
|
||||
},
|
||||
"email_preview": {
|
||||
"loading": "Cargando vista previa del correo electrónico...",
|
||||
"render_error": "No se pudo renderizar la vista previa del correo electrónico.",
|
||||
"not_available": "El archivo .eml sin procesar no está disponible para este correo electrónico."
|
||||
},
|
||||
"footer": {
|
||||
"all_rights_reserved": "Todos los derechos reservados."
|
||||
},
|
||||
"ingestion_source_form": {
|
||||
"provider_generic_imap": "IMAP genérico",
|
||||
"provider_google_workspace": "Google Workspace",
|
||||
"provider_microsoft_365": "Microsoft 365",
|
||||
"provider_pst_import": "Importación de PST",
|
||||
"provider_eml_import": "Importación de EML",
|
||||
"select_provider": "Seleccione un proveedor",
|
||||
"service_account_key": "Clave de cuenta de servicio (JSON)",
|
||||
"service_account_key_placeholder": "Pegue el contenido JSON de su clave de cuenta de servicio",
|
||||
"impersonated_admin_email": "Correo electrónico de administrador suplantado",
|
||||
"client_id": "ID de aplicación (cliente)",
|
||||
"client_secret": "Valor secreto del cliente",
|
||||
"client_secret_placeholder": "Ingrese el valor secreto, no el ID secreto",
|
||||
"tenant_id": "ID de directorio (inquilino)",
|
||||
"host": "Host",
|
||||
"port": "Puerto",
|
||||
"username": "Nombre de usuario",
|
||||
"use_tls": "Usar TLS",
|
||||
"pst_file": "Archivo PST",
|
||||
"eml_file": "Archivo EML",
|
||||
"heads_up": "¡Atención!",
|
||||
"org_wide_warning": "Tenga en cuenta que esta es una operación para toda la organización. Este tipo de ingestas importará e indexará <b>todos</b> los buzones de correo electrónico de su organización. Si desea importar solo buzones de correo electrónico específicos, utilice el conector IMAP.",
|
||||
"upload_failed": "Error al cargar, por favor intente de nuevo"
|
||||
},
|
||||
"role_form": {
|
||||
"policies_json": "Políticas (JSON)",
|
||||
"invalid_json": "Formato JSON no válido para las políticas."
|
||||
},
|
||||
"theme_switcher": {
|
||||
"toggle_theme": "Cambiar tema"
|
||||
},
|
||||
"user_form": {
|
||||
"select_role": "Seleccione un rol"
|
||||
}
|
||||
},
|
||||
"dashboard_page": {
|
||||
"title": "Tablero",
|
||||
"meta_description": "Resumen de su archivo de correo electrónico.",
|
||||
"header": "Tablero",
|
||||
"create_ingestion": "Crear una ingesta",
|
||||
"no_ingestion_header": "No tiene ninguna fuente de ingesta configurada.",
|
||||
"no_ingestion_text": "Agregue una fuente de ingesta para comenzar a archivar sus bandejas de entrada.",
|
||||
"total_emails_archived": "Total de correos electrónicos archivados",
|
||||
"total_storage_used": "Almacenamiento total utilizado",
|
||||
"failed_ingestions": "Ingestas fallidas (últimos 7 días)",
|
||||
"ingestion_history": "Historial de ingesta",
|
||||
"no_ingestion_history": "No hay historial de ingesta disponible.",
|
||||
"storage_by_source": "Almacenamiento por fuente de ingesta",
|
||||
"no_ingestion_sources": "No hay fuentes de ingesta disponibles.",
|
||||
"indexed_insights": "Información indexada",
|
||||
"top_10_senders": "Los 10 principales remitentes",
|
||||
"no_indexed_insights": "No hay información indexada disponible."
|
||||
}
|
||||
}
|
||||
}
|
||||
245
packages/frontend/src/lib/translations/et.json
Normal file
245
packages/frontend/src/lib/translations/et.json
Normal file
@@ -0,0 +1,245 @@
|
||||
{
|
||||
"app": {
|
||||
"auth": {
|
||||
"login": "Logi sisse",
|
||||
"login_tip": "Oma kontole sisselogimiseks sisestage allpool oma e-posti aadress.",
|
||||
"email": "E-post",
|
||||
"password": "Parool"
|
||||
},
|
||||
"common": {
|
||||
"working": "Töötan"
|
||||
},
|
||||
"archive": {
|
||||
"title": "Arhiiv",
|
||||
"no_subject": "Teema puudub",
|
||||
"from": "Kellelt",
|
||||
"sent": "Saadetud",
|
||||
"recipients": "Saajad",
|
||||
"to": "Kellele",
|
||||
"meta_data": "Metaandmed",
|
||||
"folder": "Kaust",
|
||||
"tags": "Sildid",
|
||||
"size": "Suurus",
|
||||
"email_preview": "E-kirja eelvaade",
|
||||
"attachments": "Manused",
|
||||
"download": "Laadi alla",
|
||||
"actions": "Toimingud",
|
||||
"download_eml": "Laadi alla e-kiri (.eml)",
|
||||
"delete_email": "Kustuta e-kiri",
|
||||
"email_thread": "E-kirja lõim",
|
||||
"delete_confirmation_title": "Kas olete kindel, et soovite selle e-kirja kustutada?",
|
||||
"delete_confirmation_description": "Seda toimingut ei saa tagasi võtta ja see eemaldab e-kirja ja selle manused jäädavalt.",
|
||||
"deleting": "Kustutamine",
|
||||
"confirm": "Kinnita",
|
||||
"cancel": "Tühista",
|
||||
"not_found": "E-kirja ei leitud."
|
||||
},
|
||||
"ingestions": {
|
||||
"title": "Sissevõtuallikad",
|
||||
"ingestion_sources": "Sissevõtuallikad",
|
||||
"bulk_actions": "Hulgitoimingud",
|
||||
"force_sync": "Sunni sünkroonimine",
|
||||
"delete": "Kustuta",
|
||||
"create_new": "Loo uus",
|
||||
"name": "Nimi",
|
||||
"provider": "Pakkuja",
|
||||
"status": "Olek",
|
||||
"active": "Aktiivne",
|
||||
"created_at": "Loodud",
|
||||
"actions": "Toimingud",
|
||||
"last_sync_message": "Viimane sünkroonimissõnum",
|
||||
"empty": "Tühi",
|
||||
"open_menu": "Ava menüü",
|
||||
"edit": "Muuda",
|
||||
"create": "Loo",
|
||||
"ingestion_source": "Sissevõtuallikas",
|
||||
"edit_description": "Tehke siin oma sissevõtuallikas muudatusi.",
|
||||
"create_description": "E-kirjade arhiveerimise alustamiseks lisage uus sissevõtuallikas.",
|
||||
"read": "Loe",
|
||||
"docs_here": "dokumendid siin",
|
||||
"delete_confirmation_title": "Kas olete kindel, et soovite selle sissevõtu kustutada?",
|
||||
"delete_confirmation_description": "See kustutab kõik selle sissevõtuga seotud arhiveeritud e-kirjad, manused, indekseerimise ja failid. Kui soovite ainult uute e-kirjade sünkroonimise peatada, saate sissevõtu peatada.",
|
||||
"deleting": "Kustutamine",
|
||||
"confirm": "Kinnita",
|
||||
"cancel": "Tühista",
|
||||
"bulk_delete_confirmation_title": "Kas olete kindel, et soovite kustutada {{count}} valitud sissevõttu?",
|
||||
"bulk_delete_confirmation_description": "See kustutab kõik nende sissevõttudega seotud arhiveeritud e-kirjad, manused, indekseerimise ja failid. Kui soovite ainult uute e-kirjade sünkroonimise peatada, saate sissevõtud peatada."
|
||||
},
|
||||
"search": {
|
||||
"title": "Otsing",
|
||||
"description": "Otsige arhiveeritud e-kirju.",
|
||||
"email_search": "E-kirja otsing",
|
||||
"placeholder": "Otsige märksõna, saatja, saaja järgi...",
|
||||
"search_button": "Otsi",
|
||||
"search_options": "Otsinguvalikud",
|
||||
"strategy_fuzzy": "Hägune",
|
||||
"strategy_verbatim": "Sõnasõnaline",
|
||||
"strategy_frequency": "Sagedus",
|
||||
"select_strategy": "Valige strateegia",
|
||||
"error": "Viga",
|
||||
"found_results_in": "Leiti {{total}} tulemust {{seconds}} sekundiga",
|
||||
"found_results": "Leiti {{total}} tulemust",
|
||||
"from": "Kellelt",
|
||||
"to": "Kellele",
|
||||
"in_email_body": "E-kirja sisus",
|
||||
"in_attachment": "Manuses: {{filename}}",
|
||||
"prev": "Eelmine",
|
||||
"next": "Järgmine"
|
||||
},
|
||||
"roles": {
|
||||
"title": "Rollide haldamine",
|
||||
"role_management": "Rollide haldamine",
|
||||
"create_new": "Loo uus",
|
||||
"name": "Nimi",
|
||||
"created_at": "Loodud",
|
||||
"actions": "Toimingud",
|
||||
"open_menu": "Ava menüü",
|
||||
"view_policy": "Vaata poliitikat",
|
||||
"edit": "Muuda",
|
||||
"delete": "Kustuta",
|
||||
"no_roles_found": "Rolle ei leitud.",
|
||||
"role_policy": "Rollipoliitika",
|
||||
"viewing_policy_for_role": "Rolli poliitika vaatamine: {{name}}",
|
||||
"create": "Loo",
|
||||
"role": "Roll",
|
||||
"edit_description": "Tehke siin rollis muudatusi.",
|
||||
"create_description": "Lisage süsteemi uus roll.",
|
||||
"delete_confirmation_title": "Kas olete kindel, et soovite selle rolli kustutada?",
|
||||
"delete_confirmation_description": "Seda toimingut ei saa tagasi võtta. See kustutab rolli jäädavalt.",
|
||||
"deleting": "Kustutamine",
|
||||
"confirm": "Kinnita",
|
||||
"cancel": "Tühista"
|
||||
},
|
||||
"system_settings": {
|
||||
"title": "Süsteemi seaded",
|
||||
"system_settings": "Süsteemi seaded",
|
||||
"description": "Hallake globaalseid rakenduse seadeid.",
|
||||
"language": "Keel",
|
||||
"default_theme": "Vaiketeema",
|
||||
"light": "Hele",
|
||||
"dark": "Tume",
|
||||
"system": "Süsteem",
|
||||
"support_email": "Tugi e-post",
|
||||
"saving": "Salvestamine",
|
||||
"save_changes": "Salvesta muudatused"
|
||||
},
|
||||
"users": {
|
||||
"title": "Kasutajate haldamine",
|
||||
"user_management": "Kasutajate haldamine",
|
||||
"create_new": "Loo uus",
|
||||
"name": "Nimi",
|
||||
"email": "E-post",
|
||||
"role": "Roll",
|
||||
"created_at": "Loodud",
|
||||
"actions": "Toimingud",
|
||||
"open_menu": "Ava menüü",
|
||||
"edit": "Muuda",
|
||||
"delete": "Kustuta",
|
||||
"no_users_found": "Kasutajaid ei leitud.",
|
||||
"create": "Loo",
|
||||
"user": "Kasutaja",
|
||||
"edit_description": "Tehke siin kasutajas muudatusi.",
|
||||
"create_description": "Lisage süsteemi uus kasutaja.",
|
||||
"delete_confirmation_title": "Kas olete kindel, et soovite selle kasutaja kustutada?",
|
||||
"delete_confirmation_description": "Seda toimingut ei saa tagasi võtta. See kustutab kasutaja jäädavalt ja eemaldab tema andmed meie serveritest.",
|
||||
"deleting": "Kustutamine",
|
||||
"confirm": "Kinnita",
|
||||
"cancel": "Tühista"
|
||||
},
|
||||
"setup": {
|
||||
"title": "Seadistamine",
|
||||
"description": "Seadistage Open Archiveri esialgne administraatorikonto.",
|
||||
"welcome": "Tere tulemast",
|
||||
"create_admin_account": "Alustamiseks looge esimene administraatorikonto.",
|
||||
"first_name": "Eesnimi",
|
||||
"last_name": "Perekonnanimi",
|
||||
"email": "E-post",
|
||||
"password": "Parool",
|
||||
"creating_account": "Konto loomine",
|
||||
"create_account": "Loo konto"
|
||||
},
|
||||
"layout": {
|
||||
"dashboard": "Armatuurlaud",
|
||||
"ingestions": "Sissevõtud",
|
||||
"archived_emails": "Arhiveeritud e-kirjad",
|
||||
"search": "Otsing",
|
||||
"settings": "Seaded",
|
||||
"system": "Süsteem",
|
||||
"users": "Kasutajad",
|
||||
"roles": "Rollid",
|
||||
"logout": "Logi välja"
|
||||
},
|
||||
"components": {
|
||||
"charts": {
|
||||
"emails_ingested": "Sissevõetud e-kirjad",
|
||||
"storage_used": "Kasutatud salvestusruum",
|
||||
"emails": "E-kirjad"
|
||||
},
|
||||
"common": {
|
||||
"submitting": "Esitamine...",
|
||||
"submit": "Esita",
|
||||
"save": "Salvesta"
|
||||
},
|
||||
"email_preview": {
|
||||
"loading": "E-kirja eelvaate laadimine...",
|
||||
"render_error": "E-kirja eelvaadet ei saanud renderdada.",
|
||||
"not_available": "Selle e-kirja jaoks pole toores .eml-faili saadaval."
|
||||
},
|
||||
"footer": {
|
||||
"all_rights_reserved": "Kõik õigused kaitstud."
|
||||
},
|
||||
"ingestion_source_form": {
|
||||
"provider_generic_imap": "Üldine IMAP",
|
||||
"provider_google_workspace": "Google Workspace",
|
||||
"provider_microsoft_365": "Microsoft 365",
|
||||
"provider_pst_import": "PST import",
|
||||
"provider_eml_import": "EML import",
|
||||
"select_provider": "Valige pakkuja",
|
||||
"service_account_key": "Teenusekonto võti (JSON)",
|
||||
"service_account_key_placeholder": "Kleepige oma teenusekonto võtme JSON-sisu",
|
||||
"impersonated_admin_email": "Impersoniseeritud administraatori e-post",
|
||||
"client_id": "Rakenduse (kliendi) ID",
|
||||
"client_secret": "Kliendi salajane väärtus",
|
||||
"client_secret_placeholder": "Sisestage salajane väärtus, mitte salajane ID",
|
||||
"tenant_id": "Kataloogi (üürniku) ID",
|
||||
"host": "Host",
|
||||
"port": "Port",
|
||||
"username": "Kasutajanimi",
|
||||
"use_tls": "Kasuta TLS-i",
|
||||
"pst_file": "PST-fail",
|
||||
"eml_file": "EML-fail",
|
||||
"heads_up": "Tähelepanu!",
|
||||
"org_wide_warning": "Pange tähele, et see on kogu organisatsiooni hõlmav toiming. Seda tüüpi sissevõtud impordivad ja indekseerivad <b>kõik</b> teie organisatsiooni e-posti postkastid. Kui soovite importida ainult konkreetseid e-posti postkaste, kasutage IMAP-konnektorit.",
|
||||
"upload_failed": "Üleslaadimine ebaõnnestus, proovige uuesti"
|
||||
},
|
||||
"role_form": {
|
||||
"policies_json": "Poliitikad (JSON)",
|
||||
"invalid_json": "Poliitikate jaoks kehtetu JSON-vorming."
|
||||
},
|
||||
"theme_switcher": {
|
||||
"toggle_theme": "Vaheta teemat"
|
||||
},
|
||||
"user_form": {
|
||||
"select_role": "Valige roll"
|
||||
}
|
||||
},
|
||||
"dashboard_page": {
|
||||
"title": "Armatuurlaud",
|
||||
"meta_description": "Ülevaade teie e-posti arhiivist.",
|
||||
"header": "Armatuurlaud",
|
||||
"create_ingestion": "Loo sissevõtt",
|
||||
"no_ingestion_header": "Teil pole ühtegi sissevõtuallikat seadistatud.",
|
||||
"no_ingestion_text": "Postkastide arhiveerimise alustamiseks lisage sissevõtuallikas.",
|
||||
"total_emails_archived": "Arhiveeritud e-kirjade koguarv",
|
||||
"total_storage_used": "Kasutatud salvestusruum kokku",
|
||||
"failed_ingestions": "Ebaõnnestunud sissevõtud (viimased 7 päeva)",
|
||||
"ingestion_history": "Sissevõtuajalugu",
|
||||
"no_ingestion_history": "Sissevõtuajalugu pole saadaval.",
|
||||
"storage_by_source": "Salvestusruum sissevõtuallika järgi",
|
||||
"no_ingestion_sources": "Sissevõtuallikaid pole saadaval.",
|
||||
"indexed_insights": "Indekseeritud ülevaated",
|
||||
"top_10_senders": "Top 10 saatjat",
|
||||
"no_indexed_insights": "Indekseeritud ülevaateid pole saadaval."
|
||||
}
|
||||
}
|
||||
}
|
||||
245
packages/frontend/src/lib/translations/fr.json
Normal file
245
packages/frontend/src/lib/translations/fr.json
Normal file
@@ -0,0 +1,245 @@
|
||||
{
|
||||
"app": {
|
||||
"auth": {
|
||||
"login": "Connexion",
|
||||
"login_tip": "Entrez votre email ci-dessous pour vous connecter à votre compte.",
|
||||
"email": "Email",
|
||||
"password": "Mot de passe"
|
||||
},
|
||||
"common": {
|
||||
"working": "Travail en cours"
|
||||
},
|
||||
"archive": {
|
||||
"title": "Archive",
|
||||
"no_subject": "Pas de sujet",
|
||||
"from": "De",
|
||||
"sent": "Envoyé",
|
||||
"recipients": "Destinataires",
|
||||
"to": "À",
|
||||
"meta_data": "Métadonnées",
|
||||
"folder": "Dossier",
|
||||
"tags": "Tags",
|
||||
"size": "Taille",
|
||||
"email_preview": "Aperçu de l'email",
|
||||
"attachments": "Pièces jointes",
|
||||
"download": "Télécharger",
|
||||
"actions": "Actions",
|
||||
"download_eml": "Télécharger l'email (.eml)",
|
||||
"delete_email": "Supprimer l'email",
|
||||
"email_thread": "Fil de discussion",
|
||||
"delete_confirmation_title": "Êtes-vous sûr de vouloir supprimer cet email ?",
|
||||
"delete_confirmation_description": "Cette action est irréversible et supprimera définitivement l'email et ses pièces jointes.",
|
||||
"deleting": "Suppression en cours",
|
||||
"confirm": "Confirmer",
|
||||
"cancel": "Annuler",
|
||||
"not_found": "Email non trouvé."
|
||||
},
|
||||
"ingestions": {
|
||||
"title": "Sources d'ingestion",
|
||||
"ingestion_sources": "Sources d'ingestion",
|
||||
"bulk_actions": "Actions en masse",
|
||||
"force_sync": "Forcer la synchronisation",
|
||||
"delete": "Supprimer",
|
||||
"create_new": "Créer",
|
||||
"name": "Nom",
|
||||
"provider": "Fournisseur",
|
||||
"status": "Statut",
|
||||
"active": "Actif",
|
||||
"created_at": "Créé le",
|
||||
"actions": "Actions",
|
||||
"last_sync_message": "Dernier message de synchronisation",
|
||||
"empty": "Vide",
|
||||
"open_menu": "Ouvrir le menu",
|
||||
"edit": "Modifier",
|
||||
"create": "Créer",
|
||||
"ingestion_source": "Source d'ingestion",
|
||||
"edit_description": "Modifiez votre source d'ingestion ici.",
|
||||
"create_description": "Ajoutez une nouvelle source d'ingestion pour commencer à archiver les emails.",
|
||||
"read": "Lire",
|
||||
"docs_here": "docs ici",
|
||||
"delete_confirmation_title": "Êtes-vous sûr de vouloir supprimer cette ingestion ?",
|
||||
"delete_confirmation_description": "Cela supprimera tous les emails archivés, les pièces jointes, l'indexation et les fichiers associés à cette ingestion. Si vous souhaitez uniquement arrêter la synchronisation des nouveaux emails, vous pouvez suspendre l'ingestion à la place.",
|
||||
"deleting": "Suppression en cours",
|
||||
"confirm": "Confirmer",
|
||||
"cancel": "Annuler",
|
||||
"bulk_delete_confirmation_title": "Êtes-vous sûr de vouloir supprimer {{count}} ingestions sélectionnées ?",
|
||||
"bulk_delete_confirmation_description": "Cela supprimera tous les emails archivés, les pièces jointes, l'indexation et les fichiers associés à ces ingestions. Si vous souhaitez uniquement arrêter la synchronisation des nouveaux emails, vous pouvez suspendre les ingestions à la place."
|
||||
},
|
||||
"search": {
|
||||
"title": "Recherche",
|
||||
"description": "Rechercher des emails archivés.",
|
||||
"email_search": "Recherche d'emails",
|
||||
"placeholder": "Rechercher par mot-clé, expéditeur, destinataire...",
|
||||
"search_button": "Rechercher",
|
||||
"search_options": "Options de recherche",
|
||||
"strategy_fuzzy": "Floue",
|
||||
"strategy_verbatim": "Textuelle",
|
||||
"strategy_frequency": "Fréquence",
|
||||
"select_strategy": "Sélectionnez une stratégie",
|
||||
"error": "Erreur",
|
||||
"found_results_in": "{{total}} résultats trouvés en {{seconds}}s",
|
||||
"found_results": "{{total}} résultats trouvés",
|
||||
"from": "De",
|
||||
"to": "À",
|
||||
"in_email_body": "Dans le corps de l'email",
|
||||
"in_attachment": "Dans la pièce jointe : {{filename}}",
|
||||
"prev": "Préc",
|
||||
"next": "Suiv"
|
||||
},
|
||||
"roles": {
|
||||
"title": "Gestion des rôles",
|
||||
"role_management": "Gestion des rôles",
|
||||
"create_new": "Créer",
|
||||
"name": "Nom",
|
||||
"created_at": "Créé le",
|
||||
"actions": "Actions",
|
||||
"open_menu": "Ouvrir le menu",
|
||||
"view_policy": "Voir la politique",
|
||||
"edit": "Modifier",
|
||||
"delete": "Supprimer",
|
||||
"no_roles_found": "Aucun rôle trouvé.",
|
||||
"role_policy": "Politique de rôle",
|
||||
"viewing_policy_for_role": "Affichage de la politique pour le rôle : {{name}}",
|
||||
"create": "Créer",
|
||||
"role": "Rôle",
|
||||
"edit_description": "Modifiez le rôle ici.",
|
||||
"create_description": "Ajoutez un nouveau rôle au système.",
|
||||
"delete_confirmation_title": "Êtes-vous sûr de vouloir supprimer ce rôle ?",
|
||||
"delete_confirmation_description": "Cette action est irréversible. Cela supprimera définitivement le rôle.",
|
||||
"deleting": "Suppression en cours",
|
||||
"confirm": "Confirmer",
|
||||
"cancel": "Annuler"
|
||||
},
|
||||
"system_settings": {
|
||||
"title": "Paramètres système",
|
||||
"system_settings": "Paramètres système",
|
||||
"description": "Gérer les paramètres globaux de l'application.",
|
||||
"language": "Langue",
|
||||
"default_theme": "Thème par défaut",
|
||||
"light": "Clair",
|
||||
"dark": "Sombre",
|
||||
"system": "Système",
|
||||
"support_email": "Email de support",
|
||||
"saving": "Enregistrement",
|
||||
"save_changes": "Enregistrer les modifications"
|
||||
},
|
||||
"users": {
|
||||
"title": "Gestion des utilisateurs",
|
||||
"user_management": "Gestion des utilisateurs",
|
||||
"create_new": "Créer",
|
||||
"name": "Nom",
|
||||
"email": "Email",
|
||||
"role": "Rôle",
|
||||
"created_at": "Créé le",
|
||||
"actions": "Actions",
|
||||
"open_menu": "Ouvrir le menu",
|
||||
"edit": "Modifier",
|
||||
"delete": "Supprimer",
|
||||
"no_users_found": "Aucun utilisateur trouvé.",
|
||||
"create": "Créer",
|
||||
"user": "Utilisateur",
|
||||
"edit_description": "Modifiez l'utilisateur ici.",
|
||||
"create_description": "Ajoutez un nouvel utilisateur au système.",
|
||||
"delete_confirmation_title": "Êtes-vous sûr de vouloir supprimer cet utilisateur ?",
|
||||
"delete_confirmation_description": "Cette action est irréversible. Cela supprimera définitivement l'utilisateur et ses données de nos serveurs.",
|
||||
"deleting": "Suppression en cours",
|
||||
"confirm": "Confirmer",
|
||||
"cancel": "Annuler"
|
||||
},
|
||||
"setup": {
|
||||
"title": "Configuration",
|
||||
"description": "Configurez le compte administrateur initial pour Open Archiver.",
|
||||
"welcome": "Bienvenue",
|
||||
"create_admin_account": "Créez le premier compte administrateur pour commencer.",
|
||||
"first_name": "Prénom",
|
||||
"last_name": "Nom de famille",
|
||||
"email": "Email",
|
||||
"password": "Mot de passe",
|
||||
"creating_account": "Création du compte",
|
||||
"create_account": "Créer un compte"
|
||||
},
|
||||
"layout": {
|
||||
"dashboard": "Tableau de bord",
|
||||
"ingestions": "Ingestions",
|
||||
"archived_emails": "E-mails archivés",
|
||||
"search": "Recherche",
|
||||
"settings": "Paramètres",
|
||||
"system": "Système",
|
||||
"users": "Utilisateurs",
|
||||
"roles": "Rôles",
|
||||
"logout": "Déconnexion"
|
||||
},
|
||||
"components": {
|
||||
"charts": {
|
||||
"emails_ingested": "E-mails ingérés",
|
||||
"storage_used": "Stockage utilisé",
|
||||
"emails": "E-mails"
|
||||
},
|
||||
"common": {
|
||||
"submitting": "Soumission...",
|
||||
"submit": "Soumettre",
|
||||
"save": "Enregistrer"
|
||||
},
|
||||
"email_preview": {
|
||||
"loading": "Chargement de l'aperçu de l'email...",
|
||||
"render_error": "Impossible de rendre l'aperçu de l'email.",
|
||||
"not_available": "Le fichier .eml brut n'est pas disponible pour cet email."
|
||||
},
|
||||
"footer": {
|
||||
"all_rights_reserved": "Tous droits réservés."
|
||||
},
|
||||
"ingestion_source_form": {
|
||||
"provider_generic_imap": "IMAP générique",
|
||||
"provider_google_workspace": "Google Workspace",
|
||||
"provider_microsoft_365": "Microsoft 365",
|
||||
"provider_pst_import": "Importation PST",
|
||||
"provider_eml_import": "Importation EML",
|
||||
"select_provider": "Sélectionnez un fournisseur",
|
||||
"service_account_key": "Clé de compte de service (JSON)",
|
||||
"service_account_key_placeholder": "Collez le contenu JSON de votre clé de compte de service",
|
||||
"impersonated_admin_email": "Email de l'administrateur impersonné",
|
||||
"client_id": "ID de l'application (client)",
|
||||
"client_secret": "Valeur secrète du client",
|
||||
"client_secret_placeholder": "Entrez la valeur secrète, pas l'ID secret",
|
||||
"tenant_id": "ID du répertoire (locataire)",
|
||||
"host": "Hôte",
|
||||
"port": "Port",
|
||||
"username": "Nom d'utilisateur",
|
||||
"use_tls": "Utiliser TLS",
|
||||
"pst_file": "Fichier PST",
|
||||
"eml_file": "Fichier EML",
|
||||
"heads_up": "Attention !",
|
||||
"org_wide_warning": "Veuillez noter qu'il s'agit d'une opération à l'échelle de l'organisation. Ce type d'ingestion importera et indexera <b>toutes</b> les boîtes de réception de votre organisation. Si vous souhaitez importer uniquement des boîtes de réception spécifiques, utilisez le connecteur IMAP.",
|
||||
"upload_failed": "Échec du téléchargement, veuillez réessayer"
|
||||
},
|
||||
"role_form": {
|
||||
"policies_json": "Politiques (JSON)",
|
||||
"invalid_json": "Format JSON invalide pour les politiques."
|
||||
},
|
||||
"theme_switcher": {
|
||||
"toggle_theme": "Changer de thème"
|
||||
},
|
||||
"user_form": {
|
||||
"select_role": "Sélectionnez un rôle"
|
||||
}
|
||||
},
|
||||
"dashboard_page": {
|
||||
"title": "Tableau de bord",
|
||||
"meta_description": "Aperçu de vos archives d'e-mails.",
|
||||
"header": "Tableau de bord",
|
||||
"create_ingestion": "Créer une ingestion",
|
||||
"no_ingestion_header": "Vous n'avez aucune source d'ingestion configurée.",
|
||||
"no_ingestion_text": "Ajoutez une source d'ingestion pour commencer à archiver vos boîtes de réception.",
|
||||
"total_emails_archived": "Total des e-mails archivés",
|
||||
"total_storage_used": "Stockage total utilisé",
|
||||
"failed_ingestions": "Ingestions échouées (7 derniers jours)",
|
||||
"ingestion_history": "Historique d'ingestion",
|
||||
"no_ingestion_history": "Aucun historique d'ingestion disponible.",
|
||||
"storage_by_source": "Stockage par source d'ingestion",
|
||||
"no_ingestion_sources": "Aucune source d'ingestion disponible.",
|
||||
"indexed_insights": "Informations indexées",
|
||||
"top_10_senders": "Top 10 des expéditeurs",
|
||||
"no_indexed_insights": "Aucune information indexée disponible."
|
||||
}
|
||||
}
|
||||
}
|
||||
123
packages/frontend/src/lib/translations/index.ts
Normal file
123
packages/frontend/src/lib/translations/index.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import i18n from 'sveltekit-i18n';
|
||||
import type { Config } from 'sveltekit-i18n';
|
||||
|
||||
// Import your locales
|
||||
import en from './en.json';
|
||||
import de from './de.json';
|
||||
import es from './es.json';
|
||||
import fr from './fr.json';
|
||||
import it from './it.json';
|
||||
import pt from './pt.json';
|
||||
import nl from './nl.json';
|
||||
import ja from './ja.json';
|
||||
import et from './et.json';
|
||||
// This is your config object.
|
||||
// It defines the languages and how to load them.
|
||||
const config: Config = {
|
||||
// Define the loaders for each language
|
||||
loaders: [
|
||||
// English 🇬🇧
|
||||
{
|
||||
locale: 'en',
|
||||
key: 'app', // This key matches the top-level key in your en.json
|
||||
loader: async () => en.app, // We return the nested 'app' object
|
||||
},
|
||||
// German 🇩🇪
|
||||
{
|
||||
locale: 'de',
|
||||
key: 'app', // This key matches the top-level key in your en.json
|
||||
loader: async () => de.app, // We return the nested 'app' object
|
||||
},
|
||||
// Spanish 🇪🇸
|
||||
{
|
||||
locale: 'es',
|
||||
key: 'app',
|
||||
loader: async () => es.app,
|
||||
},
|
||||
// French 🇫🇷
|
||||
{
|
||||
locale: 'fr',
|
||||
key: 'app',
|
||||
loader: async () => fr.app,
|
||||
},
|
||||
// Italian 🇮🇹
|
||||
{
|
||||
locale: 'it',
|
||||
key: 'app',
|
||||
loader: async () => it.app,
|
||||
},
|
||||
// Portuguese 🇵🇹
|
||||
{
|
||||
locale: 'pt',
|
||||
key: 'app',
|
||||
loader: async () => pt.app,
|
||||
},
|
||||
// Dutch 🇳🇱
|
||||
{
|
||||
locale: 'nl',
|
||||
key: 'app',
|
||||
loader: async () => nl.app,
|
||||
},
|
||||
// Japanese 🇯🇵
|
||||
{
|
||||
locale: 'ja',
|
||||
key: 'app',
|
||||
loader: async () => ja.app,
|
||||
},
|
||||
// Estonian 🇪🇪
|
||||
{
|
||||
locale: 'et',
|
||||
key: 'app',
|
||||
loader: async () => et.app,
|
||||
},
|
||||
],
|
||||
fallbackLocale: 'en',
|
||||
};
|
||||
|
||||
// Create the i18n instance.
|
||||
// export const i18nInstance = new i18n(config);
|
||||
|
||||
export const { t, locale, locales, loading, loadTranslations } = new i18n(config);
|
||||
|
||||
// Export the t store for use in components
|
||||
// export const t = i18n.t;
|
||||
|
||||
|
||||
// import i18n from 'sveltekit-i18n';
|
||||
// import type { Config } from 'sveltekit-i18n';
|
||||
|
||||
// const config: Config = ({
|
||||
// loaders: [
|
||||
// {
|
||||
// locale: 'en',
|
||||
// key: 'app',
|
||||
// loader: async () => (
|
||||
// await import('./en/app.json')
|
||||
// ).default,
|
||||
// },
|
||||
// {
|
||||
// locale: 'en',
|
||||
// key: 'marketing',
|
||||
// loader: async () => (
|
||||
// await import('./en/marketing.json')
|
||||
// ).default,
|
||||
// },
|
||||
// {
|
||||
// locale: 'fr',
|
||||
// key: 'app',
|
||||
// loader: async () => (
|
||||
// await import('./fr/app.json')
|
||||
// ).default,
|
||||
// },
|
||||
// {
|
||||
// locale: 'fr',
|
||||
// key: 'marketing',
|
||||
// loader: async () => (
|
||||
// await import('./fr/marketing.json')
|
||||
// ).default,
|
||||
// },
|
||||
// ],
|
||||
// fallbackLocale: 'en'
|
||||
// });
|
||||
|
||||
// export const { t, locale, locales, loading, loadTranslations } = new i18n(config);
|
||||
245
packages/frontend/src/lib/translations/it.json
Normal file
245
packages/frontend/src/lib/translations/it.json
Normal file
@@ -0,0 +1,245 @@
|
||||
{
|
||||
"app": {
|
||||
"auth": {
|
||||
"login": "Accesso",
|
||||
"login_tip": "Inserisci la tua email qui sotto per accedere al tuo account.",
|
||||
"email": "Email",
|
||||
"password": "Password"
|
||||
},
|
||||
"common": {
|
||||
"working": "In lavorazione"
|
||||
},
|
||||
"archive": {
|
||||
"title": "Archivio",
|
||||
"no_subject": "Nessun oggetto",
|
||||
"from": "Da",
|
||||
"sent": "Inviato",
|
||||
"recipients": "Destinatari",
|
||||
"to": "A",
|
||||
"meta_data": "Metadati",
|
||||
"folder": "Cartella",
|
||||
"tags": "Tag",
|
||||
"size": "Dimensione",
|
||||
"email_preview": "Anteprima email",
|
||||
"attachments": "Allegati",
|
||||
"download": "Scarica",
|
||||
"actions": "Azioni",
|
||||
"download_eml": "Scarica email (.eml)",
|
||||
"delete_email": "Elimina email",
|
||||
"email_thread": "Thread email",
|
||||
"delete_confirmation_title": "Sei sicuro di voler eliminare questa email?",
|
||||
"delete_confirmation_description": "Questa azione non può essere annullata ed eliminerà permanentemente l'email e i suoi allegati.",
|
||||
"deleting": "Eliminazione in corso",
|
||||
"confirm": "Conferma",
|
||||
"cancel": "Annulla",
|
||||
"not_found": "Email non trovata."
|
||||
},
|
||||
"ingestions": {
|
||||
"title": "Fonti di ingestione",
|
||||
"ingestion_sources": "Fonti di ingestione",
|
||||
"bulk_actions": "Azioni di massa",
|
||||
"force_sync": "Forza sincronizzazione",
|
||||
"delete": "Elimina",
|
||||
"create_new": "Crea nuovo",
|
||||
"name": "Nome",
|
||||
"provider": "Provider",
|
||||
"status": "Stato",
|
||||
"active": "Attivo",
|
||||
"created_at": "Creato il",
|
||||
"actions": "Azioni",
|
||||
"last_sync_message": "Ultimo messaggio di sincronizzazione",
|
||||
"empty": "Vuoto",
|
||||
"open_menu": "Apri menu",
|
||||
"edit": "Modifica",
|
||||
"create": "Crea",
|
||||
"ingestion_source": "Fonte di ingestione",
|
||||
"edit_description": "Apporta modifiche alla tua fonte di ingestione qui.",
|
||||
"create_description": "Aggiungi una nuova fonte di ingestione per iniziare ad archiviare le email.",
|
||||
"read": "Leggi",
|
||||
"docs_here": "documenti qui",
|
||||
"delete_confirmation_title": "Sei sicuro di voler eliminare questa ingestione?",
|
||||
"delete_confirmation_description": "Questo eliminerà tutte le email archiviate, gli allegati, l'indicizzazione e i file associati a questa ingestione. Se desideri solo interrompere la sincronizzazione di nuove email, puoi invece mettere in pausa l'ingestione.",
|
||||
"deleting": "Eliminazione in corso",
|
||||
"confirm": "Conferma",
|
||||
"cancel": "Annulla",
|
||||
"bulk_delete_confirmation_title": "Sei sicuro di voler eliminare {{count}} ingestioni selezionate?",
|
||||
"bulk_delete_confirmation_description": "Questo eliminerà tutte le email archiviate, gli allegati, l'indicizzazione e i file associati a queste ingestioni. Se desideri solo interrompere la sincronizzazione di nuove email, puoi invece mettere in pausa le ingestioni."
|
||||
},
|
||||
"search": {
|
||||
"title": "Cerca",
|
||||
"description": "Cerca email archiviate.",
|
||||
"email_search": "Ricerca email",
|
||||
"placeholder": "Cerca per parola chiave, mittente, destinatario...",
|
||||
"search_button": "Cerca",
|
||||
"search_options": "Opzioni di ricerca",
|
||||
"strategy_fuzzy": "Fuzzy",
|
||||
"strategy_verbatim": "Verbatim",
|
||||
"strategy_frequency": "Frequenza",
|
||||
"select_strategy": "Seleziona una strategia",
|
||||
"error": "Errore",
|
||||
"found_results_in": "Trovati {{total}} risultati in {{seconds}}s",
|
||||
"found_results": "Trovati {{total}} risultati",
|
||||
"from": "Da",
|
||||
"to": "A",
|
||||
"in_email_body": "Nel corpo dell'email",
|
||||
"in_attachment": "Nell'allegato: {{filename}}",
|
||||
"prev": "Prec",
|
||||
"next": "Succ"
|
||||
},
|
||||
"roles": {
|
||||
"title": "Gestione ruoli",
|
||||
"role_management": "Gestione ruoli",
|
||||
"create_new": "Crea nuovo",
|
||||
"name": "Nome",
|
||||
"created_at": "Creato il",
|
||||
"actions": "Azioni",
|
||||
"open_menu": "Apri menu",
|
||||
"view_policy": "Visualizza policy",
|
||||
"edit": "Modifica",
|
||||
"delete": "Elimina",
|
||||
"no_roles_found": "Nessun ruolo trovato.",
|
||||
"role_policy": "Policy ruolo",
|
||||
"viewing_policy_for_role": "Visualizzazione policy per il ruolo: {{name}}",
|
||||
"create": "Crea",
|
||||
"role": "Ruolo",
|
||||
"edit_description": "Apporta modifiche al ruolo qui.",
|
||||
"create_description": "Aggiungi un nuovo ruolo al sistema.",
|
||||
"delete_confirmation_title": "Sei sicuro di voler eliminare questo ruolo?",
|
||||
"delete_confirmation_description": "Questa azione non può essere annullata. Questo eliminerà permanentemente il ruolo.",
|
||||
"deleting": "Eliminazione in corso",
|
||||
"confirm": "Conferma",
|
||||
"cancel": "Annulla"
|
||||
},
|
||||
"system_settings": {
|
||||
"title": "Impostazioni di sistema",
|
||||
"system_settings": "Impostazioni di sistema",
|
||||
"description": "Gestisci le impostazioni globali dell'applicazione.",
|
||||
"language": "Lingua",
|
||||
"default_theme": "Tema predefinito",
|
||||
"light": "Chiaro",
|
||||
"dark": "Scuro",
|
||||
"system": "Sistema",
|
||||
"support_email": "Email di supporto",
|
||||
"saving": "Salvataggio",
|
||||
"save_changes": "Salva modifiche"
|
||||
},
|
||||
"users": {
|
||||
"title": "Gestione utenti",
|
||||
"user_management": "Gestione utenti",
|
||||
"create_new": "Crea nuovo",
|
||||
"name": "Nome",
|
||||
"email": "Email",
|
||||
"role": "Ruolo",
|
||||
"created_at": "Creato il",
|
||||
"actions": "Azioni",
|
||||
"open_menu": "Apri menu",
|
||||
"edit": "Modifica",
|
||||
"delete": "Elimina",
|
||||
"no_users_found": "Nessun utente trovato.",
|
||||
"create": "Crea",
|
||||
"user": "Utente",
|
||||
"edit_description": "Apporta modifiche all'utente qui.",
|
||||
"create_description": "Aggiungi un nuovo utente al sistema.",
|
||||
"delete_confirmation_title": "Sei sicuro di voler eliminare questo utente?",
|
||||
"delete_confirmation_description": "Questa azione non può essere annullata. Questo eliminerà permanentemente l'utente e rimuoverà i suoi dati dai nostri server.",
|
||||
"deleting": "Eliminazione in corso",
|
||||
"confirm": "Conferma",
|
||||
"cancel": "Annulla"
|
||||
},
|
||||
"setup": {
|
||||
"title": "Configurazione",
|
||||
"description": "Configura l'account amministratore iniziale per Open Archiver.",
|
||||
"welcome": "Benvenuto",
|
||||
"create_admin_account": "Crea il primo account amministratore per iniziare.",
|
||||
"first_name": "Nome",
|
||||
"last_name": "Cognome",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"creating_account": "Creazione account",
|
||||
"create_account": "Crea account"
|
||||
},
|
||||
"layout": {
|
||||
"dashboard": "Dashboard",
|
||||
"ingestions": "Ingestioni",
|
||||
"archived_emails": "Email archiviate",
|
||||
"search": "Cerca",
|
||||
"settings": "Impostazioni",
|
||||
"system": "Sistema",
|
||||
"users": "Utenti",
|
||||
"roles": "Ruoli",
|
||||
"logout": "Esci"
|
||||
},
|
||||
"components": {
|
||||
"charts": {
|
||||
"emails_ingested": "Email ingerite",
|
||||
"storage_used": "Spazio di archiviazione utilizzato",
|
||||
"emails": "Email"
|
||||
},
|
||||
"common": {
|
||||
"submitting": "Invio in corso...",
|
||||
"submit": "Invia",
|
||||
"save": "Salva"
|
||||
},
|
||||
"email_preview": {
|
||||
"loading": "Caricamento anteprima email...",
|
||||
"render_error": "Impossibile visualizzare l'anteprima dell'email.",
|
||||
"not_available": "File .eml non disponibile per questa email."
|
||||
},
|
||||
"footer": {
|
||||
"all_rights_reserved": "Tutti i diritti riservati."
|
||||
},
|
||||
"ingestion_source_form": {
|
||||
"provider_generic_imap": "IMAP generico",
|
||||
"provider_google_workspace": "Google Workspace",
|
||||
"provider_microsoft_365": "Microsoft 365",
|
||||
"provider_pst_import": "Importazione PST",
|
||||
"provider_eml_import": "Importazione EML",
|
||||
"select_provider": "Seleziona un provider",
|
||||
"service_account_key": "Chiave account di servizio (JSON)",
|
||||
"service_account_key_placeholder": "Incolla il contenuto JSON della tua chiave account di servizio",
|
||||
"impersonated_admin_email": "Email amministratore impersonata",
|
||||
"client_id": "ID applicazione (client)",
|
||||
"client_secret": "Valore segreto client",
|
||||
"client_secret_placeholder": "Inserisci il valore segreto, non l'ID segreto",
|
||||
"tenant_id": "ID directory (tenant)",
|
||||
"host": "Host",
|
||||
"port": "Porta",
|
||||
"username": "Nome utente",
|
||||
"use_tls": "Usa TLS",
|
||||
"pst_file": "File PST",
|
||||
"eml_file": "File EML",
|
||||
"heads_up": "Attenzione!",
|
||||
"org_wide_warning": "Si prega di notare che questa è un'operazione a livello di organizzazione. Questo tipo di ingestione importerà e indicizzerà <b>tutte</b> le caselle di posta elettronica della tua organizzazione. Se desideri importare solo caselle di posta elettronica specifiche, utilizza il connettore IMAP.",
|
||||
"upload_failed": "Caricamento non riuscito, riprova"
|
||||
},
|
||||
"role_form": {
|
||||
"policies_json": "Policy (JSON)",
|
||||
"invalid_json": "Formato JSON non valido per le policy."
|
||||
},
|
||||
"theme_switcher": {
|
||||
"toggle_theme": "Cambia tema"
|
||||
},
|
||||
"user_form": {
|
||||
"select_role": "Seleziona un ruolo"
|
||||
}
|
||||
},
|
||||
"dashboard_page": {
|
||||
"title": "Dashboard",
|
||||
"meta_description": "Panoramica del tuo archivio email.",
|
||||
"header": "Dashboard",
|
||||
"create_ingestion": "Crea un'ingestione",
|
||||
"no_ingestion_header": "Non hai alcuna fonte di ingestione configurata.",
|
||||
"no_ingestion_text": "Aggiungi una fonte di ingestione per iniziare ad archiviare le tue caselle di posta.",
|
||||
"total_emails_archived": "Email totali archiviate",
|
||||
"total_storage_used": "Spazio di archiviazione totale utilizzato",
|
||||
"failed_ingestions": "Ingestioni non riuscite (ultimi 7 giorni)",
|
||||
"ingestion_history": "Cronologia ingestioni",
|
||||
"no_ingestion_history": "Nessuna cronologia di ingestione disponibile.",
|
||||
"storage_by_source": "Archiviazione per fonte di ingestione",
|
||||
"no_ingestion_sources": "Nessuna fonte di ingestione disponibile.",
|
||||
"indexed_insights": "Approfondimenti indicizzati",
|
||||
"top_10_senders": "Top 10 mittenti",
|
||||
"no_indexed_insights": "Nessun approfondimento indicizzato disponibile."
|
||||
}
|
||||
}
|
||||
}
|
||||
245
packages/frontend/src/lib/translations/ja.json
Normal file
245
packages/frontend/src/lib/translations/ja.json
Normal file
@@ -0,0 +1,245 @@
|
||||
{
|
||||
"app": {
|
||||
"auth": {
|
||||
"login": "ログイン",
|
||||
"login_tip": "アカウントにログインするには、以下にメールアドレスを入力してください。",
|
||||
"email": "メール",
|
||||
"password": "パスワード"
|
||||
},
|
||||
"common": {
|
||||
"working": "作業中"
|
||||
},
|
||||
"archive": {
|
||||
"title": "アーカイブ",
|
||||
"no_subject": "件名なし",
|
||||
"from": "差出人",
|
||||
"sent": "送信日時",
|
||||
"recipients": "受信者",
|
||||
"to": "宛先",
|
||||
"meta_data": "メタデータ",
|
||||
"folder": "フォルダー",
|
||||
"tags": "タグ",
|
||||
"size": "サイズ",
|
||||
"email_preview": "メールプレビュー",
|
||||
"attachments": "添付ファイル",
|
||||
"download": "ダウンロード",
|
||||
"actions": "アクション",
|
||||
"download_eml": "メールをダウンロード (.eml)",
|
||||
"delete_email": "メールを削除",
|
||||
"email_thread": "メールのスレッド",
|
||||
"delete_confirmation_title": "このメールを削除してもよろしいですか?",
|
||||
"delete_confirmation_description": "この操作は元に戻せません。メールと添付ファイルが完全に削除されます。",
|
||||
"deleting": "削除中",
|
||||
"confirm": "確認",
|
||||
"cancel": "キャンセル",
|
||||
"not_found": "メールが見つかりません。"
|
||||
},
|
||||
"ingestions": {
|
||||
"title": "取り込み元",
|
||||
"ingestion_sources": "取り込み元",
|
||||
"bulk_actions": "一括操作",
|
||||
"force_sync": "強制同期",
|
||||
"delete": "削除",
|
||||
"create_new": "新規作成",
|
||||
"name": "名前",
|
||||
"provider": "プロバイダー",
|
||||
"status": "ステータス",
|
||||
"active": "アクティブ",
|
||||
"created_at": "作成日",
|
||||
"actions": "アクション",
|
||||
"last_sync_message": "最終同期メッセージ",
|
||||
"empty": "空",
|
||||
"open_menu": "メニューを開く",
|
||||
"edit": "編集",
|
||||
"create": "作成",
|
||||
"ingestion_source": "取り込み元",
|
||||
"edit_description": "ここで取り込み元を変更します。",
|
||||
"create_description": "メールのアーカイブを開始するために、新しい取り込み元を追加します。",
|
||||
"read": "読む",
|
||||
"docs_here": "ドキュメントはこちら",
|
||||
"delete_confirmation_title": "この取り込みを削除してもよろしいですか?",
|
||||
"delete_confirmation_description": "これにより、この取り込みに関連するすべてのアーカイブ済みメール、添付ファイル、インデックス、およびファイルが削除されます。新しいメールの同期を停止したいだけの場合は、代わりに取り込みを一時停止できます。",
|
||||
"deleting": "削除中",
|
||||
"confirm": "確認",
|
||||
"cancel": "キャンセル",
|
||||
"bulk_delete_confirmation_title": "選択した{{count}}件の取り込みを削除してもよろしいですか?",
|
||||
"bulk_delete_confirmation_description": "これにより、これらの取り込みに関連するすべてのアーカイブ済みメール、添付ファイル、インデックス、およびファイルが削除されます。新しいメールの同期を停止したいだけの場合は、代わりに取り込みを一時停止できます。"
|
||||
},
|
||||
"search": {
|
||||
"title": "検索",
|
||||
"description": "アーカイブされたメールを検索します。",
|
||||
"email_search": "メール検索",
|
||||
"placeholder": "キーワード、送信者、受信者で検索...",
|
||||
"search_button": "検索",
|
||||
"search_options": "検索オプション",
|
||||
"strategy_fuzzy": "あいまい",
|
||||
"strategy_verbatim": "逐語的",
|
||||
"strategy_frequency": "頻度",
|
||||
"select_strategy": "戦略を選択",
|
||||
"error": "エラー",
|
||||
"found_results_in": "{{seconds}}秒で{{total}}件の結果が見つかりました",
|
||||
"found_results": "{{total}}件の結果が見つかりました",
|
||||
"from": "差出人",
|
||||
"to": "宛先",
|
||||
"in_email_body": "メール本文内",
|
||||
"in_attachment": "添付ファイル内: {{filename}}",
|
||||
"prev": "前へ",
|
||||
"next": "次へ"
|
||||
},
|
||||
"roles": {
|
||||
"title": "ロール管理",
|
||||
"role_management": "ロール管理",
|
||||
"create_new": "新規作成",
|
||||
"name": "名前",
|
||||
"created_at": "作成日",
|
||||
"actions": "アクション",
|
||||
"open_menu": "メニューを開く",
|
||||
"view_policy": "ポリシーを表示",
|
||||
"edit": "編集",
|
||||
"delete": "削除",
|
||||
"no_roles_found": "ロールが見つかりません。",
|
||||
"role_policy": "ロールポリシー",
|
||||
"viewing_policy_for_role": "ロールのポリシーを表示中: {{name}}",
|
||||
"create": "作成",
|
||||
"role": "ロール",
|
||||
"edit_description": "ここでロールを変更します。",
|
||||
"create_description": "システムに新しいロールを追加します。",
|
||||
"delete_confirmation_title": "このロールを削除してもよろしいですか?",
|
||||
"delete_confirmation_description": "この操作は元に戻せません。これにより、ロールが完全に削除されます。",
|
||||
"deleting": "削除中",
|
||||
"confirm": "確認",
|
||||
"cancel": "キャンセル"
|
||||
},
|
||||
"system_settings": {
|
||||
"title": "システム設定",
|
||||
"system_settings": "システム設定",
|
||||
"description": "グローバルなアプリケーション設定を管理します。",
|
||||
"language": "言語",
|
||||
"default_theme": "デフォルトのテーマ",
|
||||
"light": "ライト",
|
||||
"dark": "ダーク",
|
||||
"system": "システム",
|
||||
"support_email": "サポートメール",
|
||||
"saving": "保存中",
|
||||
"save_changes": "変更を保存"
|
||||
},
|
||||
"users": {
|
||||
"title": "ユーザー管理",
|
||||
"user_management": "ユーザー管理",
|
||||
"create_new": "新規作成",
|
||||
"name": "名前",
|
||||
"email": "メール",
|
||||
"role": "ロール",
|
||||
"created_at": "作成日",
|
||||
"actions": "アクション",
|
||||
"open_menu": "メニューを開く",
|
||||
"edit": "編集",
|
||||
"delete": "削除",
|
||||
"no_users_found": "ユーザーが見つかりません。",
|
||||
"create": "作成",
|
||||
"user": "ユーザー",
|
||||
"edit_description": "ここでユーザーを変更します。",
|
||||
"create_description": "システムに新しいユーザーを追加します。",
|
||||
"delete_confirmation_title": "このユーザーを削除してもよろしいですか?",
|
||||
"delete_confirmation_description": "この操作は元に戻せません。これにより、ユーザーが完全に削除され、データがサーバーから削除されます。",
|
||||
"deleting": "削除中",
|
||||
"confirm": "確認",
|
||||
"cancel": "キャンセル"
|
||||
},
|
||||
"setup": {
|
||||
"title": "セットアップ",
|
||||
"description": "Open Archiverの初期管理者アカウントをセットアップします。",
|
||||
"welcome": "ようこそ",
|
||||
"create_admin_account": "開始するには、最初の管理者アカウントを作成してください。",
|
||||
"first_name": "名",
|
||||
"last_name": "姓",
|
||||
"email": "メール",
|
||||
"password": "パスワード",
|
||||
"creating_account": "アカウント作成中",
|
||||
"create_account": "アカウントを作成"
|
||||
},
|
||||
"layout": {
|
||||
"dashboard": "ダッシュボード",
|
||||
"ingestions": "取り込み",
|
||||
"archived_emails": "アーカイブされたメール",
|
||||
"search": "検索",
|
||||
"settings": "設定",
|
||||
"system": "システム",
|
||||
"users": "ユーザー",
|
||||
"roles": "ロール",
|
||||
"logout": "ログアウト"
|
||||
},
|
||||
"components": {
|
||||
"charts": {
|
||||
"emails_ingested": "取り込まれたメール",
|
||||
"storage_used": "使用済みストレージ",
|
||||
"emails": "メール"
|
||||
},
|
||||
"common": {
|
||||
"submitting": "送信中...",
|
||||
"submit": "送信",
|
||||
"save": "保存"
|
||||
},
|
||||
"email_preview": {
|
||||
"loading": "メールプレビューを読み込んでいます...",
|
||||
"render_error": "メールプレビューをレンダリングできませんでした。",
|
||||
"not_available": "このメールの生の.emlファイルはありません。"
|
||||
},
|
||||
"footer": {
|
||||
"all_rights_reserved": "無断複写・転載を禁じます。"
|
||||
},
|
||||
"ingestion_source_form": {
|
||||
"provider_generic_imap": "汎用IMAP",
|
||||
"provider_google_workspace": "Google Workspace",
|
||||
"provider_microsoft_365": "Microsoft 365",
|
||||
"provider_pst_import": "PSTインポート",
|
||||
"provider_eml_import": "EMLインポート",
|
||||
"select_provider": "プロバイダーを選択",
|
||||
"service_account_key": "サービスアカウントキー (JSON)",
|
||||
"service_account_key_placeholder": "サービスアカウントキーのJSONコンテンツを貼り付けます",
|
||||
"impersonated_admin_email": "偽装された管理者メール",
|
||||
"client_id": "アプリケーション (クライアント) ID",
|
||||
"client_secret": "クライアントシークレット値",
|
||||
"client_secret_placeholder": "シークレットIDではなく、シークレット値を入力してください",
|
||||
"tenant_id": "ディレクトリ (テナント) ID",
|
||||
"host": "ホスト",
|
||||
"port": "ポート",
|
||||
"username": "ユーザー名",
|
||||
"use_tls": "TLSを使用",
|
||||
"pst_file": "PSTファイル",
|
||||
"eml_file": "EMLファイル",
|
||||
"heads_up": "ご注意ください!",
|
||||
"org_wide_warning": "これは組織全体の操作であることに注意してください。この種の取り込みは、組織内の<b>すべて</b>のメールボックスをインポートしてインデックスを作成します。特定のメールボックスのみをインポートする場合は、IMAPコネクタを使用してください。",
|
||||
"upload_failed": "アップロードに失敗しました。もう一度お試しください"
|
||||
},
|
||||
"role_form": {
|
||||
"policies_json": "ポリシー (JSON)",
|
||||
"invalid_json": "ポリシーのJSON形式が無効です。"
|
||||
},
|
||||
"theme_switcher": {
|
||||
"toggle_theme": "テーマを切り替える"
|
||||
},
|
||||
"user_form": {
|
||||
"select_role": "役割を選択"
|
||||
}
|
||||
},
|
||||
"dashboard_page": {
|
||||
"title": "ダッシュボード",
|
||||
"meta_description": "メールアーカイブの概要。",
|
||||
"header": "ダッシュボード",
|
||||
"create_ingestion": "取り込みを作成",
|
||||
"no_ingestion_header": "取り込みソースが設定されていません。",
|
||||
"no_ingestion_text": "受信トレイのアーカイブを開始するには、取り込みソースを追加してください。",
|
||||
"total_emails_archived": "アーカイブされたメールの総数",
|
||||
"total_storage_used": "総ストレージ使用量",
|
||||
"failed_ingestions": "失敗した取り込み(過去7日間)",
|
||||
"ingestion_history": "取り込み履歴",
|
||||
"no_ingestion_history": "取り込み履歴はありません。",
|
||||
"storage_by_source": "取り込みソース別のストレージ",
|
||||
"no_ingestion_sources": "利用可能な取り込みソースはありません。",
|
||||
"indexed_insights": "インデックス付きインサイト",
|
||||
"top_10_senders": "トップ10送信者",
|
||||
"no_indexed_insights": "インデックス付きインサイトはありません。"
|
||||
}
|
||||
}
|
||||
}
|
||||
245
packages/frontend/src/lib/translations/nl.json
Normal file
245
packages/frontend/src/lib/translations/nl.json
Normal file
@@ -0,0 +1,245 @@
|
||||
{
|
||||
"app": {
|
||||
"auth": {
|
||||
"login": "Inloggen",
|
||||
"login_tip": "Voer hieronder uw e-mailadres in om in te loggen op uw account.",
|
||||
"email": "E-mail",
|
||||
"password": "Wachtwoord"
|
||||
},
|
||||
"common": {
|
||||
"working": "Bezig"
|
||||
},
|
||||
"archive": {
|
||||
"title": "Archief",
|
||||
"no_subject": "Geen onderwerp",
|
||||
"from": "Van",
|
||||
"sent": "Verzonden",
|
||||
"recipients": "Ontvangers",
|
||||
"to": "Aan",
|
||||
"meta_data": "Metadata",
|
||||
"folder": "Map",
|
||||
"tags": "Tags",
|
||||
"size": "Grootte",
|
||||
"email_preview": "E-mailvoorbeeld",
|
||||
"attachments": "Bijlagen",
|
||||
"download": "Downloaden",
|
||||
"actions": "Acties",
|
||||
"download_eml": "E-mail downloaden (.eml)",
|
||||
"delete_email": "E-mail verwijderen",
|
||||
"email_thread": "E-mailthread",
|
||||
"delete_confirmation_title": "Weet u zeker dat u deze e-mail wilt verwijderen?",
|
||||
"delete_confirmation_description": "Deze actie kan niet ongedaan worden gemaakt en zal de e-mail en de bijlagen permanent verwijderen.",
|
||||
"deleting": "Verwijderen",
|
||||
"confirm": "Bevestigen",
|
||||
"cancel": "Annuleren",
|
||||
"not_found": "E-mail niet gevonden."
|
||||
},
|
||||
"ingestions": {
|
||||
"title": "Innamebronnen",
|
||||
"ingestion_sources": "Innamebronnen",
|
||||
"bulk_actions": "Bulkacties",
|
||||
"force_sync": "Synchronisatie forceren",
|
||||
"delete": "Verwijderen",
|
||||
"create_new": "Nieuw aanmaken",
|
||||
"name": "Naam",
|
||||
"provider": "Provider",
|
||||
"status": "Status",
|
||||
"active": "Actief",
|
||||
"created_at": "Aangemaakt op",
|
||||
"actions": "Acties",
|
||||
"last_sync_message": "Laatste synchronisatiebericht",
|
||||
"empty": "Leeg",
|
||||
"open_menu": "Menu openen",
|
||||
"edit": "Bewerken",
|
||||
"create": "Aanmaken",
|
||||
"ingestion_source": "Innamebron",
|
||||
"edit_description": "Breng hier wijzigingen aan in uw innamebron.",
|
||||
"create_description": "Voeg een nieuwe innamebron toe om e-mails te archiveren.",
|
||||
"read": "Lezen",
|
||||
"docs_here": "documenten hier",
|
||||
"delete_confirmation_title": "Weet u zeker dat u deze inname wilt verwijderen?",
|
||||
"delete_confirmation_description": "Dit verwijdert alle gearchiveerde e-mails, bijlagen, indexering en bestanden die aan deze inname zijn gekoppeld. Als u alleen wilt stoppen met het synchroniseren van nieuwe e-mails, kunt u de inname in plaats daarvan pauzeren.",
|
||||
"deleting": "Verwijderen",
|
||||
"confirm": "Bevestigen",
|
||||
"cancel": "Annuleren",
|
||||
"bulk_delete_confirmation_title": "Weet u zeker dat u {{count}} geselecteerde innames wilt verwijderen?",
|
||||
"bulk_delete_confirmation_description": "Dit verwijdert alle gearchiveerde e-mails, bijlagen, indexering en bestanden die aan deze innames zijn gekoppeld. Als u alleen wilt stoppen met het synchroniseren van nieuwe e-mails, kunt u de innames in plaats daarvan pauzeren."
|
||||
},
|
||||
"search": {
|
||||
"title": "Zoeken",
|
||||
"description": "Zoeken naar gearchiveerde e-mails.",
|
||||
"email_search": "E-mail zoeken",
|
||||
"placeholder": "Zoeken op trefwoord, afzender, ontvanger...",
|
||||
"search_button": "Zoeken",
|
||||
"search_options": "Zoekopties",
|
||||
"strategy_fuzzy": "Fuzzy",
|
||||
"strategy_verbatim": "Letterlijk",
|
||||
"strategy_frequency": "Frequentie",
|
||||
"select_strategy": "Selecteer een strategie",
|
||||
"error": "Fout",
|
||||
"found_results_in": "{{total}} resultaten gevonden in {{seconds}}s",
|
||||
"found_results": "{{total}} resultaten gevonden",
|
||||
"from": "Van",
|
||||
"to": "Aan",
|
||||
"in_email_body": "In e-mailtekst",
|
||||
"in_attachment": "In bijlage: {{filename}}",
|
||||
"prev": "Vorige",
|
||||
"next": "Volgende"
|
||||
},
|
||||
"roles": {
|
||||
"title": "Rollenbeheer",
|
||||
"role_management": "Rollenbeheer",
|
||||
"create_new": "Nieuw aanmaken",
|
||||
"name": "Naam",
|
||||
"created_at": "Aangemaakt op",
|
||||
"actions": "Acties",
|
||||
"open_menu": "Menu openen",
|
||||
"view_policy": "Beleid bekijken",
|
||||
"edit": "Bewerken",
|
||||
"delete": "Verwijderen",
|
||||
"no_roles_found": "Geen rollen gevonden.",
|
||||
"role_policy": "Rollenbeleid",
|
||||
"viewing_policy_for_role": "Beleid bekijken voor rol: {{name}}",
|
||||
"create": "Aanmaken",
|
||||
"role": "Rol",
|
||||
"edit_description": "Breng hier wijzigingen aan in de rol.",
|
||||
"create_description": "Voeg een nieuwe rol toe aan het systeem.",
|
||||
"delete_confirmation_title": "Weet u zeker dat u deze rol wilt verwijderen?",
|
||||
"delete_confirmation_description": "Deze actie kan niet ongedaan worden gemaakt. Dit zal de rol permanent verwijderen.",
|
||||
"deleting": "Verwijderen",
|
||||
"confirm": "Bevestigen",
|
||||
"cancel": "Annuleren"
|
||||
},
|
||||
"system_settings": {
|
||||
"title": "Systeeminstellingen",
|
||||
"system_settings": "Systeeminstellingen",
|
||||
"description": "Beheer de algemene applicatie-instellingen.",
|
||||
"language": "Taal",
|
||||
"default_theme": "Standaardthema",
|
||||
"light": "Licht",
|
||||
"dark": "Donker",
|
||||
"system": "Systeem",
|
||||
"support_email": "Ondersteunings-e-mail",
|
||||
"saving": "Opslaan",
|
||||
"save_changes": "Wijzigingen opslaan"
|
||||
},
|
||||
"users": {
|
||||
"title": "Gebruikersbeheer",
|
||||
"user_management": "Gebruikersbeheer",
|
||||
"create_new": "Nieuw aanmaken",
|
||||
"name": "Naam",
|
||||
"email": "E-mail",
|
||||
"role": "Rol",
|
||||
"created_at": "Aangemaakt op",
|
||||
"actions": "Acties",
|
||||
"open_menu": "Menu openen",
|
||||
"edit": "Bewerken",
|
||||
"delete": "Verwijderen",
|
||||
"no_users_found": "Geen gebruikers gevonden.",
|
||||
"create": "Aanmaken",
|
||||
"user": "Gebruiker",
|
||||
"edit_description": "Breng hier wijzigingen aan in de gebruiker.",
|
||||
"create_description": "Voeg een nieuwe gebruiker toe aan het systeem.",
|
||||
"delete_confirmation_title": "Weet u zeker dat u deze gebruiker wilt verwijderen?",
|
||||
"delete_confirmation_description": "Deze actie kan niet ongedaan worden gemaakt. Dit zal de gebruiker permanent verwijderen en hun gegevens van onze servers verwijderen.",
|
||||
"deleting": "Verwijderen",
|
||||
"confirm": "Bevestigen",
|
||||
"cancel": "Annuleren"
|
||||
},
|
||||
"setup": {
|
||||
"title": "Installatie",
|
||||
"description": "Stel het initiële beheerdersaccount in voor Open Archiver.",
|
||||
"welcome": "Welkom",
|
||||
"create_admin_account": "Maak het eerste beheerdersaccount aan om te beginnen.",
|
||||
"first_name": "Voornaam",
|
||||
"last_name": "Achternaam",
|
||||
"email": "E-mail",
|
||||
"password": "Wachtwoord",
|
||||
"creating_account": "Account aanmaken",
|
||||
"create_account": "Account aanmaken"
|
||||
},
|
||||
"layout": {
|
||||
"dashboard": "Dashboard",
|
||||
"ingestions": "Innames",
|
||||
"archived_emails": "Gearchiveerde e-mails",
|
||||
"search": "Zoeken",
|
||||
"settings": "Instellingen",
|
||||
"system": "Systeem",
|
||||
"users": "Gebruikers",
|
||||
"roles": "Rollen",
|
||||
"logout": "Uitloggen"
|
||||
},
|
||||
"components": {
|
||||
"charts": {
|
||||
"emails_ingested": "E-mails opgenomen",
|
||||
"storage_used": "Opslag gebruikt",
|
||||
"emails": "E-mails"
|
||||
},
|
||||
"common": {
|
||||
"submitting": "Verzenden...",
|
||||
"submit": "Verzenden",
|
||||
"save": "Opslaan"
|
||||
},
|
||||
"email_preview": {
|
||||
"loading": "E-mailvoorbeeld laden...",
|
||||
"render_error": "Kan e-mailvoorbeeld niet weergeven.",
|
||||
"not_available": "Ruwe .eml-bestand niet beschikbaar voor deze e-mail."
|
||||
},
|
||||
"footer": {
|
||||
"all_rights_reserved": "Alle rechten voorbehouden."
|
||||
},
|
||||
"ingestion_source_form": {
|
||||
"provider_generic_imap": "Generieke IMAP",
|
||||
"provider_google_workspace": "Google Workspace",
|
||||
"provider_microsoft_365": "Microsoft 365",
|
||||
"provider_pst_import": "PST importeren",
|
||||
"provider_eml_import": "EML importeren",
|
||||
"select_provider": "Selecteer een provider",
|
||||
"service_account_key": "Servicesleutel (JSON)",
|
||||
"service_account_key_placeholder": "Plak de JSON-inhoud van uw servicesleutel",
|
||||
"impersonated_admin_email": "Geïmpersoneerde beheerders-e-mail",
|
||||
"client_id": "Applicatie (client) ID",
|
||||
"client_secret": "Clientgeheimwaarde",
|
||||
"client_secret_placeholder": "Voer de geheime waarde in, niet de geheime ID",
|
||||
"tenant_id": "Directory (tenant) ID",
|
||||
"host": "Host",
|
||||
"port": "Poort",
|
||||
"username": "Gebruikersnaam",
|
||||
"use_tls": "TLS gebruiken",
|
||||
"pst_file": "PST-bestand",
|
||||
"eml_file": "EML-bestand",
|
||||
"heads_up": "Let op!",
|
||||
"org_wide_warning": "Houd er rekening mee dat dit een organisatiebrede bewerking is. Dit type inname importeert en indexeert <b>alle</b> e-mailboxen in uw organisatie. Als u alleen specifieke e-mailboxen wilt importeren, gebruik dan de IMAP-connector.",
|
||||
"upload_failed": "Uploaden mislukt, probeer het opnieuw"
|
||||
},
|
||||
"role_form": {
|
||||
"policies_json": "Beleid (JSON)",
|
||||
"invalid_json": "Ongeldig JSON-formaat voor beleid."
|
||||
},
|
||||
"theme_switcher": {
|
||||
"toggle_theme": "Thema wisselen"
|
||||
},
|
||||
"user_form": {
|
||||
"select_role": "Selecteer een rol"
|
||||
}
|
||||
},
|
||||
"dashboard_page": {
|
||||
"title": "Dashboard",
|
||||
"meta_description": "Overzicht van uw e-mailarchief.",
|
||||
"header": "Dashboard",
|
||||
"create_ingestion": "Maak een inname",
|
||||
"no_ingestion_header": "U heeft geen innamebron ingesteld.",
|
||||
"no_ingestion_text": "Voeg een innamebron toe om uw inboxen te archiveren.",
|
||||
"total_emails_archived": "Totaal aantal gearchiveerde e-mails",
|
||||
"total_storage_used": "Totaal gebruikte opslag",
|
||||
"failed_ingestions": "Mislukte innames (laatste 7 dagen)",
|
||||
"ingestion_history": "Innamegeschiedenis",
|
||||
"no_ingestion_history": "Geen innamegeschiedenis beschikbaar.",
|
||||
"storage_by_source": "Opslag per innamebron",
|
||||
"no_ingestion_sources": "Geen innamebronnen beschikbaar.",
|
||||
"indexed_insights": "Geïndexeerde inzichten",
|
||||
"top_10_senders": "Top 10 afzenders",
|
||||
"no_indexed_insights": "Geen geïndexeerde inzichten beschikbaar."
|
||||
}
|
||||
}
|
||||
}
|
||||
245
packages/frontend/src/lib/translations/pt.json
Normal file
245
packages/frontend/src/lib/translations/pt.json
Normal file
@@ -0,0 +1,245 @@
|
||||
{
|
||||
"app": {
|
||||
"auth": {
|
||||
"login": "Login",
|
||||
"login_tip": "Digite seu e-mail abaixo para fazer login em sua conta.",
|
||||
"email": "E-mail",
|
||||
"password": "Senha"
|
||||
},
|
||||
"common": {
|
||||
"working": "Trabalhando"
|
||||
},
|
||||
"archive": {
|
||||
"title": "Arquivo",
|
||||
"no_subject": "Sem assunto",
|
||||
"from": "De",
|
||||
"sent": "Enviado",
|
||||
"recipients": "Destinatários",
|
||||
"to": "Para",
|
||||
"meta_data": "Metadados",
|
||||
"folder": "Pasta",
|
||||
"tags": "Tags",
|
||||
"size": "Tamanho",
|
||||
"email_preview": "Visualização de e-mail",
|
||||
"attachments": "Anexos",
|
||||
"download": "Baixar",
|
||||
"actions": "Ações",
|
||||
"download_eml": "Baixar e-mail (.eml)",
|
||||
"delete_email": "Excluir e-mail",
|
||||
"email_thread": "Thread de e-mail",
|
||||
"delete_confirmation_title": "Tem certeza de que deseja excluir este e-mail?",
|
||||
"delete_confirmation_description": "Esta ação não pode ser desfeita e removerá permanentemente o e-mail e seus anexos.",
|
||||
"deleting": "Excluindo",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar",
|
||||
"not_found": "E-mail não encontrado."
|
||||
},
|
||||
"ingestions": {
|
||||
"title": "Fontes de ingestão",
|
||||
"ingestion_sources": "Fontes de ingestão",
|
||||
"bulk_actions": "Ações em massa",
|
||||
"force_sync": "Forçar sincronização",
|
||||
"delete": "Excluir",
|
||||
"create_new": "Criar novo",
|
||||
"name": "Nome",
|
||||
"provider": "Provedor",
|
||||
"status": "Status",
|
||||
"active": "Ativo",
|
||||
"created_at": "Criado em",
|
||||
"actions": "Ações",
|
||||
"last_sync_message": "Última mensagem de sincronização",
|
||||
"empty": "Vazio",
|
||||
"open_menu": "Abrir menu",
|
||||
"edit": "Editar",
|
||||
"create": "Criar",
|
||||
"ingestion_source": "Fonte de ingestão",
|
||||
"edit_description": "Faça alterações em sua fonte de ingestão aqui.",
|
||||
"create_description": "Adicione uma nova fonte de ingestão para começar a arquivar e-mails.",
|
||||
"read": "Ler",
|
||||
"docs_here": "documentos aqui",
|
||||
"delete_confirmation_title": "Tem certeza de que deseja excluir esta ingestão?",
|
||||
"delete_confirmation_description": "Isso excluirá todos os e-mails arquivados, anexos, indexação e arquivos associados a esta ingestão. Se você deseja apenas parar de sincronizar novos e-mails, pode pausar a ingestão.",
|
||||
"deleting": "Excluindo",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar",
|
||||
"bulk_delete_confirmation_title": "Tem certeza de que deseja excluir {{count}} ingestões selecionadas?",
|
||||
"bulk_delete_confirmation_description": "Isso excluirá todos os e-mails arquivados, anexos, indexação e arquivos associados a essas ingestões. Se você deseja apenas parar de sincronizar novos e-mails, pode pausar as ingestões."
|
||||
},
|
||||
"search": {
|
||||
"title": "Pesquisar",
|
||||
"description": "Pesquisar e-mails arquivados.",
|
||||
"email_search": "Pesquisa de e-mail",
|
||||
"placeholder": "Pesquisar por palavra-chave, remetente, destinatário...",
|
||||
"search_button": "Pesquisar",
|
||||
"search_options": "Opções de pesquisa",
|
||||
"strategy_fuzzy": "Fuzzy",
|
||||
"strategy_verbatim": "Verbatim",
|
||||
"strategy_frequency": "Frequência",
|
||||
"select_strategy": "Selecione uma estratégia",
|
||||
"error": "Erro",
|
||||
"found_results_in": "Encontrados {{total}} resultados em {{seconds}}s",
|
||||
"found_results": "Encontrados {{total}} resultados",
|
||||
"from": "De",
|
||||
"to": "Para",
|
||||
"in_email_body": "No corpo do e-mail",
|
||||
"in_attachment": "No anexo: {{filename}}",
|
||||
"prev": "Anterior",
|
||||
"next": "Próximo"
|
||||
},
|
||||
"roles": {
|
||||
"title": "Gerenciamento de funções",
|
||||
"role_management": "Gerenciamento de funções",
|
||||
"create_new": "Criar novo",
|
||||
"name": "Nome",
|
||||
"created_at": "Criado em",
|
||||
"actions": "Ações",
|
||||
"open_menu": "Abrir menu",
|
||||
"view_policy": "Visualizar política",
|
||||
"edit": "Editar",
|
||||
"delete": "Excluir",
|
||||
"no_roles_found": "Nenhuma função encontrada.",
|
||||
"role_policy": "Política de função",
|
||||
"viewing_policy_for_role": "Visualizando política para a função: {{name}}",
|
||||
"create": "Criar",
|
||||
"role": "Função",
|
||||
"edit_description": "Faça alterações na função aqui.",
|
||||
"create_description": "Adicione uma nova função ao sistema.",
|
||||
"delete_confirmation_title": "Tem certeza de que deseja excluir esta função?",
|
||||
"delete_confirmation_description": "Esta ação não pode ser desfeita. Isso excluirá permanentemente a função.",
|
||||
"deleting": "Excluindo",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar"
|
||||
},
|
||||
"system_settings": {
|
||||
"title": "Configurações do sistema",
|
||||
"system_settings": "Configurações do sistema",
|
||||
"description": "Gerenciar configurações globais do aplicativo.",
|
||||
"language": "Idioma",
|
||||
"default_theme": "Tema padrão",
|
||||
"light": "Claro",
|
||||
"dark": "Escuro",
|
||||
"system": "Sistema",
|
||||
"support_email": "E-mail de suporte",
|
||||
"saving": "Salvando",
|
||||
"save_changes": "Salvar alterações"
|
||||
},
|
||||
"users": {
|
||||
"title": "Gerenciamento de usuários",
|
||||
"user_management": "Gerenciamento de usuários",
|
||||
"create_new": "Criar novo",
|
||||
"name": "Nome",
|
||||
"email": "E-mail",
|
||||
"role": "Função",
|
||||
"created_at": "Criado em",
|
||||
"actions": "Ações",
|
||||
"open_menu": "Abrir menu",
|
||||
"edit": "Editar",
|
||||
"delete": "Excluir",
|
||||
"no_users_found": "Nenhum usuário encontrado.",
|
||||
"create": "Criar",
|
||||
"user": "Usuário",
|
||||
"edit_description": "Faça alterações no usuário aqui.",
|
||||
"create_description": "Adicione um novo usuário ao sistema.",
|
||||
"delete_confirmation_title": "Tem certeza de que deseja excluir este usuário?",
|
||||
"delete_confirmation_description": "Esta ação não pode ser desfeita. Isso excluirá permanentemente o usuário e removerá seus dados de nossos servidores.",
|
||||
"deleting": "Excluindo",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar"
|
||||
},
|
||||
"setup": {
|
||||
"title": "Configuração",
|
||||
"description": "Configure a conta de administrador inicial para o Open Archiver.",
|
||||
"welcome": "Bem-vindo",
|
||||
"create_admin_account": "Crie a primeira conta de administrador para começar.",
|
||||
"first_name": "Primeiro nome",
|
||||
"last_name": "Último nome",
|
||||
"email": "E-mail",
|
||||
"password": "Senha",
|
||||
"creating_account": "Criando conta",
|
||||
"create_account": "Criar conta"
|
||||
},
|
||||
"layout": {
|
||||
"dashboard": "Painel",
|
||||
"ingestions": "Ingestões",
|
||||
"archived_emails": "E-mails arquivados",
|
||||
"search": "Pesquisar",
|
||||
"settings": "Configurações",
|
||||
"system": "Sistema",
|
||||
"users": "Usuários",
|
||||
"roles": "Funções",
|
||||
"logout": "Sair"
|
||||
},
|
||||
"components": {
|
||||
"charts": {
|
||||
"emails_ingested": "E-mails ingeridos",
|
||||
"storage_used": "Armazenamento usado",
|
||||
"emails": "E-mails"
|
||||
},
|
||||
"common": {
|
||||
"submitting": "Enviando...",
|
||||
"submit": "Enviar",
|
||||
"save": "Salvar"
|
||||
},
|
||||
"email_preview": {
|
||||
"loading": "Carregando visualização de e-mail...",
|
||||
"render_error": "Não foi possível renderizar a visualização do e-mail.",
|
||||
"not_available": "Arquivo .eml bruto não disponível para este e-mail."
|
||||
},
|
||||
"footer": {
|
||||
"all_rights_reserved": "Todos os direitos reservados."
|
||||
},
|
||||
"ingestion_source_form": {
|
||||
"provider_generic_imap": "IMAP genérico",
|
||||
"provider_google_workspace": "Google Workspace",
|
||||
"provider_microsoft_365": "Microsoft 365",
|
||||
"provider_pst_import": "Importação de PST",
|
||||
"provider_eml_import": "Importação de EML",
|
||||
"select_provider": "Selecione um provedor",
|
||||
"service_account_key": "Chave da conta de serviço (JSON)",
|
||||
"service_account_key_placeholder": "Cole o conteúdo JSON da sua chave de conta de serviço",
|
||||
"impersonated_admin_email": "E-mail de administrador representado",
|
||||
"client_id": "ID do aplicativo (cliente)",
|
||||
"client_secret": "Valor secreto do cliente",
|
||||
"client_secret_placeholder": "Digite o valor secreto, não o ID secreto",
|
||||
"tenant_id": "ID do diretório (locatário)",
|
||||
"host": "Host",
|
||||
"port": "Porta",
|
||||
"username": "Nome de usuário",
|
||||
"use_tls": "Usar TLS",
|
||||
"pst_file": "Arquivo PST",
|
||||
"eml_file": "Arquivo EML",
|
||||
"heads_up": "Atenção!",
|
||||
"org_wide_warning": "Observe que esta é uma operação em toda a organização. Esse tipo de ingestão importará e indexará <b>todas</b> as caixas de correio de e-mail em sua organização. Se você deseja importar apenas caixas de correio de e-mail específicas, use o conector IMAP.",
|
||||
"upload_failed": "Falha no upload, tente novamente"
|
||||
},
|
||||
"role_form": {
|
||||
"policies_json": "Políticas (JSON)",
|
||||
"invalid_json": "Formato JSON inválido para políticas."
|
||||
},
|
||||
"theme_switcher": {
|
||||
"toggle_theme": "Alternar tema"
|
||||
},
|
||||
"user_form": {
|
||||
"select_role": "Selecione uma função"
|
||||
}
|
||||
},
|
||||
"dashboard_page": {
|
||||
"title": "Painel",
|
||||
"meta_description": "Visão geral do seu arquivo de e-mail.",
|
||||
"header": "Painel",
|
||||
"create_ingestion": "Criar uma ingestão",
|
||||
"no_ingestion_header": "Você não tem nenhuma fonte de ingestão configurada.",
|
||||
"no_ingestion_text": "Adicione uma fonte de ingestão para começar a arquivar suas caixas de entrada.",
|
||||
"total_emails_archived": "Total de e-mails arquivados",
|
||||
"total_storage_used": "Armazenamento total usado",
|
||||
"failed_ingestions": "Ingestões com falha (últimos 7 dias)",
|
||||
"ingestion_history": "Histórico de ingestão",
|
||||
"no_ingestion_history": "Nenhum histórico de ingestão disponível.",
|
||||
"storage_by_source": "Armazenamento por fonte de ingestão",
|
||||
"no_ingestion_sources": "Nenhuma fonte de ingestão disponível.",
|
||||
"indexed_insights": "Informações indexadas",
|
||||
"top_10_senders": "10 principais remetentes",
|
||||
"no_indexed_insights": "Nenhuma informação indexada disponível."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { redirect } from '@sveltejs/kit';
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
import 'dotenv/config';
|
||||
import { api } from '$lib/server/api';
|
||||
import type { SystemSettings } from '@open-archiver/types';
|
||||
|
||||
export const load: LayoutServerLoad = async (event) => {
|
||||
const { locals, url } = event;
|
||||
@@ -21,7 +22,7 @@ export const load: LayoutServerLoad = async (event) => {
|
||||
}
|
||||
|
||||
const settingsResponse = await api('/settings', event);
|
||||
const settings = settingsResponse.ok ? await settingsResponse.json() : null;
|
||||
const settings: SystemSettings | null = settingsResponse.ok ? await settingsResponse.json() : null;
|
||||
|
||||
return {
|
||||
user: locals.user,
|
||||
|
||||
21
packages/frontend/src/routes/+layout.ts
Normal file
21
packages/frontend/src/routes/+layout.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { loadTranslations } from '$lib/translations';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import { browser } from '$app/environment';
|
||||
import type { SupportedLanguage } from '@open-archiver/types';
|
||||
|
||||
export const load: LayoutLoad = async ({ url, data }) => {
|
||||
const { pathname } = url;
|
||||
|
||||
let initLocale: SupportedLanguage = 'en'; // Default fallback
|
||||
|
||||
if (data.settings?.language) {
|
||||
initLocale = data.settings.language;
|
||||
}
|
||||
|
||||
console.log(initLocale);
|
||||
await loadTranslations(initLocale, pathname);
|
||||
|
||||
return {
|
||||
...data
|
||||
};
|
||||
};
|
||||
@@ -5,6 +5,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import ThemeSwitcher from '$lib/components/custom/ThemeSwitcher.svelte';
|
||||
import { t } from '$lib/translations';
|
||||
const navItems: {
|
||||
href?: string;
|
||||
label: string;
|
||||
@@ -13,24 +14,24 @@
|
||||
label: string;
|
||||
}[];
|
||||
}[] = [
|
||||
{ href: '/dashboard', label: 'Dashboard' },
|
||||
{ href: '/dashboard/ingestions', label: 'Ingestions' },
|
||||
{ href: '/dashboard/archived-emails', label: 'Archived emails' },
|
||||
{ href: '/dashboard/search', label: 'Search' },
|
||||
{ href: '/dashboard', label: $t('app.layout.dashboard') },
|
||||
{ href: '/dashboard/ingestions', label: $t('app.layout.ingestions') },
|
||||
{ href: '/dashboard/archived-emails', label: $t('app.layout.archived_emails') },
|
||||
{ href: '/dashboard/search', label: $t('app.layout.search') },
|
||||
{
|
||||
label: 'Settings',
|
||||
label: $t('app.layout.settings'),
|
||||
subMenu: [
|
||||
{
|
||||
href: '/dashboard/settings/system',
|
||||
label: 'System',
|
||||
label: $t('app.layout.system'),
|
||||
},
|
||||
{
|
||||
href: '/dashboard/settings/users',
|
||||
label: 'Users',
|
||||
label: $t('app.layout.users'),
|
||||
},
|
||||
{
|
||||
href: '/dashboard/settings/roles',
|
||||
label: 'Roles',
|
||||
label: $t('app.layout.roles'),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -90,7 +91,7 @@
|
||||
</NavigationMenu.Root>
|
||||
<div class="flex items-center gap-4">
|
||||
<ThemeSwitcher />
|
||||
<Button onclick={handleLogout} variant="outline">Logout</Button>
|
||||
<Button onclick={handleLogout} variant="outline">{$t('app.layout.logout')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import TopSendersChart from '$lib/components/custom/charts/TopSendersChart.svelte';
|
||||
import IngestionHistoryChart from '$lib/components/custom/charts/IngestionHistoryChart.svelte';
|
||||
import StorageBySourceChart from '$lib/components/custom/charts/StorageBySourceChart.svelte';
|
||||
import { t } from '$lib/translations';
|
||||
let { data }: { data: PageData } = $props();
|
||||
|
||||
const transformedHistory = $derived(
|
||||
@@ -19,20 +20,20 @@
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Dashboard - OpenArchiver</title>
|
||||
<meta name="description" content="Overview of your email archive." />
|
||||
<title>{$t('app.dashboard_page.title')} - OpenArchiver</title>
|
||||
<meta name="description" content={$t('app.dashboard_page.meta_description')} />
|
||||
</svelte:head>
|
||||
|
||||
<div class="flex-1 space-y-4">
|
||||
<div class="flex items-center justify-between space-y-2">
|
||||
<h2 class="text-3xl font-bold tracking-tight">Dashboard</h2>
|
||||
<h2 class="text-3xl font-bold tracking-tight">{$t('app.dashboard_page.header')}</h2>
|
||||
</div>
|
||||
{#if !data.ingestionSources || data.ingestionSources?.length === 0}
|
||||
<div>
|
||||
<EmptyState
|
||||
buttonText="Create an ingestion"
|
||||
header="You don't have any ingestion source set up."
|
||||
text="Add an ingestion source to start archiving your inboxes."
|
||||
buttonText={$t('app.dashboard_page.create_ingestion')}
|
||||
header={$t('app.dashboard_page.no_ingestion_header')}
|
||||
text={$t('app.dashboard_page.no_ingestion_text')}
|
||||
click={() => {
|
||||
goto('/dashboard/ingestions');
|
||||
}}
|
||||
@@ -47,9 +48,9 @@
|
||||
<Card.Header
|
||||
class="flex flex-row items-center justify-between space-y-0 pb-2"
|
||||
>
|
||||
<Card.Title class="text-sm font-medium"
|
||||
>Total Emails Archived</Card.Title
|
||||
>
|
||||
<Card.Title class="text-sm font-medium">
|
||||
{$t('app.dashboard_page.total_emails_archived')}
|
||||
</Card.Title>
|
||||
<Archive class="text-muted-foreground h-4 w-4" />
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
@@ -62,7 +63,9 @@
|
||||
<Card.Header
|
||||
class="flex flex-row items-center justify-between space-y-0 pb-2"
|
||||
>
|
||||
<Card.Title class="text-sm font-medium">Total Storage Used</Card.Title>
|
||||
<Card.Title class="text-sm font-medium"
|
||||
>{$t('app.dashboard_page.total_storage_used')}</Card.Title
|
||||
>
|
||||
<HardDrive class="text-muted-foreground h-4 w-4" />
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
@@ -75,9 +78,9 @@
|
||||
<Card.Header
|
||||
class="flex flex-row items-center justify-between space-y-0 pb-2"
|
||||
>
|
||||
<Card.Title class="text-sm font-medium"
|
||||
>Failed Ingestions (Last 7 Days)</Card.Title
|
||||
>
|
||||
<Card.Title class="text-sm font-medium">
|
||||
{$t('app.dashboard_page.failed_ingestions')}
|
||||
</Card.Title>
|
||||
<CircleAlert class=" text-muted-foreground h-4 w-4" />
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
@@ -97,13 +100,13 @@
|
||||
<div class=" lg:col-span-2">
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Ingestion History</Card.Title>
|
||||
<Card.Title>{$t('app.dashboard_page.ingestion_history')}</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class=" pl-4">
|
||||
{#if transformedHistory.length > 0}
|
||||
<IngestionHistoryChart data={transformedHistory} />
|
||||
{:else}
|
||||
<p>No ingestion history available.</p>
|
||||
<p>{$t('app.dashboard_page.no_ingestion_history')}</p>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
@@ -111,31 +114,33 @@
|
||||
<div class=" lg:col-span-1">
|
||||
<Card.Root class="h-full">
|
||||
<Card.Header>
|
||||
<Card.Title>Storage by Ingestion Source</Card.Title>
|
||||
<Card.Title>{$t('app.dashboard_page.storage_by_source')}</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="h-full">
|
||||
{#if data.ingestionSources && data.ingestionSources.length > 0}
|
||||
<StorageBySourceChart data={data.ingestionSources} />
|
||||
{:else}
|
||||
<p>No ingestion sources available.</p>
|
||||
<p>{$t('app.dashboard_page.no_ingestion_sources')}</p>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-xl font-semibold leading-6">Indexed insights</h1>
|
||||
<h1 class="text-xl font-semibold leading-6">
|
||||
{$t('app.dashboard_page.indexed_insights')}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="grid grid-cols-1">
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Top 10 Senders</Card.Title>
|
||||
<Card.Title>{$t('app.dashboard_page.top_10_senders')}</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
{#if data.indexedInsights && data.indexedInsights.topSenders.length > 0}
|
||||
<TopSendersChart data={data.indexedInsights.topSenders} />
|
||||
{:else}
|
||||
<p>No indexed insights available.</p>
|
||||
<p>{$t('app.dashboard_page.no_indexed_insights')}</p>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { goto } from '$app/navigation';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
|
||||
@@ -68,11 +69,11 @@
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Archived emails - OpenArchiver</title>
|
||||
<title>{$t('app.archived_emails_page.title')} - OpenArchiver</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<h1 class="text-2xl font-bold">Archived Emails</h1>
|
||||
<h1 class="text-2xl font-bold">{$t('app.archived_emails_page.header')}</h1>
|
||||
{#if ingestionSources.length > 0}
|
||||
<div class="w-[250px]">
|
||||
<Select.Root
|
||||
@@ -84,7 +85,7 @@
|
||||
<span
|
||||
>{selectedIngestionSourceId
|
||||
? ingestionSources.find((s) => s.id === selectedIngestionSourceId)?.name
|
||||
: 'Select an ingestion source'}</span
|
||||
: $t('app.archived_emails_page.select_ingestion_source')}</span
|
||||
>
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
@@ -101,12 +102,12 @@
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.Head>Date</Table.Head>
|
||||
<Table.Head>Subject</Table.Head>
|
||||
<Table.Head>Sender</Table.Head>
|
||||
<Table.Head>Inbox</Table.Head>
|
||||
<Table.Head>Path</Table.Head>
|
||||
<Table.Head class="text-right">Actions</Table.Head>
|
||||
<Table.Head>{$t('app.archived_emails_page.date')}</Table.Head>
|
||||
<Table.Head>{$t('app.archived_emails_page.subject')}</Table.Head>
|
||||
<Table.Head>{$t('app.archived_emails_page.sender')}</Table.Head>
|
||||
<Table.Head>{$t('app.archived_emails_page.inbox')}</Table.Head>
|
||||
<Table.Head>{$t('app.archived_emails_page.path')}</Table.Head>
|
||||
<Table.Head class="text-right">{$t('app.archived_emails_page.actions')}</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body class="text-sm">
|
||||
@@ -135,7 +136,9 @@
|
||||
</Table.Cell>
|
||||
<Table.Cell class="text-right">
|
||||
<a href={`/dashboard/archived-emails/${email.id}`}>
|
||||
<Button variant="outline">View</Button>
|
||||
<Button variant="outline"
|
||||
>{$t('app.archived_emails_page.view')}</Button
|
||||
>
|
||||
</a>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
@@ -143,7 +146,7 @@
|
||||
{:else}
|
||||
<Table.Row>
|
||||
<Table.Cell colspan={5} class="text-center"
|
||||
>No archived emails found.</Table.Cell
|
||||
>{$t('app.archived_emails_page.no_emails_found')}</Table.Cell
|
||||
>
|
||||
</Table.Row>
|
||||
{/if}
|
||||
@@ -159,7 +162,9 @@
|
||||
}&limit=${archivedEmails.limit}`}
|
||||
class={archivedEmails.page === 1 ? 'pointer-events-none' : ''}
|
||||
>
|
||||
<Button variant="outline" disabled={archivedEmails.page === 1}>Prev</Button>
|
||||
<Button variant="outline" disabled={archivedEmails.page === 1}
|
||||
>{$t('app.archived_emails_page.prev')}</Button
|
||||
>
|
||||
</a>
|
||||
|
||||
{#each paginationItems as item}
|
||||
@@ -187,7 +192,8 @@
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={archivedEmails.page ===
|
||||
Math.ceil(archivedEmails.total / archivedEmails.limit)}>Next</Button
|
||||
Math.ceil(archivedEmails.total / archivedEmails.limit)}
|
||||
>{$t('app.archived_emails_page.next')}</Button
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import * as Dialog from '$lib/components/ui/dialog';
|
||||
import { setAlert } from '$lib/components/custom/alert/alert-state.svelte';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
let email = $derived(data.email);
|
||||
@@ -72,7 +73,7 @@
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{email?.subject} | Archived emails - OpenArchiver</title>
|
||||
<title>{email?.subject} | {$t('app.archive.title')} - OpenArchiver</title>
|
||||
</svelte:head>
|
||||
|
||||
{#if email}
|
||||
@@ -80,29 +81,31 @@
|
||||
<div class="col-span-3 md:col-span-2">
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>{email.subject || 'No Subject'}</Card.Title>
|
||||
<Card.Title>{email.subject || $t('app.archive.no_subject')}</Card.Title>
|
||||
<Card.Description>
|
||||
From: {email.senderEmail || email.senderName} | Sent: {new Date(
|
||||
email.sentAt
|
||||
).toLocaleString()}
|
||||
{$t('app.archive.from')}: {email.senderEmail || email.senderName} | {$t(
|
||||
'app.archive.sent'
|
||||
)}: {new Date(email.sentAt).toLocaleString()}
|
||||
</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-1">
|
||||
<h3 class="font-semibold">Recipients</h3>
|
||||
<h3 class="font-semibold">{$t('app.archive.recipients')}</h3>
|
||||
<Card.Description>
|
||||
<p>
|
||||
To: {email.recipients.map((r) => r.email || r.name).join(', ')}
|
||||
{$t('app.archive.to')}: {email.recipients
|
||||
.map((r) => r.email || r.name)
|
||||
.join(', ')}
|
||||
</p>
|
||||
</Card.Description>
|
||||
</div>
|
||||
<div class=" space-y-1">
|
||||
<h3 class="font-semibold">Meta data</h3>
|
||||
<h3 class="font-semibold">{$t('app.archive.meta_data')}</h3>
|
||||
<Card.Description class="space-y-2">
|
||||
{#if email.path}
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span>Folder:</span>
|
||||
<span>{$t('app.archive.folder')}:</span>
|
||||
<span class=" bg-muted truncate rounded p-1.5 text-xs"
|
||||
>{email.path || '/'}</span
|
||||
>
|
||||
@@ -110,7 +113,7 @@
|
||||
{/if}
|
||||
{#if email.tags && email.tags.length > 0}
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span> Tags: </span>
|
||||
<span> {$t('app.archive.tags')}: </span>
|
||||
{#each email.tags as tag}
|
||||
<span class=" bg-muted truncate rounded p-1.5 text-xs"
|
||||
>{tag}</span
|
||||
@@ -119,7 +122,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span>size:</span>
|
||||
<span>{$t('app.archive.size')}:</span>
|
||||
<span class=" bg-muted truncate rounded p-1.5 text-xs"
|
||||
>{formatBytes(email.sizeBytes)}</span
|
||||
>
|
||||
@@ -127,12 +130,14 @@
|
||||
</Card.Description>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold">Email Preview</h3>
|
||||
<h3 class="font-semibold">{$t('app.archive.email_preview')}</h3>
|
||||
<EmailPreview raw={email.raw} />
|
||||
</div>
|
||||
{#if email.attachments && email.attachments.length > 0}
|
||||
<div>
|
||||
<h3 class="font-semibold">Attachments</h3>
|
||||
<h3 class="font-semibold">
|
||||
{$t('app.archive.attachments')}
|
||||
</h3>
|
||||
<ul class="mt-2 space-y-2">
|
||||
{#each email.attachments as attachment}
|
||||
<li
|
||||
@@ -152,7 +157,7 @@
|
||||
attachment.filename
|
||||
)}
|
||||
>
|
||||
Download
|
||||
{$t('app.archive.download')}
|
||||
</Button>
|
||||
</li>
|
||||
{/each}
|
||||
@@ -166,16 +171,16 @@
|
||||
<div class="col-span-3 space-y-6 md:col-span-1">
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Actions</Card.Title>
|
||||
<Card.Title>{$t('app.archive.actions')}</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="space-y-2">
|
||||
<Button
|
||||
onclick={() =>
|
||||
download(email.storagePath, `${email.subject || 'email'}.eml`)}
|
||||
>Download Email (.eml)</Button
|
||||
>{$t('app.archive.download_eml')}</Button
|
||||
>
|
||||
<Button variant="destructive" onclick={() => (isDeleteDialogOpen = true)}>
|
||||
Delete Email
|
||||
{$t('app.archive.delete_email')}
|
||||
</Button>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
@@ -183,7 +188,7 @@
|
||||
{#if email.thread && email.thread.length > 1}
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Email thread</Card.Title>
|
||||
<Card.Title>{$t('app.archive.email_thread')}</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<EmailThread thread={email.thread} currentEmailId={email.id} />
|
||||
@@ -196,10 +201,9 @@
|
||||
<Dialog.Root bind:open={isDeleteDialogOpen}>
|
||||
<Dialog.Content class="sm:max-w-lg">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Are you sure you want to delete this email?</Dialog.Title>
|
||||
<Dialog.Title>{$t('app.archive.delete_confirmation_title')}</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
This action cannot be undone and will permanently remove the email and its
|
||||
attachments.
|
||||
{$t('app.archive.delete_confirmation_description')}
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<Dialog.Footer class="sm:justify-start">
|
||||
@@ -209,14 +213,18 @@
|
||||
onclick={confirmDelete}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
{#if isDeleting}Deleting...{:else}Confirm{/if}
|
||||
{#if isDeleting}
|
||||
{$t('app.archive.deleting')}...
|
||||
{:else}
|
||||
{$t('app.archive.confirm')}
|
||||
{/if}
|
||||
</Button>
|
||||
<Dialog.Close>
|
||||
<Button type="button" variant="secondary">Cancel</Button>
|
||||
<Button type="button" variant="secondary">{$t('app.archive.cancel')}</Button>
|
||||
</Dialog.Close>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
{:else}
|
||||
<p>Email not found.</p>
|
||||
<p>{$t('app.archive.not_found')}</p>
|
||||
{/if}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import Badge from '$lib/components/ui/badge/badge.svelte';
|
||||
import { setAlert } from '$lib/components/custom/alert/alert-state.svelte';
|
||||
import * as HoverCard from '$lib/components/ui/hover-card/index.js';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
let ingestionSources = $state(data.ingestionSources);
|
||||
@@ -266,38 +267,40 @@
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Ingestion sources - OpenArchiver</title>
|
||||
<title>{$t('app.ingestions.title')} - OpenArchiver</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<h1 class="text-2xl font-bold">Ingestion Sources</h1>
|
||||
<h1 class="text-2xl font-bold">{$t('app.ingestions.ingestion_sources')}</h1>
|
||||
{#if selectedIds.length > 0}
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button variant="outline">
|
||||
Bulk Actions ({selectedIds.length})
|
||||
{$t('app.ingestions.bulk_actions')} ({selectedIds.length})
|
||||
<MoreHorizontal class="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.Item onclick={handleBulkForceSync}>
|
||||
<RefreshCw class="mr-2 h-4 w-4" />
|
||||
Force Sync
|
||||
{$t('app.ingestions.force_sync')}
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
class="text-red-600"
|
||||
onclick={() => (isBulkDeleteDialogOpen = true)}
|
||||
>
|
||||
<Trash class="mr-2 h-4 w-4" />
|
||||
Delete
|
||||
{$t('app.ingestions.delete')}
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
{/if}
|
||||
</div>
|
||||
<Button onclick={openCreateDialog} disabled={data.isDemo}>Create New</Button>
|
||||
<Button onclick={openCreateDialog} disabled={data.isDemo}
|
||||
>{$t('app.ingestions.create_new')}</Button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border">
|
||||
@@ -319,12 +322,12 @@
|
||||
: ((selectedIds.length > 0 ? 'indeterminate' : false) as any)}
|
||||
/>
|
||||
</Table.Head>
|
||||
<Table.Head>Name</Table.Head>
|
||||
<Table.Head>Provider</Table.Head>
|
||||
<Table.Head>Status</Table.Head>
|
||||
<Table.Head>Active</Table.Head>
|
||||
<Table.Head>Created At</Table.Head>
|
||||
<Table.Head class="text-right">Actions</Table.Head>
|
||||
<Table.Head>{$t('app.ingestions.name')}</Table.Head>
|
||||
<Table.Head>{$t('app.ingestions.provider')}</Table.Head>
|
||||
<Table.Head>{$t('app.ingestions.status')}</Table.Head>
|
||||
<Table.Head>{$t('app.ingestions.active')}</Table.Head>
|
||||
<Table.Head>{$t('app.ingestions.created_at')}</Table.Head>
|
||||
<Table.Head class="text-right">{$t('app.ingestions.actions')}</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
@@ -369,8 +372,9 @@
|
||||
<HoverCard.Content class="{getStatusClasses(source.status)} ">
|
||||
<div class="flex flex-col space-y-4 text-sm">
|
||||
<p class=" font-mono">
|
||||
<b>Last sync message:</b>
|
||||
{source.lastSyncStatusMessage || 'Empty'}
|
||||
<b>{$t('app.ingestions.last_sync_message')}:</b>
|
||||
{source.lastSyncStatusMessage ||
|
||||
$t('app.ingestions.empty')}
|
||||
</p>
|
||||
</div>
|
||||
</HoverCard.Content>
|
||||
@@ -393,23 +397,27 @@
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button variant="ghost" class="h-8 w-8 p-0">
|
||||
<span class="sr-only">Open menu</span>
|
||||
<span class="sr-only"
|
||||
>{$t('app.ingestions.open_menu')}</span
|
||||
>
|
||||
<MoreHorizontal class="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.Label>Actions</DropdownMenu.Label>
|
||||
<DropdownMenu.Label
|
||||
>{$t('app.ingestions.actions')}</DropdownMenu.Label
|
||||
>
|
||||
<DropdownMenu.Item onclick={() => openEditDialog(source)}
|
||||
>Edit</DropdownMenu.Item
|
||||
>{$t('app.ingestions.edit')}</DropdownMenu.Item
|
||||
>
|
||||
<DropdownMenu.Item onclick={() => handleSync(source.id)}
|
||||
>Force sync</DropdownMenu.Item
|
||||
>{$t('app.ingestions.force_sync')}</DropdownMenu.Item
|
||||
>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item
|
||||
class="text-red-600"
|
||||
onclick={() => openDeleteDialog(source)}
|
||||
>Delete</DropdownMenu.Item
|
||||
>{$t('app.ingestions.delete')}</DropdownMenu.Item
|
||||
>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
@@ -429,17 +437,21 @@
|
||||
<Dialog.Root bind:open={isDialogOpen}>
|
||||
<Dialog.Content class="sm:max-w-120 md:max-w-180">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>{selectedSource ? 'Edit' : 'Create'} Ingestion Source</Dialog.Title>
|
||||
<Dialog.Title
|
||||
>{selectedSource ? $t('app.ingestions.edit') : $t('app.ingestions.create')}{' '}
|
||||
{$t('app.ingestions.ingestion_source')}</Dialog.Title
|
||||
>
|
||||
<Dialog.Description>
|
||||
{selectedSource
|
||||
? 'Make changes to your ingestion source here.'
|
||||
: 'Add a new ingestion source to start archiving emails.'}
|
||||
? $t('app.ingestions.edit_description')
|
||||
: $t('app.ingestions.create_description')}
|
||||
<span
|
||||
>Read <a
|
||||
>{$t('app.ingestions.read')}{' '}
|
||||
<a
|
||||
class="text-primary underline underline-offset-2"
|
||||
target="_blank"
|
||||
href="https://docs.openarchiver.com/user-guides/email-providers/"
|
||||
>docs here</a
|
||||
>{$t('app.ingestions.docs_here')}</a
|
||||
>.</span
|
||||
>
|
||||
</Dialog.Description>
|
||||
@@ -451,11 +463,9 @@
|
||||
<Dialog.Root bind:open={isDeleteDialogOpen}>
|
||||
<Dialog.Content class="sm:max-w-lg">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Are you sure you want to delete this ingestion?</Dialog.Title>
|
||||
<Dialog.Title>{$t('app.ingestions.delete_confirmation_title')}</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
This will delete all archived emails, attachments, indexing, and files associated
|
||||
with this ingestion. If you only want to stop syncing new emails, you can pause the
|
||||
ingestion instead.
|
||||
{$t('app.ingestions.delete_confirmation_description')}
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<Dialog.Footer class="sm:justify-start">
|
||||
@@ -464,10 +474,14 @@
|
||||
variant="destructive"
|
||||
onclick={confirmDelete}
|
||||
disabled={isDeleting}
|
||||
>{#if isDeleting}Deleting...{:else}Confirm{/if}</Button
|
||||
>{#if isDeleting}
|
||||
{$t('app.ingestions.deleting')}...
|
||||
{:else}
|
||||
{$t('app.ingestions.confirm')}
|
||||
{/if}</Button
|
||||
>
|
||||
<Dialog.Close>
|
||||
<Button type="button" variant="secondary">Cancel</Button>
|
||||
<Button type="button" variant="secondary">{$t('app.ingestions.cancel')}</Button>
|
||||
</Dialog.Close>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
@@ -477,12 +491,12 @@
|
||||
<Dialog.Content class="sm:max-w-lg">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title
|
||||
>Are you sure you want to delete {selectedIds.length} selected ingestions?</Dialog.Title
|
||||
>{$t('app.ingestions.bulk_delete_confirmation_title', {
|
||||
count: selectedIds.length,
|
||||
} as any)}</Dialog.Title
|
||||
>
|
||||
<Dialog.Description>
|
||||
This will delete all archived emails, attachments, indexing, and files associated
|
||||
with these ingestions. If you only want to stop syncing new emails, you can pause
|
||||
the ingestions instead.
|
||||
{$t('app.ingestions.bulk_delete_confirmation_description')}
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<Dialog.Footer class="sm:justify-start">
|
||||
@@ -491,10 +505,14 @@
|
||||
variant="destructive"
|
||||
onclick={handleBulkDelete}
|
||||
disabled={isDeleting}
|
||||
>{#if isDeleting}Deleting...{:else}Confirm{/if}</Button
|
||||
>{#if isDeleting}
|
||||
{$t('app.ingestions.deleting')}...
|
||||
{:else}
|
||||
{$t('app.ingestions.confirm')}
|
||||
{/if}</Button
|
||||
>
|
||||
<Dialog.Close>
|
||||
<Button type="button" variant="secondary">Cancel</Button>
|
||||
<Button type="button" variant="secondary">{$t('app.ingestions.cancel')}</Button>
|
||||
</Dialog.Close>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import type { MatchingStrategy } from '@open-archiver/types';
|
||||
import CircleAlertIcon from '@lucide/svelte/icons/circle-alert';
|
||||
import * as Alert from '$lib/components/ui/alert/index.js';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
let searchResult = $derived(data.searchResult);
|
||||
@@ -27,13 +28,14 @@
|
||||
);
|
||||
|
||||
const strategies = [
|
||||
{ value: 'last', label: 'Fuzzy' },
|
||||
{ value: 'all', label: 'Verbatim' },
|
||||
{ value: 'frequency', label: 'Frequency' },
|
||||
{ value: 'last', label: $t('app.search.strategy_fuzzy') },
|
||||
{ value: 'all', label: $t('app.search.strategy_verbatim') },
|
||||
{ value: 'frequency', label: $t('app.search.strategy_frequency') },
|
||||
];
|
||||
|
||||
const triggerContent = $derived(
|
||||
strategies.find((s) => s.value === matchingStrategy)?.label ?? 'Select a strategy'
|
||||
strategies.find((s) => s.value === matchingStrategy)?.label ??
|
||||
$t('app.search.select_strategy')
|
||||
);
|
||||
|
||||
let isMounted = $state(false);
|
||||
@@ -170,25 +172,27 @@
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Search | Open Archiver</title>
|
||||
<meta name="description" content="Search for archived emails." />
|
||||
<title>{$t('app.search.title')} | Open Archiver</title>
|
||||
<meta name="description" content={$t('app.search.description')} />
|
||||
</svelte:head>
|
||||
|
||||
<div class="container mx-auto p-4 md:p-8">
|
||||
<h1 class="mb-4 text-2xl font-bold">Email Search</h1>
|
||||
<h1 class="mb-4 text-2xl font-bold">{$t('app.search.email_search')}</h1>
|
||||
|
||||
<form onsubmit={handleSearch} class="mb-8 flex flex-col space-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<Input
|
||||
type="search"
|
||||
name="keywords"
|
||||
placeholder="Search by keyword, sender, recipient..."
|
||||
placeholder={$t('app.search.placeholder')}
|
||||
class=" h-12 flex-grow"
|
||||
bind:value={keywords}
|
||||
/>
|
||||
<Button type="submit" class="h-12 cursor-pointer">Search</Button>
|
||||
<Button type="submit" class="h-12 cursor-pointer"
|
||||
>{$t('app.search.search_button')}</Button
|
||||
>
|
||||
</div>
|
||||
<div class="mt-1 text-xs font-medium">Search options</div>
|
||||
<div class="mt-1 text-xs font-medium">{$t('app.search.search_options')}</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Select.Root type="single" name="matchingStrategy" bind:value={matchingStrategy}>
|
||||
<Select.Trigger class=" w-[180px] cursor-pointer">
|
||||
@@ -212,7 +216,7 @@
|
||||
{#if error}
|
||||
<Alert.Root variant="destructive">
|
||||
<CircleAlertIcon class="size-4" />
|
||||
<Alert.Title>Error</Alert.Title>
|
||||
<Alert.Title>{$t('app.search.error')}</Alert.Title>
|
||||
<Alert.Description>{error}</Alert.Description>
|
||||
</Alert.Root>
|
||||
{/if}
|
||||
@@ -220,9 +224,12 @@
|
||||
{#if searchResult}
|
||||
<p class="text-muted-foreground mb-4">
|
||||
{#if searchResult.total > 0}
|
||||
Found {searchResult.total} results in {searchResult.processingTimeMs / 1000}s
|
||||
{$t('app.search.found_results_in', {
|
||||
total: searchResult.total,
|
||||
seconds: searchResult.processingTimeMs / 1000,
|
||||
} as any)}
|
||||
{:else}
|
||||
Found {searchResult.total} results
|
||||
{$t('app.search.found_results', { total: searchResult.total } as any)}
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
@@ -240,7 +247,7 @@
|
||||
{/if}
|
||||
</CardTitle>
|
||||
<CardDescription class="flex items-center space-x-1">
|
||||
<span>From:</span>
|
||||
<span>{$t('app.search.from')}:</span>
|
||||
{#if !isMounted}
|
||||
<span class="bg-accent h-4 w-40 animate-pulse rounded-md"
|
||||
></span>
|
||||
@@ -251,7 +258,7 @@
|
||||
></span>
|
||||
{/if}
|
||||
<span class="mx-2">|</span>
|
||||
<span>To:</span>
|
||||
<span>{$t('app.search.to')}:</span>
|
||||
{#if !isMounted}
|
||||
<span class="bg-accent h-4 w-40 animate-pulse rounded-md"
|
||||
></span>
|
||||
@@ -280,7 +287,9 @@
|
||||
<div
|
||||
class="space-y-2 rounded-md bg-slate-100 p-2 dark:bg-slate-800"
|
||||
>
|
||||
<p class="text-sm text-gray-500">In email body:</p>
|
||||
<p class="text-sm text-gray-500">
|
||||
{$t('app.search.in_email_body')}:
|
||||
</p>
|
||||
{#if !isMounted}
|
||||
<Skeleton class="my-2 h-5 w-full bg-gray-200" />
|
||||
{:else}
|
||||
@@ -302,7 +311,9 @@
|
||||
class="space-y-2 rounded-md bg-slate-100 p-2 dark:bg-slate-800"
|
||||
>
|
||||
<p class="text-sm text-gray-500">
|
||||
In attachment: {attachment.filename}
|
||||
{$t('app.search.in_attachment', {
|
||||
filename: attachment.filename,
|
||||
} as any)}
|
||||
</p>
|
||||
{#if !isMounted}
|
||||
<Skeleton class="my-2 h-5 w-full bg-gray-200" />
|
||||
@@ -331,7 +342,7 @@
|
||||
}&matchingStrategy=${matchingStrategy}`}
|
||||
class={page === 1 ? 'pointer-events-none' : ''}
|
||||
>
|
||||
<Button variant="outline" disabled={page === 1}>Prev</Button>
|
||||
<Button variant="outline" disabled={page === 1}>{$t('app.search.prev')}</Button>
|
||||
</a>
|
||||
|
||||
{#each paginationItems as item}
|
||||
@@ -357,7 +368,7 @@
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={page === Math.ceil(searchResult.total / searchResult.limit)}
|
||||
>Next</Button
|
||||
>{$t('app.search.next')}</Button
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import RoleForm from '$lib/components/custom/RoleForm.svelte';
|
||||
import { api } from '$lib/api.client';
|
||||
import type { Role } from '@open-archiver/types';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
let roles = $state(data.roles);
|
||||
@@ -108,22 +109,22 @@
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Role Management - OpenArchiver</title>
|
||||
<title>{$t('app.roles.title')} - OpenArchiver</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<h1 class="text-2xl font-bold">Role Management</h1>
|
||||
<Button onclick={openCreateDialog}>Create New</Button>
|
||||
<h1 class="text-2xl font-bold">{$t('app.roles.role_management')}</h1>
|
||||
<Button onclick={openCreateDialog}>{$t('app.roles.create_new')}</Button>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border">
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.Head>Name</Table.Head>
|
||||
<Table.Head>Created At</Table.Head>
|
||||
<Table.Head class="text-right">Actions</Table.Head>
|
||||
<Table.Head>{$t('app.roles.name')}</Table.Head>
|
||||
<Table.Head>{$t('app.roles.created_at')}</Table.Head>
|
||||
<Table.Head class="text-right">{$t('app.roles.actions')}</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
@@ -136,25 +137,27 @@
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button variant="ghost" class="h-8 w-8 p-0">
|
||||
<span class="sr-only">Open menu</span>
|
||||
<span class="sr-only">{$t('app.roles.open_menu')}</span>
|
||||
<MoreHorizontal class="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.Label>Actions</DropdownMenu.Label>
|
||||
<DropdownMenu.Label
|
||||
>{$t('app.roles.actions')}</DropdownMenu.Label
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
onclick={() => openViewPolicyDialog(role)}
|
||||
class="cursor-pointer"
|
||||
>
|
||||
<Eye class="mr-2 h-4 w-4" />
|
||||
View Policy
|
||||
{$t('app.roles.view_policy')}
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
onclick={() => openEditDialog(role)}
|
||||
class="cursor-pointer"
|
||||
>
|
||||
<Edit class="mr-2 h-4 w-4" />
|
||||
Edit
|
||||
{$t('app.roles.edit')}
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item
|
||||
@@ -162,7 +165,7 @@
|
||||
onclick={() => openDeleteDialog(role)}
|
||||
>
|
||||
<Trash class="mr-2 h-4 w-4" />
|
||||
Delete
|
||||
{$t('app.roles.delete')}
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
@@ -171,7 +174,8 @@
|
||||
{/each}
|
||||
{:else}
|
||||
<Table.Row>
|
||||
<Table.Cell colspan={3} class="h-24 text-center">No roles found.</Table.Cell
|
||||
<Table.Cell colspan={3} class="h-24 text-center"
|
||||
>{$t('app.roles.no_roles_found')}</Table.Cell
|
||||
>
|
||||
</Table.Row>
|
||||
{/if}
|
||||
@@ -183,9 +187,9 @@
|
||||
<Dialog.Root bind:open={isViewPolicyDialogOpen}>
|
||||
<Dialog.Content class="sm:max-w-lg">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Role Policy</Dialog.Title>
|
||||
<Dialog.Title>{$t('app.roles.role_policy')}</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
Viewing policy for role: {selectedRole?.name}
|
||||
{$t('app.roles.viewing_policy_for_role', { name: selectedRole?.name } as any)}
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<div
|
||||
@@ -199,9 +203,14 @@
|
||||
<Dialog.Root bind:open={isFormDialogOpen}>
|
||||
<Dialog.Content class="sm:max-w-lg">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>{selectedRole ? 'Edit' : 'Create'} Role</Dialog.Title>
|
||||
<Dialog.Title
|
||||
>{selectedRole ? $t('app.roles.edit') : $t('app.roles.create')}
|
||||
{$t('app.roles.role')}</Dialog.Title
|
||||
>
|
||||
<Dialog.Description>
|
||||
{selectedRole ? 'Make changes to the role here.' : 'Add a new role to the system.'}
|
||||
{selectedRole
|
||||
? $t('app.roles.edit_description')
|
||||
: $t('app.roles.create_description')}
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<RoleForm role={selectedRole} onSubmit={handleFormSubmit} />
|
||||
@@ -211,9 +220,9 @@
|
||||
<Dialog.Root bind:open={isDeleteDialogOpen}>
|
||||
<Dialog.Content class="sm:max-w-lg">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Are you sure you want to delete this role?</Dialog.Title>
|
||||
<Dialog.Title>{$t('app.roles.delete_confirmation_title')}</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
This action cannot be undone. This will permanently delete the role.
|
||||
{$t('app.roles.delete_confirmation_description')}
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<Dialog.Footer class="sm:justify-start">
|
||||
@@ -223,10 +232,14 @@
|
||||
onclick={confirmDelete}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
{#if isDeleting}Deleting...{:else}Confirm{/if}
|
||||
{#if isDeleting}
|
||||
{$t('app.roles.deleting')}...
|
||||
{:else}
|
||||
{$t('app.roles.confirm')}
|
||||
{/if}
|
||||
</Button>
|
||||
<Dialog.Close>
|
||||
<Button type="button" variant="secondary">Cancel</Button>
|
||||
<Button type="button" variant="secondary">{$t('app.roles.cancel')}</Button>
|
||||
</Dialog.Close>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
|
||||
@@ -8,20 +8,22 @@
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { setAlert } from '$lib/components/custom/alert/alert-state.svelte';
|
||||
import type { SupportedLanguage } from '@open-archiver/types';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let { data, form }: { data: PageData; form: any } = $props();
|
||||
let settings = $state(data.settings);
|
||||
let isSaving = $state(false);
|
||||
|
||||
const languageOptions: { value: SupportedLanguage; label: string }[] = [
|
||||
{ value: 'en', label: 'English' },
|
||||
{ value: 'es', label: 'Spanish' },
|
||||
{ value: 'fr', label: 'French' },
|
||||
{ value: 'de', label: 'German' },
|
||||
{ value: 'it', label: 'Italian' },
|
||||
{ value: 'pt', label: 'Portuguese' },
|
||||
{ value: 'nl', label: 'Dutch' },
|
||||
{ value: 'ja', label: 'Japanese' },
|
||||
{ value: 'en', label: '🇬🇧 English' },
|
||||
{ value: 'de', label: '🇩🇪 Deutsch' },
|
||||
{ value: 'fr', label: '🇫🇷 Français' },
|
||||
{ value: 'et', label: '🇪🇪 Eesti' },
|
||||
{ value: 'es', label: '🇪🇸 Español' },
|
||||
{ value: 'it', label: '🇮🇹 Italiano' },
|
||||
{ value: 'pt', label: '🇵🇹 Português' },
|
||||
{ value: 'nl', label: '🇳🇱 Nederlands' },
|
||||
{ value: 'ja', label: '🇯🇵 日本語' },
|
||||
];
|
||||
|
||||
const languageTriggerContent = $derived(
|
||||
@@ -52,35 +54,37 @@
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>System Settings - OpenArchiver</title>
|
||||
<title>{$t('app.system_settings.title')} - OpenArchiver</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold">System Settings</h1>
|
||||
<p class="text-muted-foreground">Manage global application settings.</p>
|
||||
<h1 class="text-2xl font-bold">{$t('app.system_settings.system_settings')}</h1>
|
||||
<p class="text-muted-foreground">{$t('app.system_settings.description')}</p>
|
||||
</div>
|
||||
|
||||
<form method="POST" class="space-y-8" onsubmit={() => (isSaving = true)}>
|
||||
<Card.Root>
|
||||
<Card.Content class="space-y-4">
|
||||
<!-- Hide language setting for now -->
|
||||
<!-- <div class="grid gap-2">
|
||||
<Label.Root class="mb-1" for="language">Language</Label.Root>
|
||||
<div class="grid gap-2">
|
||||
<Label.Root class="mb-1" for="language"
|
||||
>{$t('app.system_settings.language')}</Label.Root
|
||||
>
|
||||
<Select.Root name="language" bind:value={settings.language} type="single">
|
||||
<Select.Trigger class="w-[280px]">
|
||||
{languageTriggerContent}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Content>
|
||||
{#each languageOptions as lang}
|
||||
<Select.Item value={lang.value}>{lang.label}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label.Root class="mb-1">Default theme</Label.Root>
|
||||
<Label.Root class="mb-1">{$t('app.system_settings.default_theme')}</Label.Root>
|
||||
<RadioGroup.Root
|
||||
bind:value={settings.theme}
|
||||
name="theme"
|
||||
@@ -88,21 +92,23 @@
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<RadioGroup.Item value="light" id="light" />
|
||||
<Label.Root for="light">Light</Label.Root>
|
||||
<Label.Root for="light">{$t('app.system_settings.light')}</Label.Root>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<RadioGroup.Item value="dark" id="dark" />
|
||||
<Label.Root for="dark">Dark</Label.Root>
|
||||
<Label.Root for="dark">{$t('app.system_settings.dark')}</Label.Root>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<RadioGroup.Item value="system" id="system" />
|
||||
<Label.Root for="system">System</Label.Root>
|
||||
<Label.Root for="system">{$t('app.system_settings.system')}</Label.Root>
|
||||
</div>
|
||||
</RadioGroup.Root>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<Label.Root class="mb-1" for="supportEmail">Support Email</Label.Root>
|
||||
<Label.Root class="mb-1" for="supportEmail"
|
||||
>{$t('app.system_settings.support_email')}</Label.Root
|
||||
>
|
||||
<Input
|
||||
id="supportEmail"
|
||||
name="supportEmail"
|
||||
@@ -115,7 +121,11 @@
|
||||
</Card.Content>
|
||||
<Card.Footer class="border-t px-6 py-4">
|
||||
<Button type="submit" disabled={isSaving}>
|
||||
{#if isSaving}Saving...{:else}Save Changes{/if}
|
||||
{#if isSaving}
|
||||
{$t('app.system_settings.saving')}...
|
||||
{:else}
|
||||
{$t('app.system_settings.save_changes')}
|
||||
{/if}
|
||||
</Button>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import UserForm from '$lib/components/custom/UserForm.svelte';
|
||||
import { api } from '$lib/api.client';
|
||||
import type { User } from '@open-archiver/types';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
let users = $state(data.users);
|
||||
@@ -103,24 +104,24 @@
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>User Management - OpenArchiver</title>
|
||||
<title>{$t('app.users.title')} - OpenArchiver</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<h1 class="text-2xl font-bold">User Management</h1>
|
||||
<Button onclick={openCreateDialog}>Create New</Button>
|
||||
<h1 class="text-2xl font-bold">{$t('app.users.user_management')}</h1>
|
||||
<Button onclick={openCreateDialog}>{$t('app.users.create_new')}</Button>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border">
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.Head>Name</Table.Head>
|
||||
<Table.Head>Email</Table.Head>
|
||||
<Table.Head>Role</Table.Head>
|
||||
<Table.Head>Created At</Table.Head>
|
||||
<Table.Head class="text-right">Actions</Table.Head>
|
||||
<Table.Head>{$t('app.users.name')}</Table.Head>
|
||||
<Table.Head>{$t('app.users.email')}</Table.Head>
|
||||
<Table.Head>{$t('app.users.role')}</Table.Head>
|
||||
<Table.Head>{$t('app.users.created_at')}</Table.Head>
|
||||
<Table.Head class="text-right">{$t('app.users.actions')}</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
@@ -135,18 +136,20 @@
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button variant="ghost" class="h-8 w-8 p-0">
|
||||
<span class="sr-only">Open menu</span>
|
||||
<span class="sr-only">{$t('app.users.open_menu')}</span>
|
||||
<MoreHorizontal class="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.Label>Actions</DropdownMenu.Label>
|
||||
<DropdownMenu.Label
|
||||
>{$t('app.users.actions')}</DropdownMenu.Label
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
onclick={() => openEditDialog(user)}
|
||||
class="cursor-pointer"
|
||||
>
|
||||
<Edit class="mr-2 h-4 w-4" />
|
||||
Edit</DropdownMenu.Item
|
||||
{$t('app.users.edit')}</DropdownMenu.Item
|
||||
>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item
|
||||
@@ -154,7 +157,7 @@
|
||||
onclick={() => openDeleteDialog(user)}
|
||||
>
|
||||
<Trash class="mr-2 h-4 w-4" />
|
||||
Delete
|
||||
{$t('app.users.delete')}
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
@@ -163,7 +166,8 @@
|
||||
{/each}
|
||||
{:else}
|
||||
<Table.Row>
|
||||
<Table.Cell colspan={5} class="h-24 text-center">No users found.</Table.Cell
|
||||
<Table.Cell colspan={5} class="h-24 text-center"
|
||||
>{$t('app.users.no_users_found')}</Table.Cell
|
||||
>
|
||||
</Table.Row>
|
||||
{/if}
|
||||
@@ -175,9 +179,14 @@
|
||||
<Dialog.Root bind:open={isDialogOpen}>
|
||||
<Dialog.Content class="sm:max-w-lg">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>{selectedUser ? 'Edit' : 'Create'} User</Dialog.Title>
|
||||
<Dialog.Title
|
||||
>{selectedUser ? $t('app.users.edit') : $t('app.users.create')}
|
||||
{$t('app.users.user')}</Dialog.Title
|
||||
>
|
||||
<Dialog.Description>
|
||||
{selectedUser ? 'Make changes to the user here.' : 'Add a new user to the system.'}
|
||||
{selectedUser
|
||||
? $t('app.users.edit_description')
|
||||
: $t('app.users.create_description')}
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<UserForm {roles} user={selectedUser} onSubmit={handleFormSubmit} />
|
||||
@@ -187,10 +196,9 @@
|
||||
<Dialog.Root bind:open={isDeleteDialogOpen}>
|
||||
<Dialog.Content class="sm:max-w-lg">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Are you sure you want to delete this user?</Dialog.Title>
|
||||
<Dialog.Title>{$t('app.users.delete_confirmation_title')}</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
This action cannot be undone. This will permanently delete the user and remove their
|
||||
data from our servers.
|
||||
{$t('app.users.delete_confirmation_description')}
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<Dialog.Footer class="sm:justify-start">
|
||||
@@ -200,10 +208,14 @@
|
||||
onclick={confirmDelete}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
{#if isDeleting}Deleting...{:else}Confirm{/if}
|
||||
{#if isDeleting}
|
||||
{$t('app.users.deleting')}...
|
||||
{:else}
|
||||
{$t('app.users.confirm')}
|
||||
{/if}
|
||||
</Button>
|
||||
<Dialog.Close>
|
||||
<Button type="button" variant="secondary">Cancel</Button>
|
||||
<Button type="button" variant="secondary">{$t('app.users.cancel')}</Button>
|
||||
</Dialog.Close>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { api } from '$lib/api.client';
|
||||
import { authStore } from '$lib/stores/auth.store';
|
||||
import { setAlert } from '$lib/components/custom/alert/alert-state.svelte';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let first_name = '';
|
||||
let last_name = '';
|
||||
@@ -45,11 +46,8 @@
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Setup - Open Archiver</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Set up the initial administrator account for Open Archiver."
|
||||
/>
|
||||
<title>{$t('app.setup.title')} - Open Archiver</title>
|
||||
<meta name="description" content={$t('app.setup.description')} />
|
||||
</svelte:head>
|
||||
|
||||
<div
|
||||
@@ -67,35 +65,33 @@
|
||||
</div>
|
||||
<Card.Root class="w-full max-w-md">
|
||||
<Card.Header class="space-y-1">
|
||||
<Card.Title class="text-2xl">Welcome</Card.Title>
|
||||
<Card.Description
|
||||
>Create the first administrator account to get started.</Card.Description
|
||||
>
|
||||
<Card.Title class="text-2xl">{$t('app.setup.welcome')}</Card.Title>
|
||||
<Card.Description>{$t('app.setup.create_admin_account')}</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content class="grid gap-4">
|
||||
<form on:submit|preventDefault={handleSubmit} class="grid gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="first_name">First name</Label>
|
||||
<Label for="first_name">{$t('app.setup.first_name')}</Label>
|
||||
<Input
|
||||
id="first_name"
|
||||
type="text"
|
||||
placeholder="First name"
|
||||
placeholder={$t('app.setup.first_name')}
|
||||
bind:value={first_name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="last_name">Last name</Label>
|
||||
<Label for="last_name">{$t('app.setup.last_name')}</Label>
|
||||
<Input
|
||||
id="last_name"
|
||||
type="text"
|
||||
placeholder="Last name"
|
||||
placeholder={$t('app.setup.last_name')}
|
||||
bind:value={last_name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="email">Email</Label>
|
||||
<Label for="email">{$t('app.setup.email')}</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
@@ -105,15 +101,15 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="password">Password</Label>
|
||||
<Label for="password">{$t('app.setup.password')}</Label>
|
||||
<Input id="password" type="password" bind:value={password} required />
|
||||
</div>
|
||||
|
||||
<Button type="submit" class="w-full" disabled={isLoading}>
|
||||
{#if isLoading}
|
||||
<span>Creating Account...</span>
|
||||
<span>{$t('app.setup.creating_account')}...</span>
|
||||
{:else}
|
||||
<span>Create Account</span>
|
||||
<span>{$t('app.setup.create_account')}</span>
|
||||
{/if}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { authStore } from '$lib/stores/auth.store';
|
||||
import type { LoginResponse } from '@open-archiver/types';
|
||||
import { setAlert } from '$lib/components/custom/alert/alert-state.svelte';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
let email = '';
|
||||
let password = '';
|
||||
@@ -50,7 +51,7 @@
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Login - Open Archiver</title>
|
||||
<title>{$t('app.auth.login')} - Open Archiver</title>
|
||||
<meta name="description" content="Login to your Open Archiver account." />
|
||||
</svelte:head>
|
||||
|
||||
@@ -69,13 +70,13 @@
|
||||
</div>
|
||||
<Card.Root class="w-full max-w-md">
|
||||
<Card.Header class="space-y-1">
|
||||
<Card.Title class="text-2xl">Login</Card.Title>
|
||||
<Card.Description>Enter your email below to login to your account.</Card.Description>
|
||||
<Card.Title class="text-2xl">{$t('app.auth.login')}</Card.Title>
|
||||
<Card.Description>{$t('app.auth.login_tip')}</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content class="grid gap-4">
|
||||
<form onsubmit={handleSubmit} class="grid gap-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="email">Email</Label>
|
||||
<Label for="email">{$t('app.auth.email')}</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
@@ -85,12 +86,12 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="password">Password</Label>
|
||||
<Label for="password">{$t('app.auth.password')}</Label>
|
||||
<Input id="password" type="password" bind:value={password} required />
|
||||
</div>
|
||||
|
||||
<Button type="submit" class=" w-full" disabled={isLoading}>
|
||||
{isLoading ? 'Logging in...' : 'Login'}
|
||||
{isLoading ? $t('app.common.working') : $t('app.auth.login')}
|
||||
</Button>
|
||||
</form>
|
||||
</Card.Content>
|
||||
|
||||
@@ -6,7 +6,8 @@ export type SupportedLanguage =
|
||||
| 'it' // Italian
|
||||
| 'pt' // Portuguese
|
||||
| 'nl' // Dutch
|
||||
| 'ja'; // Japanese
|
||||
| 'ja' // Japanese
|
||||
| 'et'; // Estonian
|
||||
|
||||
export type Theme = 'light' | 'dark' | 'system';
|
||||
|
||||
|
||||
28
pnpm-lock.yaml
generated
28
pnpm-lock.yaml
generated
@@ -220,6 +220,9 @@ importers:
|
||||
svelte-persisted-store:
|
||||
specifier: ^0.12.0
|
||||
version: 0.12.0(svelte@5.35.5)
|
||||
sveltekit-i18n:
|
||||
specifier: ^2.4.2
|
||||
version: 2.4.2(svelte@5.35.5)
|
||||
tailwind-merge:
|
||||
specifier: ^3.3.1
|
||||
version: 3.3.1
|
||||
@@ -1589,6 +1592,14 @@ packages:
|
||||
svelte: ^5.0.0
|
||||
vite: ^6.0.0
|
||||
|
||||
'@sveltekit-i18n/base@1.3.7':
|
||||
resolution: {integrity: sha512-kg1kql1/ro/lIudwFiWrv949Q07gmweln87tflUZR51MNdXXzK4fiJQv5Mw50K/CdQ5BOk/dJ0WOH2vOtBI6yw==}
|
||||
peerDependencies:
|
||||
svelte: '>=3.49.0'
|
||||
|
||||
'@sveltekit-i18n/parser-default@1.1.1':
|
||||
resolution: {integrity: sha512-/gtzLlqm/sox7EoPKD56BxGZktK/syGc79EbJAPWY5KVitQD9SM0TP8yJCqDxTVPk7Lk0WJhrBGUE2Nn0f5M1w==}
|
||||
|
||||
'@swc/helpers@0.5.17':
|
||||
resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==}
|
||||
|
||||
@@ -4370,6 +4381,11 @@ packages:
|
||||
resolution: {integrity: sha512-KuRvI82rhh0RMz1EKsUJD96gZyHJ+h2+8zrwO8iqE/p/CmcNKvIItDUAeUePhuCDgtegDJmF8IKThbHIfmTgTA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
sveltekit-i18n@2.4.2:
|
||||
resolution: {integrity: sha512-hjRWn4V4DBL8JQKJoJa3MRvn6d32Zo+rWkoSP5bsQ/XIAguPdQUZJ8LMe6Nc1rST8WEVdu9+vZI3aFdKYGR3+Q==}
|
||||
peerDependencies:
|
||||
svelte: '>=3.49.0'
|
||||
|
||||
tabbable@6.2.0:
|
||||
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
|
||||
|
||||
@@ -6320,6 +6336,12 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@sveltekit-i18n/base@1.3.7(svelte@5.35.5)':
|
||||
dependencies:
|
||||
svelte: 5.35.5
|
||||
|
||||
'@sveltekit-i18n/parser-default@1.1.1': {}
|
||||
|
||||
'@swc/helpers@0.5.17':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
@@ -9308,6 +9330,12 @@ snapshots:
|
||||
magic-string: 0.30.17
|
||||
zimmerframe: 1.1.2
|
||||
|
||||
sveltekit-i18n@2.4.2(svelte@5.35.5):
|
||||
dependencies:
|
||||
'@sveltekit-i18n/base': 1.3.7(svelte@5.35.5)
|
||||
'@sveltekit-i18n/parser-default': 1.1.1
|
||||
svelte: 5.35.5
|
||||
|
||||
tabbable@6.2.0: {}
|
||||
|
||||
tailwind-merge@3.0.2: {}
|
||||
|
||||
Reference in New Issue
Block a user