[ENG-1502] I18n (#1897)

* you know, you could just work on first try

* fix extension

* configure plugin and fix few translation issues

* more

* more keys

* and more

* more keys and sort

* commit msg

* we like keys here

* end my suffering

* jk i just love keys

* key fix

* add turkish

* add german

* Entendido

* Demnächst

* Mettre une étoile sur GitHub

* 成功

* pnpm-lock

* vite plugin

* remove i18next backends

---------

Co-authored-by: Brendan Allan <brendonovich@outlook.com>
This commit is contained in:
Utku 2024-01-08 23:26:46 +03:00 committed by GitHub
parent 59532b1aac
commit a94832c1ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
105 changed files with 3551 additions and 4069 deletions

View file

@ -1,12 +1,13 @@
{
"recommendations": [
"tauri-apps.tauri-vscode",
"rust-lang.rust-analyzer",
"oscartbeaumont.rspc-vscode",
"editorconfig.editorconfig",
"bradlc.vscode-tailwindcss",
"prisma.prisma",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
"tauri-apps.tauri-vscode", // Tauri is a framework for building lightweight, secure applications with web technologies
"rust-lang.rust-analyzer", // Provides Rust language support
"oscartbeaumont.rspc-vscode", // RSPC is a Rust version of trpc.
"editorconfig.editorconfig", // EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs
"bradlc.vscode-tailwindcss", // Provides Tailwind CSS IntelliSense
"prisma.prisma", // Prisma is an open-source database toolkit
"dbaeumer.vscode-eslint", // Integrates ESLint JavaScript into VS Code
"esbenp.prettier-vscode", // Code formatter using prettier
"inlang.vs-code-extension" // Improved i18n DX (Internationalization Developer Experience)
]
}

View file

@ -1,9 +1,12 @@
import { useNavigate } from 'react-router';
import { Button } from '@sd/ui';
import { useLocale } from '~/hooks';
export const Component = () => {
const navigate = useNavigate();
const { t } = useLocale();
return (
<div className="w-full bg-app/80">
<div
@ -17,7 +20,7 @@ export const Component = () => {
</p>
<div className="flex flex-row space-x-2">
<Button variant="outline" className="mt-4" onClick={() => navigate(-1)}>
Go Back
{t('go_back')}
</Button>
</div>
</div>

View file

@ -11,7 +11,7 @@ import CreateDialog, {
useAssignItemsToTag
} from '~/app/$libraryId/settings/library/tags/CreateDialog';
import { Menu } from '~/components/Menu';
import { useOperatingSystem } from '~/hooks';
import { useLocale, useOperatingSystem } from '~/hooks';
import { useScrolled } from '~/hooks/useScrolled';
import { keybindForOs } from '~/util/keybinds';
@ -52,6 +52,8 @@ export default (props: Props) => {
const ref = useRef<HTMLDivElement>(null);
const { isScrolled } = useScrolled(ref, 10);
const { t } = useLocale();
const os = useOperatingSystem();
const keybind = keybindForOs(os);
@ -61,7 +63,7 @@ export default (props: Props) => {
<>
<Menu.Item
className="tag-menu"
label="New tag"
label={t('new_tag')}
icon={Plus}
iconProps={{ size: 15 }}
keybind={keybind([ModifierKeys.Control], ['N'])}
@ -74,8 +76,8 @@ export default (props: Props) => {
onReset={() => queryClient.invalidateQueries()}
fallbackRender={(props) => (
<EmptyContainer>
Failed to load tags
<Button onClick={() => props.resetErrorBoundary()}>Retry</Button>
{t('failed_to_load_tags')}
<Button onClick={() => props.resetErrorBoundary()}>{t('retry')}</Button>
</EmptyContainer>
)}
>
@ -88,6 +90,8 @@ export default (props: Props) => {
const Tags = ({ items, parentRef }: Props & { parentRef: RefObject<HTMLDivElement> }) => {
const { tags, tagsWithObjects } = useData({ items });
const { t } = useLocale();
// tags are sorted by assignment, and assigned tags are sorted by most recently assigned
const sortedTags = useMemo(() => {
if (!tags.data) return [];
@ -242,7 +246,7 @@ const Tags = ({ items, parentRef }: Props & { parentRef: RefObject<HTMLDivElemen
</div>
</div>
) : (
<EmptyContainer>No tags</EmptyContainer>
<EmptyContainer>{t('no_tags')}</EmptyContainer>
)}
</>
);

View file

@ -1,6 +1,7 @@
import { Copy, Scissors } from '@phosphor-icons/react';
import { useLibraryMutation } from '@sd/client';
import { ContextMenu, ModifierKeys, toast } from '@sd/ui';
import { useLocale } from '~/hooks';
import { useKeybindFactory } from '~/hooks/useKeybindFactory';
import { isNonEmpty } from '~/util';
@ -27,6 +28,8 @@ export const CutCopyItems = new ConditionalItem({
const keybind = useKeybindFactory();
const [{ path }] = useExplorerSearchParams();
const { t } = useLocale();
const copyFiles = useLibraryMutation('files.copyFiles');
const copyEphemeralFiles = useLibraryMutation('ephemeralFiles.copyFiles');
@ -46,7 +49,7 @@ export const CutCopyItems = new ConditionalItem({
return (
<>
<ContextMenu.Item
label="Cut"
label={t('cut')}
keybind={keybind([ModifierKeys.Control], ['X'])}
onClick={() => {
explorerStore.cutCopyState = {
@ -60,7 +63,7 @@ export const CutCopyItems = new ConditionalItem({
/>
<ContextMenu.Item
label="Copy"
label={t('copy')}
keybind={keybind([ModifierKeys.Control], ['C'])}
onClick={() => {
explorerStore.cutCopyState = {
@ -74,7 +77,7 @@ export const CutCopyItems = new ConditionalItem({
/>
<ContextMenu.Item
label="Duplicate"
label={t('duplicate')}
keybind={keybind([ModifierKeys.Control], ['D'])}
onClick={async () => {
try {
@ -95,7 +98,7 @@ export const CutCopyItems = new ConditionalItem({
}
} catch (error) {
toast.error({
title: 'Failed to duplicate file',
title: t('failed_to_duplicate_file'),
body: `Error: ${error}.`
});
}

View file

@ -2,7 +2,7 @@ import { Hash, Image, Package, Trash, TrashSimple } from '@phosphor-icons/react'
import { libraryClient, useLibraryMutation } from '@sd/client';
import { ContextMenu, dialogManager, ModifierKeys, toast } from '@sd/ui';
import { Menu } from '~/components/Menu';
import { useKeysMatcher, useOperatingSystem } from '~/hooks';
import { useKeysMatcher, useLocale, useOperatingSystem } from '~/hooks';
import { useKeybindFactory } from '~/hooks/useKeybindFactory';
import { useQuickRescan } from '~/hooks/useQuickRescan';
import { isNonEmpty } from '~/util';
@ -25,6 +25,7 @@ export const Delete = new ConditionalItem({
return { selectedFilePaths, selectedEphemeralPaths };
},
Component: ({ selectedFilePaths, selectedEphemeralPaths }) => {
const { t } = useLocale();
const rescan = useQuickRescan();
const os = useOperatingSystem();
const dirCount =
@ -53,7 +54,7 @@ export const Delete = new ConditionalItem({
return (
<Menu.Item
icon={Trash}
label="Delete"
label={t('delete')}
variant="danger"
keybind={
os === 'windows'
@ -112,10 +113,11 @@ export const Compress = new ConditionalItem({
},
Component: ({ selectedFilePaths: _ }) => {
const keybind = useKeybindFactory();
const { t } = useLocale();
return (
<Menu.Item
label="Compress"
label={t('compress')}
icon={Package}
keybind={keybind([ModifierKeys.Control], ['B'])}
disabled
@ -197,19 +199,26 @@ export const SecureDelete = new ConditionalItem({
return { locationId, selectedFilePaths };
},
Component: ({ locationId, selectedFilePaths }) => (
<Menu.Item
variant="danger"
label="Secure delete"
icon={TrashSimple}
onClick={() =>
dialogManager.create((dp) => (
<EraseDialog {...dp} locationId={locationId} filePaths={selectedFilePaths} />
))
}
disabled
/>
)
Component: ({ locationId, selectedFilePaths }) => {
const { t } = useLocale();
return (
<Menu.Item
variant="danger"
label={t('secure_delete')}
icon={TrashSimple}
onClick={() =>
dialogManager.create((dp) => (
<EraseDialog
{...dp}
locationId={locationId}
filePaths={selectedFilePaths}
/>
))
}
disabled
/>
);
}
});
export const ParentFolderActions = new ConditionalItem({
@ -227,6 +236,8 @@ export const ParentFolderActions = new ConditionalItem({
const generateThumbnails = useLibraryMutation('jobs.generateThumbsForLocation');
const generateLabels = useLibraryMutation('jobs.generateLabelsForLocation');
const { t } = useLocale();
return (
<>
<ContextMenu.Item
@ -238,12 +249,12 @@ export const ParentFolderActions = new ConditionalItem({
});
} catch (error) {
toast.error({
title: `Failed to rescan location`,
title: t('failed_to_rescan_location'),
body: `Error: ${error}.`
});
}
}}
label="Rescan Directory"
label={t('rescan_directory')}
icon={Package}
/>
<ContextMenu.Item
@ -256,12 +267,12 @@ export const ParentFolderActions = new ConditionalItem({
});
} catch (error) {
toast.error({
title: `Failed to generate thumbnails`,
title: t('failed_to_generate_thumbnails'),
body: `Error: ${error}.`
});
}
}}
label="Regen Thumbnails"
label={t('regen_thumbnails')}
icon={Image}
/>
<ContextMenu.Item
@ -274,12 +285,12 @@ export const ParentFolderActions = new ConditionalItem({
});
} catch (error) {
toast.error({
title: `Failed to generate labels`,
title: t('failed_to_generate_labels'),
body: `Error: ${error}.`
});
}
}}
label="Regen Labels"
label={t('regen_labels')}
icon={Hash}
/>
</>

View file

@ -3,6 +3,7 @@ import { useMemo } from 'react';
import { ExplorerItem, ObjectKind, useLibraryMutation, type ObjectKindEnum } from '@sd/client';
import { ContextMenu, toast } from '@sd/ui';
import { Menu } from '~/components/Menu';
import { useLocale } from '~/hooks';
import { isNonEmpty } from '~/util';
import AssignTagMenuItems from '../AssignTagMenuItems';
@ -21,9 +22,11 @@ export const RemoveFromRecents = new ConditionalItem({
Component: ({ selectedObjects }) => {
const removeFromRecents = useLibraryMutation('files.removeAccessTime');
const { t } = useLocale();
return (
<ContextMenu.Item
label="Remove From Recents"
label={t('remove_from_recents')}
onClick={async () => {
try {
await removeFromRecents.mutateAsync(
@ -31,7 +34,7 @@ export const RemoveFromRecents = new ConditionalItem({
);
} catch (error) {
toast.error({
title: `Failed to remove file from recents`,
title: t('failed_to_remove_file_from_recents'),
body: `Error: ${error}.`
});
}
@ -58,11 +61,14 @@ export const AssignTag = new ConditionalItem({
return { items };
},
Component: ({ items }) => (
<Menu.SubMenu label="Assign tag" icon={TagSimple}>
<AssignTagMenuItems items={items} />
</Menu.SubMenu>
)
Component: ({ items }) => {
const { t } = useLocale();
return (
<Menu.SubMenu label={t('assign_tag')} icon={TagSimple}>
<AssignTagMenuItems items={items} />
</Menu.SubMenu>
);
}
});
const ObjectConversions: Record<number, string[]> = {
@ -93,9 +99,14 @@ export const ConvertObject = new ConditionalItem({
return { kind };
},
Component: ({ kind }) => (
<Menu.SubMenu label="Convert to" icon={ArrowBendUpRight}>
{ObjectConversions[kind]?.map((ext) => <Menu.Item key={ext} label={ext} disabled />)}
</Menu.SubMenu>
)
Component: ({ kind }) => {
const { t } = useLocale();
return (
<Menu.SubMenu label={t('convert_to')} icon={ArrowBendUpRight}>
{ObjectConversions[kind]?.map((ext) => (
<Menu.Item key={ext} label={ext} disabled />
))}
</Menu.SubMenu>
);
}
});

View file

@ -3,6 +3,7 @@ import { Suspense } from 'react';
import { useLibraryContext } from '@sd/client';
import { toast } from '@sd/ui';
import { Menu } from '~/components/Menu';
import { useLocale } from '~/hooks';
import { Platform, usePlatform } from '~/util/Platform';
import { ConditionalItem } from './ConditionalItem';
@ -40,20 +41,23 @@ export default new ConditionalItem({
openFilePathWith,
getEphemeralFilesOpenWithApps,
openEphemeralFileWith
}) => (
<Menu.SubMenu label="Open with">
<Suspense>
<Items
actions={{
getFilePathOpenWithApps,
openFilePathWith,
getEphemeralFilesOpenWithApps,
openEphemeralFileWith
}}
/>
</Suspense>
</Menu.SubMenu>
)
}) => {
const { t } = useLocale();
return (
<Menu.SubMenu label={t('open_with')}>
<Suspense>
<Items
actions={{
getFilePathOpenWithApps,
openFilePathWith,
getEphemeralFilesOpenWithApps,
openEphemeralFileWith
}}
/>
</Suspense>
</Menu.SubMenu>
);
}
});
const Items = ({

View file

@ -3,7 +3,7 @@ import { useMemo } from 'react';
import { useSelector } from '@sd/client';
import { ContextMenu, ModifierKeys } from '@sd/ui';
import { Menu } from '~/components/Menu';
import { useOperatingSystem } from '~/hooks';
import { useLocale, useOperatingSystem } from '~/hooks';
import { useKeybindFactory } from '~/hooks/useKeybindFactory';
import { isNonEmpty } from '~/util';
import { usePlatform, type Platform } from '~/util/Platform';
@ -37,12 +37,14 @@ export const OpenOrDownload = new ConditionalItem({
const { doubleClick } = useViewItemDoubleClick();
const os = useOperatingSystem(true);
if (platform === 'web') return <Menu.Item label="Download" />;
const { t } = useLocale();
if (platform === 'web') return <Menu.Item label={t('download')} />;
else
return (
<>
<Menu.Item
label="Open"
label={t('open')}
keybind={keybind(os === 'windows' ? [] : [ModifierKeys.Control], [
os === 'windows' ? 'Enter' : 'O'
])}
@ -56,10 +58,11 @@ export const OpenOrDownload = new ConditionalItem({
export const OpenQuickView = () => {
const keybind = useKeybindFactory();
const { t } = useLocale();
return (
<ContextMenu.Item
label="Quick view"
label={t('quick_view')}
keybind={keybind([], [' '])}
onClick={() => (getQuickPreviewStore().open = true)}
/>
@ -75,10 +78,11 @@ export const Details = new ConditionalItem({
},
Component: () => {
const keybind = useKeybindFactory();
const { t } = useLocale();
return (
<ContextMenu.Item
label="Details"
label={t('details')}
keybind={keybind([ModifierKeys.Control], ['I'])}
// icon={Sidebar}
onClick={() => (explorerStore.showInspector = true)}
@ -100,10 +104,11 @@ export const Rename = new ConditionalItem({
Component: () => {
const keybind = useKeybindFactory();
const os = useOperatingSystem(true);
const { t } = useLocale();
return (
<ContextMenu.Item
label="Rename"
label={t('rename')}
keybind={keybind([], [os === 'windows' ? 'F2' : 'Enter'])}
onClick={() => (explorerStore.isRenaming = true)}
/>
@ -175,26 +180,31 @@ export const Deselect = new ConditionalItem({
return {};
},
Component: () => (
<ContextMenu.Item
label="Deselect"
icon={FileX}
onClick={() => {
explorerStore.cutCopyState = {
type: 'Idle'
};
}}
/>
)
Component: () => {
const { t } = useLocale();
return (
<ContextMenu.Item
label={t('deselect')}
icon={FileX}
onClick={() => {
explorerStore.cutCopyState = {
type: 'Idle'
};
}}
/>
);
}
});
export const Share = () => {
const { t } = useLocale();
return (
<>
<Menu.Item
label="Share"
label={t('share')}
icon={ShareIcon}
onClick={(e) => {
onClick={(e: { preventDefault: () => void }) => {
e.preventDefault();
navigator.share?.({

View file

@ -2,6 +2,7 @@ import { Plus } from '@phosphor-icons/react';
import { useMemo, type PropsWithChildren } from 'react';
import { ExplorerItem } from '@sd/client';
import { ContextMenu } from '@sd/ui';
import { useLocale } from '~/hooks';
import { isNonEmpty } from '~/util';
import { useExplorerContext } from '../Context';
@ -15,49 +16,52 @@ export * as FilePathItems from './FilePath/Items';
export * as ObjectItems from './Object/Items';
export * as SharedItems from './SharedItems';
const Items = ({ children }: PropsWithChildren) => (
<>
<Conditional items={[SharedItems.OpenOrDownload]} />
<SharedItems.OpenQuickView />
const Items = ({ children }: PropsWithChildren) => {
const { t } = useLocale();
return (
<>
<Conditional items={[SharedItems.OpenOrDownload]} />
<SharedItems.OpenQuickView />
<SeparatedConditional items={[SharedItems.Details]} />
<SeparatedConditional items={[SharedItems.Details]} />
<SeparatedConditional
items={[
SharedItems.RevealInNativeExplorer,
SharedItems.Rename,
FilePathItems.CutCopyItems,
SharedItems.Deselect
]}
/>
<SeparatedConditional
items={[
SharedItems.RevealInNativeExplorer,
SharedItems.Rename,
FilePathItems.CutCopyItems,
SharedItems.Deselect
]}
/>
{children}
{children}
<ContextMenu.Separator />
<SharedItems.Share />
<ContextMenu.Separator />
<SharedItems.Share />
<SeparatedConditional items={[ObjectItems.AssignTag]} />
<SeparatedConditional items={[ObjectItems.AssignTag]} />
<Conditional
items={[
FilePathItems.CopyAsPath,
FilePathItems.Crypto,
FilePathItems.Compress,
ObjectItems.ConvertObject,
FilePathItems.ParentFolderActions,
FilePathItems.SecureDelete
]}
>
{(items) => (
<ContextMenu.SubMenu label="More actions..." icon={Plus}>
{items}
</ContextMenu.SubMenu>
)}
</Conditional>
<Conditional
items={[
FilePathItems.CopyAsPath,
FilePathItems.Crypto,
FilePathItems.Compress,
ObjectItems.ConvertObject,
FilePathItems.ParentFolderActions,
FilePathItems.SecureDelete
]}
>
{(items) => (
<ContextMenu.SubMenu label={t('more_actions')} icon={Plus}>
{items}
</ContextMenu.SubMenu>
)}
</Conditional>
<SeparatedConditional items={[FilePathItems.Delete]} />
</>
);
<SeparatedConditional items={[FilePathItems.Delete]} />
</>
);
};
export default (props: PropsWithChildren<{ items?: ExplorerItem[]; custom?: boolean }>) => {
const explorer = useExplorerContext();

View file

@ -1,13 +1,16 @@
import { ClipboardText } from '@phosphor-icons/react';
import { toast } from '@sd/ui';
import { Menu } from '~/components/Menu';
import { useLocale } from '~/hooks';
export const CopyAsPathBase = (
props: { path: string } | { getPath: () => Promise<string | null> }
) => {
const { t } = useLocale();
return (
<Menu.Item
label="Copy as path"
label={t('copy_as_path')}
icon={ClipboardText}
onClick={async () => {
try {
@ -23,7 +26,7 @@ export const CopyAsPathBase = (
navigator.clipboard.writeText(path);
} catch (error) {
toast.error({
title: `Failed to copy file path`,
title: t('failed_to_copy_file_path'),
body: `Error: ${error}.`
});
}

View file

@ -1,7 +1,9 @@
import { ReactNode } from 'react';
import { ExplorerLayout } from '@sd/client';
import i18n from '~/app/I18n';
import { Icon } from '~/components';
import DismissibleNotice from '~/components/DismissibleNotice';
import { useLocale } from '~/hooks';
import { dismissibleNoticeStore } from '~/hooks/useDismissibleNoticeStore';
import { useExplorerContext } from './Context';
@ -39,29 +41,28 @@ interface Notice {
const notices = {
grid: {
key: 'gridView',
title: 'Grid View',
description:
"Get a visual overview of your files with Grid View. This view displays your files and folders as thumbnail images, making it easy to quickly identify the file you're looking for.",
title: i18n.t('grid_view'),
description: i18n.t('grid_view_notice_description'),
icon: <CollectionIcon />
},
list: {
key: 'listView',
title: 'List View',
description:
'Easily navigate through your files and folders with List View. This view displays your files in a simple, organized list format, allowing you to quickly locate and access the files you need.',
title: i18n.t('list_view'),
description: i18n.t('list_view_notice_description'),
icon: <CollectionIcon />
},
media: {
key: 'mediaView',
title: 'Media View',
description:
'Discover photos and videos easily, Media View will show results starting at the current location including sub directories.',
description: i18n.t('media_view_notice_description'),
icon: <MediaViewIcon />
}
// columns: undefined
} satisfies Record<ExplorerLayout, Notice | undefined>;
export default () => {
const { t } = useLocale();
const settings = useExplorerContext().useSettingsSnapshot();
const notice = notices[settings.layoutMode];
@ -70,11 +71,7 @@ export default () => {
return (
<DismissibleNotice
title={
<>
<span className="font-normal">Meet</span> {notice.title}
</>
}
title={<span className="font-normal">{t('meet_title', { title: notice.title })}</span>}
icon={notice.icon}
description={notice.description}
className="m-5"

View file

@ -1,6 +1,7 @@
import { useLibraryMutation, useZodForm } from '@sd/client';
import { CheckBox, Dialog, Tooltip, useDialog, UseDialogProps } from '@sd/ui';
import { Icon } from '~/components';
import { useLocale } from '~/hooks';
interface Props extends UseDialogProps {
indexedArgs?: {
@ -43,6 +44,7 @@ function getWording(dirCount: number, fileCount: number) {
}
export default (props: Props) => {
const { t } = useLocale();
const deleteFile = useLibraryMutation('files.deleteFiles');
const deleteEphemeralFile = useLibraryMutation('ephemeralFiles.deleteFiles');
@ -51,9 +53,10 @@ export default (props: Props) => {
const { type, prefix } = getWording(dirCount, fileCount);
const description = `Warning: This will delete your ${type} forever, we don't have a trash can yet...`;
const icon = type === 'file' || type === 'files' ? 'Document' : 'Folder';
const description = t('delete_warning', { type });
return (
<Dialog
form={form}
@ -75,14 +78,14 @@ export default (props: Props) => {
})}
icon={<Icon theme="light" name={icon} size={28} />}
dialog={useDialog(props)}
title={'Delete ' + prefix + ' ' + type}
title={t('delete_dialog_title', { prefix, type })}
description={description}
loading={deleteFile.isLoading}
ctaLabel="Delete"
ctaLabel={t('delete')}
ctaDanger
className="w-[200px]"
>
<Tooltip label="Coming soon">
<Tooltip label={t('coming_soon')}>
<div className="flex items-center pt-2 opacity-50">
<CheckBox disabled className="!mt-0" />
<p className="text-sm text-ink-dull">

View file

@ -1,6 +1,7 @@
import { useState } from 'react';
import { FilePath, useLibraryMutation, useZodForm } from '@sd/client';
import { Dialog, Slider, useDialog, UseDialogProps, z } from '@sd/ui';
import { useLocale } from '~/hooks';
interface Props extends UseDialogProps {
locationId: number;
@ -12,6 +13,7 @@ const schema = z.object({
});
export default (props: Props) => {
const { t } = useLocale();
const eraseFile = useLibraryMutation('files.eraseFiles');
const form = useZodForm({
@ -34,13 +36,13 @@ export default (props: Props) => {
})
)}
dialog={useDialog(props)}
title="Erase a file"
description="Configure your erasure settings."
title={t('erase_a_file')}
description={t('erase_a_file_description')}
loading={eraseFile.isLoading}
ctaLabel="Erase"
ctaLabel={t('erase')}
>
<div className="mt-2 flex flex-col">
<span className="text-xs font-bold"># of passes</span>
<span className="text-xs font-bold">{t('number_of_passes')}</span>
<div className="flex flex-row space-x-2">
<div className="relative mt-2 flex grow">
@ -60,7 +62,7 @@ export default (props: Props) => {
</div>
</div>
<p>TODO: checkbox for "erase all matching files" (only if a file is selected)</p>
{/* <p>TODO: checkbox for "erase all matching files" (only if a file is selected)</p> */}
</Dialog>
);
};

View file

@ -10,7 +10,9 @@ import {
type VideoHTMLAttributes
} from 'react';
import { getItemFilePath, useLibraryContext } from '@sd/client';
import i18n from '~/app/I18n';
import { PDFViewer, TextViewer } from '~/components';
import { useLocale } from '~/hooks';
import { pdfViewerEnabled } from '~/util/pdfViewer';
import { usePlatform } from '~/util/Platform';
@ -152,7 +154,7 @@ const ORIGINAL_RENDERERS: {
autoPlay
className="absolute left-2/4 top-full w-full -translate-x-1/2 translate-y-[-150%]"
>
<p>Audio preview is not supported.</p>
<p>{i18n.t('audio_preview_not_supported')}</p>
</audio>
)}
</>
@ -188,6 +190,8 @@ const Video = ({ paused, blackBars, blackBarsSize, className, ...props }: VideoP
const size = useSize(ref);
const { style: blackBarsStyle } = useBlackBars(size, blackBarsSize);
const { t } = useLocale();
useEffect(() => {
if (!ref.current) return;
paused ? ref.current.pause() : ref.current.play();
@ -217,7 +221,7 @@ const Video = ({ paused, blackBars, blackBarsSize, className, ...props }: VideoP
className={clsx(blackBars && size.width === 0 && 'invisible', className)}
{...props}
>
<p>Video preview is not supported.</p>
<p>{t('video_preview_not_supported')}</p>
</video>
);
};

View file

@ -12,6 +12,7 @@ import {
useUnitFormatStore
} from '@sd/client';
import { Accordion } from '~/components';
import { useLocale } from '~/hooks';
import { Platform, usePlatform } from '~/util/Platform';
import { explorerStore } from '../store';
@ -98,19 +99,20 @@ const UrlMetadataValue = (props: { text: string; url: string; platform: Platform
</a>
);
const orientations = {
Normal: 'Normal',
MirroredHorizontal: 'Horizontally mirrored',
MirroredHorizontalAnd90CW: 'Mirrored horizontally and rotated 90° clockwise',
MirroredHorizontalAnd270CW: 'Mirrored horizontally and rotated 270° clockwise',
MirroredVertical: 'Vertically mirrored',
CW90: 'Rotated 90° clockwise',
CW180: 'Rotated 180° clockwise',
CW270: 'Rotated 270° clockwise'
};
// const orientations = {
// Normal: 'Normal',
// MirroredHorizontal: 'Horizontally mirrored',
// MirroredHorizontalAnd90CW: 'Mirrored horizontally and rotated 90° clockwise',
// MirroredHorizontalAnd270CW: 'Mirrored horizontally and rotated 270° clockwise',
// MirroredVertical: 'Vertically mirrored',
// CW90: 'Rotated 90° clockwise',
// CW180: 'Rotated 180° clockwise',
// CW270: 'Rotated 270° clockwise'
// };
const MediaData = ({ data }: Props) => {
const platform = usePlatform();
const { t } = useLocale();
const coordinatesFormat = useUnitFormatStore().coordinatesFormat;
const showMoreInfo = useSelector(explorerStore, (s) => s.showMoreInfo);
@ -120,7 +122,7 @@ const MediaData = ({ data }: Props) => {
isOpen={showMoreInfo}
onToggle={(isOpen) => (explorerStore.showMoreInfo = isOpen)}
variant="apple"
title="More info"
title={t('more_info')}
>
<MetaData
label="Date"

View file

@ -47,7 +47,7 @@ import {
import { Button, Divider, DropdownMenu, toast, Tooltip, tw } from '@sd/ui';
import { LibraryIdParamsSchema } from '~/app/route-schemas';
import { Folder, Icon } from '~/components';
import { useZodRouteParams } from '~/hooks';
import { useLocale, useZodRouteParams } from '~/hooks';
import { isNonEmpty } from '~/util';
import { useExplorerContext } from '../Context';
@ -175,6 +175,8 @@ export const SingleItemMetadata = ({ item }: { item: ExplorerItem }) => {
let filePathData: FilePath | FilePathWithObject | null = null;
let ephemeralPathData: NonIndexedPathItem | null = null;
const { t } = useLocale();
const result = useLibraryQuery(['locations.list']);
useNodes(result.data?.nodes);
const locations = useCache(result.data?.items);
@ -275,16 +277,16 @@ export const SingleItemMetadata = ({ item }: { item: ExplorerItem }) => {
{objectData && (
<div className="mx-3 mb-0.5 mt-1 flex flex-row space-x-0.5 text-ink">
<Tooltip label="Favorite">
<Tooltip label={t('favorite')}>
<FavoriteButton data={objectData} />
</Tooltip>
<Tooltip label="Encrypt">
<Tooltip label={t('encrypt')}>
<Button size="icon">
<Lock className="h-[18px] w-[18px]" />
</Button>
</Tooltip>
<Tooltip label="Share">
<Tooltip label={t('share')}>
<Button size="icon">
<Link className="h-[18px] w-[18px]" />
</Button>
@ -295,23 +297,27 @@ export const SingleItemMetadata = ({ item }: { item: ExplorerItem }) => {
<Divider />
<MetaContainer>
<MetaData icon={Cube} label="Size" value={`${size}`} />
<MetaData icon={Cube} label={t('size')} value={`${size}`} />
<MetaData icon={Clock} label="Created" value={formatDate(dateCreated)} />
<MetaData icon={Clock} label={t('created')} value={formatDate(dateCreated)} />
<MetaData icon={Eraser} label="Modified" value={formatDate(dateModified)} />
<MetaData icon={Eraser} label={t('modified')} value={formatDate(dateModified)} />
{ephemeralPathData != null || (
<MetaData icon={Barcode} label="Indexed" value={formatDate(dateIndexed)} />
<MetaData icon={Barcode} label={t('indexed')} value={formatDate(dateIndexed)} />
)}
{ephemeralPathData != null || (
<MetaData icon={FolderOpen} label="Accessed" value={formatDate(dateAccessed)} />
<MetaData
icon={FolderOpen}
label={t('accessed')}
value={formatDate(dateAccessed)}
/>
)}
<MetaData
icon={Path}
label="Path"
label={t('path')}
value={fullPath}
onClick={() => {
if (fullPath) {
@ -324,7 +330,7 @@ export const SingleItemMetadata = ({ item }: { item: ExplorerItem }) => {
{fileLocations.length > 0 && (
<MetaContainer>
<MetaTitle>Locations</MetaTitle>
<MetaTitle>{t('locations')}</MetaTitle>
<div className="flex flex-wrap gap-2">
{fileLocations.map((location) => (
<NavLink to={`/${libraryId}/location/${location.id}`} key={location.id}>
@ -367,7 +373,7 @@ export const SingleItemMetadata = ({ item }: { item: ExplorerItem }) => {
{item.type === 'Object' ||
(item.type === 'Path' && (
<DropdownMenu.Root
trigger={<PlaceholderPill>Add Tag</PlaceholderPill>}
trigger={<PlaceholderPill>{t('add_tag')}</PlaceholderPill>}
side="left"
sideOffset={5}
alignOffset={-10}
@ -384,17 +390,17 @@ export const SingleItemMetadata = ({ item }: { item: ExplorerItem }) => {
<Divider />
<MetaContainer>
<MetaData icon={Snowflake} label="Content ID" value={casId} />
<MetaData icon={Snowflake} label={t('content_id')} value={casId} />
{integrityChecksum && (
<MetaData
icon={CircleWavyCheck}
label="Checksum"
label={t('checksum')}
value={integrityChecksum}
/>
)}
<MetaData icon={Hash} label="Object ID" value={pubId} />
<MetaData icon={Hash} label={t('object_id')} value={pubId} />
</MetaContainer>
</>
)}
@ -491,21 +497,31 @@ const MultiItemMetadata = ({ items }: { items: ExplorerItem[] }) => {
[items, getDate]
);
const { t } = useLocale();
const onlyNonIndexed = metadata.types.has('NonIndexedPath') && metadata.types.size === 1;
return (
<>
<MetaContainer>
<MetaData icon={Cube} label="Size" value={`${byteSize(metadata.size)}`} />
<MetaData icon={Clock} label="Created" value={formatDate(metadata.created)} />
<MetaData icon={Eraser} label="Modified" value={formatDate(metadata.modified)} />
<MetaData icon={Cube} label={t('size')} value={`${byteSize(metadata.size)}`} />
<MetaData icon={Clock} label={t('created')} value={formatDate(metadata.created)} />
<MetaData
icon={Eraser}
label={t('modified')}
value={formatDate(metadata.modified)}
/>
{onlyNonIndexed || (
<MetaData icon={Barcode} label="Indexed" value={formatDate(metadata.indexed)} />
<MetaData
icon={Barcode}
label={t('indexed')}
value={formatDate(metadata.indexed)}
/>
)}
{onlyNonIndexed || (
<MetaData
icon={FolderOpen}
label="Accessed"
label={t('accessed')}
value={formatDate(metadata.accessed)}
/>
)}
@ -564,7 +580,7 @@ const MultiItemMetadata = ({ items }: { items: ExplorerItem[] }) => {
{isNonEmpty(selectedObjects) && (
<DropdownMenu.Root
trigger={<PlaceholderPill>Add Tag</PlaceholderPill>}
trigger={<PlaceholderPill>{t('add_tag')}</PlaceholderPill>}
side="left"
sideOffset={5}
alignOffset={-10}

View file

@ -1,6 +1,8 @@
import { RadixCheckbox, Select, SelectOption, Slider, tw, z } from '@sd/ui';
import { getExplorerLayoutStore, useExplorerLayoutStore } from '~/../packages/client/src';
import i18n from '~/app/I18n';
import { SortOrderSchema } from '~/app/route-schemas';
import { useLocale } from '~/hooks';
import { useExplorerContext } from './Context';
import { createOrdering, getOrderingDirection, orderingKey } from './store';
@ -8,6 +10,8 @@ import { createOrdering, getOrderingDirection, orderingKey } from './store';
const Subheading = tw.div`text-ink-dull mb-1 text-xs font-medium`;
export default () => {
const { t } = useLocale();
const explorer = useExplorerContext();
const layoutStore = useExplorerLayoutStore();
const settings = explorer.useSettingsSnapshot();
@ -16,7 +20,7 @@ export default () => {
<div className="flex w-80 flex-col gap-4 p-4">
{(settings.layoutMode === 'grid' || settings.layoutMode === 'media') && (
<div>
<Subheading>Item size</Subheading>
<Subheading>{t('item_size')}</Subheading>
{settings.layoutMode === 'grid' ? (
<Slider
onValueChange={(value) => {
@ -44,7 +48,7 @@ export default () => {
{settings.layoutMode === 'grid' && (
<div>
<Subheading>Gap</Subheading>
<Subheading>{t('grid_gap')}</Subheading>
<Slider
onValueChange={([val]) => {
if (val) explorer.settingsStore.gridGap = val;
@ -60,7 +64,7 @@ export default () => {
{(settings.layoutMode === 'grid' || settings.layoutMode === 'media') && (
<div className="grid grid-cols-2 gap-2">
<div className="flex flex-col">
<Subheading>Sort by</Subheading>
<Subheading>{t('sort_by')}</Subheading>
<Select
value={settings.order ? orderingKey(settings.order) : 'none'}
size="sm"
@ -76,7 +80,7 @@ export default () => {
);
}}
>
<SelectOption value="none">None</SelectOption>
<SelectOption value="none">{t('none')}</SelectOption>
{explorer.orderingKeys?.options.map((option) => (
<SelectOption key={option.value} value={option.value}>
{option.description}
@ -86,7 +90,7 @@ export default () => {
</div>
<div className="flex flex-col">
<Subheading>Direction</Subheading>
<Subheading>{t('direction')}</Subheading>
<Select
value={settings.order ? getOrderingDirection(settings.order) : 'Asc'}
size="sm"
@ -112,11 +116,11 @@ export default () => {
)}
<div>
<Subheading>Explorer</Subheading>
<Subheading>{t('explorer')}</Subheading>
<div className="grid grid-cols-2 gap-y-1">
<RadixCheckbox
checked={layoutStore.showPathBar}
label="Show Path Bar"
label={t('show_path_bar')}
name="showPathBar"
onCheckedChange={(value) => {
if (typeof value !== 'boolean') return;
@ -127,7 +131,7 @@ export default () => {
{settings.layoutMode === 'grid' && (
<RadixCheckbox
checked={settings.showBytesInGridView}
label="Show Object size"
label={t('show_object_size')}
name="showBytesInGridView"
onCheckedChange={(value) => {
if (typeof value !== 'boolean') return;
@ -139,7 +143,7 @@ export default () => {
<RadixCheckbox
checked={settings.showHiddenFiles}
label="Show Hidden Files"
label={t('show_hidden_files')}
name="showHiddenFiles"
onCheckedChange={(value) => {
if (typeof value !== 'boolean') return;
@ -150,7 +154,7 @@ export default () => {
{settings.layoutMode === 'media' && (
<RadixCheckbox
checked={settings.mediaAspectSquare}
label="Square Thumbnails"
label={t('square_thumbnails')}
name="mediaAspectSquare"
onCheckedChange={(value) => {
if (typeof value !== 'boolean') return;
@ -164,7 +168,7 @@ export default () => {
{settings.layoutMode === 'media' && (
<div>
<Subheading>Media View Context</Subheading>
<Subheading>{t('media_view_context')}</Subheading>
<Select
className="w-full"
value={
@ -187,7 +191,7 @@ export default () => {
)}
<div>
<Subheading>Double click action</Subheading>
<Subheading>{t('double_click_action')}</Subheading>
<Select
className="w-full"
value={settings.openOnDoubleClick}
@ -207,11 +211,11 @@ export default () => {
};
const doubleClickActions = z.union([
z.literal('openFile').describe('Open File'),
z.literal('quickPreview').describe('Quick Preview')
z.literal('openFile').describe(i18n.t('open_file')),
z.literal('quickPreview').describe(i18n.t('quick_preview'))
]);
const mediaViewContextActions = z.union([
z.literal('withDescendants').describe('Current Directory With Descendants'),
z.literal('withoutDescendants').describe('Current Directory')
z.literal('withDescendants').describe(i18n.t('current_directory_with_descendants')),
z.literal('withoutDescendants').describe(i18n.t('current_directory'))
]);

View file

@ -11,7 +11,7 @@ import {
import { PropsWithChildren } from 'react';
import { useLibraryMutation, useSelector } from '@sd/client';
import { ContextMenu as CM, ModifierKeys, toast } from '@sd/ui';
import { useOperatingSystem } from '~/hooks';
import { useLocale, useOperatingSystem } from '~/hooks';
import { useQuickRescan } from '~/hooks/useQuickRescan';
import { keybindForOs } from '~/util/keybinds';
@ -58,13 +58,15 @@ export default (props: PropsWithChildren) => {
}
});
const { t } = useLocale();
return (
<CM.Root trigger={props.children}>
{(parent?.type === 'Location' || parent?.type === 'Ephemeral') &&
cutCopyState.type !== 'Idle' ? (
<>
<CM.Item
label="Paste"
label={t('paste')}
keybind={keybind([ModifierKeys.Control], ['V'])}
onClick={async () => {
const path = currentPath ?? '/';
@ -130,7 +132,7 @@ export default (props: PropsWithChildren) => {
/>
<CM.Item
label="Deselect"
label={t('deselect')}
onClick={() => {
explorerStore.cutCopyState = {
type: 'Idle'
@ -143,7 +145,7 @@ export default (props: PropsWithChildren) => {
</>
) : (
<CM.Item
label="New folder"
label={t('new_folder')}
icon={FolderPlus}
onClick={() => {
if (parent?.type === 'Location') {
@ -163,7 +165,7 @@ export default (props: PropsWithChildren) => {
)}
<CM.Item
label="Share"
label={t('share')}
icon={Share}
onClick={(e) => {
e.preventDefault();
@ -182,7 +184,7 @@ export default (props: PropsWithChildren) => {
<RevealInNativeExplorerBase
items={[{ Location: { id: parent.location.id } }]}
/>
<CM.SubMenu label="More actions...">
<CM.SubMenu label={t('more_actions')}>
<CopyAsPathBase path={`${parent.location.path}${currentPath ?? ''}`} />
<CM.Item
@ -194,12 +196,12 @@ export default (props: PropsWithChildren) => {
});
} catch (error) {
toast.error({
title: `Failed to re-index location`,
title: t('failed_to_reindex_location'),
body: `Error: ${error}.`
});
}
}}
label="Re-index"
label={t('reindex')}
icon={Repeat}
/>
@ -213,12 +215,12 @@ export default (props: PropsWithChildren) => {
});
} catch (error) {
toast.error({
title: `Failed to generate thumbnails`,
title: t('failed_to_generate_thumbnails'),
body: `Error: ${error}.`
});
}
}}
label="Regen Thumbnails"
label={t('regen_thumbnails')}
icon={Image}
/>
@ -232,12 +234,12 @@ export default (props: PropsWithChildren) => {
});
} catch (error) {
toast.error({
title: `Failed to generate labels`,
title: t('failed_to_generate_labels'),
body: `Error: ${error}.`
});
}
}}
label="Regen Labels"
label={t('regen_labels')}
icon={Hash}
/>
@ -250,12 +252,12 @@ export default (props: PropsWithChildren) => {
});
} catch (error) {
toast.error({
title: `Failed to generate checksum`,
title: t('failed_to_generate_checksum'),
body: `Error: ${error}.`
});
}
}}
label="Generate Checksums"
label={t('generate_checksums')}
icon={ShieldCheck}
/>
</CM.SubMenu>

View file

@ -35,7 +35,7 @@ import {
useZodForm
} from '@sd/client';
import { DropdownMenu, Form, toast, ToastMessage, Tooltip, z } from '@sd/ui';
import { useIsDark, useOperatingSystem, useShortcut } from '~/hooks';
import { useIsDark, useLocale, useOperatingSystem, useShortcut } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import { useExplorerContext } from '../Context';
@ -83,6 +83,8 @@ export const QuickPreview = () => {
const [isRenaming, setIsRenaming] = useState<boolean>(false);
const [newName, setNewName] = useState<string | null>(null);
const { t } = useLocale();
const items = useMemo(() => {
if (!open || !explorer.items || explorer.selectedItems.size === 0) return [];
@ -276,7 +278,7 @@ export const QuickPreview = () => {
)}
>
<div className="flex flex-1">
<Tooltip label="Close">
<Tooltip label={t('close')}>
<Dialog.Close asChild>
<IconButton>
<X weight="bold" />
@ -286,7 +288,7 @@ export const QuickPreview = () => {
{items.length > 1 && (
<div className="ml-2 flex">
<Tooltip label="Back">
<Tooltip label={t('back')}>
<IconButton
disabled={!items[itemIndex - 1]}
onClick={() =>
@ -298,7 +300,7 @@ export const QuickPreview = () => {
</IconButton>
</Tooltip>
<Tooltip label="Forward">
<Tooltip label={t('forward')}>
<IconButton
disabled={!items[itemIndex + 1]}
onClick={() =>
@ -407,7 +409,7 @@ export const QuickPreview = () => {
<DropdownMenu.Root
trigger={
<div className="flex">
<Tooltip label="More">
<Tooltip label={t('more')}>
<IconButton>
<DotsThree size={20} weight="bold" />
</IconButton>
@ -427,7 +429,7 @@ export const QuickPreview = () => {
/>
<DropdownMenu.Item
label="Rename"
label={t('rename')}
onClick={() => name && setIsRenaming(true)}
/>
@ -446,7 +448,7 @@ export const QuickPreview = () => {
>
{(items) => (
<DropdownMenu.SubMenu
label="More actions..."
label={t('more_actions')}
icon={Plus}
>
{items}
@ -460,7 +462,7 @@ export const QuickPreview = () => {
</ExplorerContextMenu>
</DropdownMenu.Root>
<Tooltip label="Show slider">
<Tooltip label={t('show_slider')}>
<IconButton
onClick={() =>
(getExplorerLayoutStore().showImageSlider =
@ -479,7 +481,7 @@ export const QuickPreview = () => {
</IconButton>
</Tooltip>
<Tooltip label="Show details">
<Tooltip label={t('show_details')}>
<IconButton
onClick={() => setShowMetadata(!showMetadata)}
active={showMetadata}
@ -501,7 +503,7 @@ export const QuickPreview = () => {
onError={(type, error) =>
type.variant === 'original' &&
setThumbErrorToast({
title: 'Error loading original file',
title: t('error_loading_original_file'),
body: error.message
})
}

View file

@ -1,7 +1,7 @@
import { useLibraryContext } from '@sd/client';
import { ModifierKeys } from '@sd/ui';
import { Menu } from '~/components/Menu';
import { useOperatingSystem } from '~/hooks';
import { useLocale, useOperatingSystem } from '~/hooks';
import { useKeybindFactory } from '~/hooks/useKeybindFactory';
import { NonEmptyArray } from '~/util';
import { Platform, usePlatform } from '~/util/Platform';
@ -18,6 +18,7 @@ export type RevealItems = NonEmptyArray<
export const RevealInNativeExplorerBase = (props: { items: RevealItems }) => {
const os = useOperatingSystem();
const keybind = useKeybindFactory();
const { t } = useLocale();
const { library } = useLibraryContext();
const { revealItems } = usePlatform();
if (!revealItems) return null;
@ -26,7 +27,7 @@ export const RevealInNativeExplorerBase = (props: { items: RevealItems }) => {
return (
<Menu.Item
label={`Reveal in ${osFileBrowserName}`}
label={t('revel_in_browser', { browser: osFileBrowserName })}
keybind={keybind([ModifierKeys.Control], ['Y'])}
onClick={() => revealItems(library.uuid, props.items)}
/>

View file

@ -14,7 +14,7 @@ import { useMemo } from 'react';
import { useDocumentEventListener } from 'rooks';
import { ExplorerLayout, useSelector } from '@sd/client';
import { toast } from '@sd/ui';
import { useKeyMatcher } from '~/hooks';
import { useKeyMatcher, useLocale } from '~/hooks';
import { KeyManager } from '../KeyManager';
import { Spacedrop, SpacedropButton } from '../Spacedrop';
@ -38,6 +38,8 @@ export const useExplorerTopBarOptions = () => {
const controlIcon = useKeyMatcher('Meta').icon;
const settings = explorer.useSettingsSnapshot();
const { t } = useLocale();
const viewOptions = useMemo(
() =>
(Object.keys(explorer.layouts) as ExplorerLayout[]).reduce(
@ -48,7 +50,7 @@ export const useExplorerTopBarOptions = () => {
const option = {
layout,
toolTipLabel: `${layout} view`,
toolTipLabel: t(`${layout}_view`),
icon: <Icon className={TOP_BAR_ICON_STYLE} />,
keybinds: [controlIcon, (i + 1).toString()],
topBarActive:
@ -66,7 +68,8 @@ export const useExplorerTopBarOptions = () => {
explorer.isLoadingPreferences,
explorer.layouts,
explorer.settingsStore,
settings.layoutMode
settings.layoutMode,
t
]
);

View file

@ -1,4 +1,3 @@
import { UseInfiniteQueryResult } from '@tanstack/react-query';
import { useCallback, useEffect, useMemo, useRef, useState, type RefObject } from 'react';
import { useDebouncedCallback } from 'use-debounce';
import { proxy, snapshot, subscribe, useSnapshot } from 'valtio';

View file

@ -1,15 +1,11 @@
import { useDndMonitor } from '@dnd-kit/core';
import { useState } from 'react';
import {
ExplorerItem,
getIndexedItemFilePath,
getItemFilePath,
libraryClient,
useLibraryMutation,
useZodForm
useLibraryMutation
} from '@sd/client';
import { Dialog, RadixCheckbox, useDialog, UseDialogProps } from '@sd/ui';
import { Icon } from '~/components';
import { isNonEmptyObject } from '~/util';
import { useAssignItemsToTag } from '../settings/library/tags/CreateDialog';
@ -172,40 +168,42 @@ export const useExplorerDnd = () => {
});
};
interface DndNoticeProps extends UseDialogProps {
count: number;
path: string;
onConfirm: (val: { dismissNotice: boolean }) => void;
}
// interface DndNoticeProps extends UseDialogProps {
// count: number;
// path: string;
// onConfirm: (val: { dismissNotice: boolean }) => void;
// }
const DndNotice = (props: DndNoticeProps) => {
const form = useZodForm();
const [dismissNotice, setDismissNotice] = useState(false);
// const DndNotice = (props: DndNoticeProps) => {
// const form = useZodForm();
// const [dismissNotice, setDismissNotice] = useState(false);
return (
<Dialog
form={form}
onSubmit={form.handleSubmit(() => props.onConfirm({ dismissNotice: dismissNotice }))}
dialog={useDialog(props)}
title="Move Files"
icon={<Icon name="MoveLocation" size={28} />}
description={
<span className="break-all">
Are you sure you want to move {props.count} file{props.count > 1 ? 's' : ''} to{' '}
{props.path}?
</span>
}
ctaDanger
ctaLabel="Continue"
closeLabel="Cancel"
buttonsSideContent={
<RadixCheckbox
label="Don't show again"
name="ephemeral-alert-notice"
checked={dismissNotice}
onCheckedChange={(val) => typeof val === 'boolean' && setDismissNotice(val)}
/>
}
/>
);
};
// const { t } = useLocale();
// return (
// <Dialog
// form={form}
// onSubmit={form.handleSubmit(() => props.onConfirm({ dismissNotice: dismissNotice }))}
// dialog={useDialog(props)}
// title={t('move_files')}
// icon={<Icon name="MoveLocation" size={28} />}
// description={
// <span className="break-all">
// Are you sure you want to move {props.count} file{props.count > 1 ? 's' : ''} to{' '}
// {props.path}?
// </span>
// }
// ctaDanger
// ctaLabel={t('continue')}
// closeLabel={t('cancel')}
// buttonsSideContent={
// <RadixCheckbox
// label={t('dont_show_again')}
// name="ephemeral-alert-notice"
// checked={dismissNotice}
// onCheckedChange={(val) => typeof val === 'boolean' && setDismissNotice(val)}
// />
// }
// />
// );
// };

View file

@ -1,8 +1,9 @@
/* eslint-disable tailwindcss/classnames-order */
import { Button, Tooltip } from '@sd/ui';
import { Icon } from '~/components';
import { useLocale } from '~/hooks';
export function KeyManager() {
const { t } = useLocale();
// const isUnlocked = useLibraryQuery(['keys.isUnlocked']);
// const isSetup = useLibraryQuery(['keys.isSetup']);
@ -10,14 +11,13 @@ export function KeyManager() {
<div className="flex h-full max-w-[300px] flex-col">
<div className="flex w-full flex-col items-center p-4">
<Icon name="Keys" size={56} />
<span className="text-lg font-bold">Key Manager</span>
<span className="text-lg font-bold">{t('key_manager')}</span>
<span className="mt-2 text-center text-ink-dull">
Create encryption keys, mount and unmount your keys to see files decrypted on
the fly.
{t('key_manager_description')}
</span>
<Tooltip className="w-full" label="Coming soon!">
<Button disabled className="mt-4 w-full" variant="accent">
Set up
{t('setup')}
</Button>
</Tooltip>
</div>

View file

@ -2,6 +2,7 @@ import { Link } from 'react-router-dom';
import { useBridgeQuery, useFeatureFlag } from '@sd/client';
import { Button, Tooltip } from '@sd/ui';
import { Icon, SubtleButton } from '~/components';
import { useLocale } from '~/hooks';
import SidebarLink from '../Link';
import Section from '../Section';
@ -10,6 +11,8 @@ export const Devices = () => {
const { data: node } = useBridgeQuery(['nodeState']);
const isPairingEnabled = useFeatureFlag('p2pPairing');
const { t } = useLocale();
return (
<Section
name="Devices"
@ -28,12 +31,9 @@ export const Devices = () => {
</SidebarLink>
)}
<Tooltip
label="Coming soon! This alpha release doesn't include library sync, it will be ready very soon."
position="right"
>
<Tooltip label={t('devices_coming_soon_tooltip')} position="right">
<Button disabled variant="dotted" className="mt-1 w-full">
Add Device
{t('add_device')}
</Button>
</Tooltip>
</Section>

View file

@ -2,7 +2,7 @@ import { Gear } from '@phosphor-icons/react';
import { useNavigate } from 'react-router';
import { JobManagerContextProvider, useClientContext, useDebugState } from '@sd/client';
import { Button, ButtonLink, Popover, Tooltip, usePopover } from '@sd/ui';
import { useKeysMatcher, useShortcut } from '~/hooks';
import { useKeysMatcher, useLocale, useShortcut } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import DebugPopover from './DebugPopover';
@ -15,6 +15,8 @@ export default () => {
const navigate = useNavigate();
const symbols = useKeysMatcher(['Meta', 'Shift']);
const { t } = useLocale();
useShortcut('navToSettings', (e) => {
e.stopPropagation();
navigate('settings/client/general');
@ -33,7 +35,7 @@ export default () => {
className="w-full"
onClick={updater.installUpdate}
>
Install Update
{t('install_update')}
</Button>
)}
</>
@ -48,7 +50,7 @@ export default () => {
>
<Tooltip
position="top"
label="Settings"
label={t('settings')}
keybinds={[symbols.Shift.icon, symbols.Meta.icon, 'T']}
>
<Gear className="h-5 w-5" />
@ -67,7 +69,7 @@ export default () => {
>
{library && (
<Tooltip
label="Recent Jobs"
label={t('recent_jobs')}
position="top"
keybinds={[symbols.Meta.icon, 'J']}
>

View file

@ -15,6 +15,7 @@ import {
useTotalElapsedTimeText
} from '@sd/client';
import { Button, Dropdown, ProgressBar, toast, Tooltip } from '@sd/ui';
import { useLocale } from '~/hooks';
import Job from './Job';
import JobContainer from './JobContainer';
@ -136,6 +137,8 @@ function Options({
}) {
const queryClient = useQueryClient();
const { t } = useLocale();
const toastErrorSuccess = (
errorMessage?: string,
successMessage?: string,
@ -162,19 +165,19 @@ function Options({
const resumeJob = useLibraryMutation(
['jobs.resume'],
toastErrorSuccess('Failed to resume job.', 'Job has been resumed.')
toastErrorSuccess(t('failed_to_resume_job'), t('job_has_been_resumed'))
);
const pauseJob = useLibraryMutation(
['jobs.pause'],
toastErrorSuccess('Failed to pause job.', 'Job has been paused.')
toastErrorSuccess(t('failed_to_pause_job'), t('job_has_been_paused'))
);
const cancelJob = useLibraryMutation(
['jobs.cancel'],
toastErrorSuccess('Failed to cancel job.', 'Job has been canceled.')
toastErrorSuccess(t('failed_to_cancel_job'), t('job_has_been_canceled'))
);
const clearJob = useLibraryMutation(
['jobs.clear'],
toastErrorSuccess('Failed to remove job.', undefined, () => {
toastErrorSuccess(t('failed_to_remove_job'), undefined, () => {
queryClient.invalidateQueries(['jobs.reports']);
})
);
@ -184,7 +187,7 @@ function Options({
clearJob.mutate(job.id);
//only one toast for all jobs
if (job.id === group.id)
toast.success({ title: 'Success', body: 'Job has been removed.' });
toast.success({ title: t('success'), body: t('job_has_been_removed') });
});
};
@ -203,7 +206,7 @@ function Options({
size="icon"
variant="outline"
>
<Tooltip label="Resume">
<Tooltip label={t('resume')}>
<Play className="h-4 w-4 cursor-pointer" />
</Tooltip>
</Button>
@ -213,7 +216,7 @@ function Options({
align="right"
itemsClassName="!bg-app-darkBox !border-app-box !top-[-8px]"
button={
<Tooltip label="Actions">
<Tooltip label={t('actions')}>
<Button className="!px-1" variant="outline">
<DotsThreeVertical className="h-4 w-4 cursor-pointer" />
</Button>
@ -229,7 +232,7 @@ function Options({
iconClassName="!w-3"
className="!text-[11px] text-ink-dull"
>
Expand
{t('expand')}
</Dropdown.Item>
)}
<Dropdown.Item
@ -238,14 +241,14 @@ function Options({
iconClassName="!w-3"
className="!text-[11px] text-ink-dull"
>
Remove
{t('remove')}
</Dropdown.Item>
</Dropdown.Section>
</Dropdown.Root>
) : (
<>
{/* Pause / Stop */}
<Tooltip label="Pause">
<Tooltip label={t('pause')}>
<Button
className="cursor-pointer"
onClick={() => {
@ -257,7 +260,7 @@ function Options({
<Pause className="h-4 w-4 cursor-pointer" />
</Button>
</Tooltip>
<Tooltip label="Stop">
<Tooltip label={t('stop')}>
<Button
className="cursor-pointer"
onClick={() => {

View file

@ -9,7 +9,7 @@ import {
useLibraryQuery
} from '@sd/client';
import { Button, PopoverClose, toast, Tooltip } from '@sd/ui';
import { useIsDark } from '~/hooks';
import { useIsDark, useLocale } from '~/hooks';
import IsRunningJob from './IsRunningJob';
import JobGroup from './JobGroup';
@ -53,19 +53,21 @@ export function JobManager() {
const isDark = useIsDark();
const { t } = useLocale();
const clearAllJobs = useLibraryMutation(['jobs.clearAll'], {
onError: () => {
toast.error({
title: 'Error',
body: 'Failed to clear all jobs.'
title: t('error'),
body: t('failed_to_clear_all_jobs')
});
},
onSuccess: () => {
queryClient.invalidateQueries(['jobs.reports ']);
setToggleConfirmation((t) => !t);
toast.success({
title: 'Success',
body: 'All jobs have been cleared.'
title: t('success'),
body: t('all_jobs_have_been_cleared')
});
}
});
@ -77,11 +79,11 @@ export function JobManager() {
return (
<div className="h-full overflow-hidden pb-10">
<div className="z-20 flex h-9 w-full items-center rounded-t-md border-b border-app-line/50 bg-app-button/30 px-2">
<span className=" ml-1.5 font-medium">Recent Jobs</span>
<span className=" ml-1.5 font-medium">{t('recent_jobs')}</span>
<div className="grow" />
{toggleConfirmation ? (
<div className="flex h-[85%] w-fit items-center justify-center gap-2 rounded-md border border-app-line bg-app/40 px-2">
<p className="text-[10px]">Are you sure?</p>
<p className="text-[10px]">{t('are_you_sure')}</p>
<PopoverClose asChild>
<Check
onClick={clearAllJobsHandler}
@ -100,14 +102,14 @@ export function JobManager() {
onClick={() => setToggleConfirmation((t) => !t)}
size="icon"
>
<Tooltip label="Clear out finished jobs">
<Tooltip label={t('clear_finished_jobs')}>
<Trash className="h-4 w-4" />
</Tooltip>
</Button>
)}
<PopoverClose asChild>
<Button className="opacity-70" size="icon">
<Tooltip label="Close">
<Tooltip label={t('close')}>
<X className="h-4 w-4" />
</Tooltip>
</Button>
@ -118,7 +120,7 @@ export function JobManager() {
{jobGroups.data &&
(jobGroups.data.length === 0 ? (
<div className="flex h-32 items-center justify-center text-sidebar-inkDull">
No jobs.
{t('no_jobs')}
</div>
) : (
sortJobData(jobGroups.data).map((group) => (

View file

@ -2,11 +2,15 @@ import { Gear, Lock, Plus } from '@phosphor-icons/react';
import clsx from 'clsx';
import { useClientContext } from '@sd/client';
import { dialogManager, Dropdown, DropdownMenu } from '@sd/ui';
import { useLocale } from '~/hooks';
import CreateDialog from '../../settings/node/libraries/CreateDialog';
export default () => {
const { library, libraries, currentLibraryId } = useClientContext();
const { t } = useLocale();
return (
<DropdownMenu.Root
trigger={
@ -41,21 +45,21 @@ export default () => {
))}
<DropdownMenu.Separator className="mx-0" />
<DropdownMenu.Item
label="New Library"
label={t('new_library')}
icon={Plus}
iconProps={{ weight: 'bold', size: 16 }}
onClick={() => dialogManager.create((dp) => <CreateDialog {...dp} />)}
className="font-medium"
/>
<DropdownMenu.Item
label="Manage Library"
label={t('manage_library')}
icon={Gear}
iconProps={{ weight: 'bold', size: 16 }}
to="settings/library/general"
className="font-medium"
/>
<DropdownMenu.Item
label="Lock"
label={t('lock')}
icon={Lock}
iconProps={{ weight: 'bold', size: 16 }}
onClick={() => alert('TODO: Not implemented yet!')}

View file

@ -6,6 +6,7 @@ import { ContextMenu as CM, dialogManager, toast } from '@sd/ui';
import { AddLocationDialog } from '~/app/$libraryId/settings/library/locations/AddLocationDialog';
import DeleteDialog from '~/app/$libraryId/settings/library/locations/DeleteDialog';
import { openDirectoryPickerDialog } from '~/app/$libraryId/settings/library/locations/openDirectoryPickerDialog';
import { useLocale } from '~/hooks';
import { usePlatform } from '~/util/Platform';
export const ContextMenu = ({
@ -16,6 +17,8 @@ export const ContextMenu = ({
const platform = usePlatform();
const libraryId = useLibraryContext().library.uuid;
const { t } = useLocale();
return (
<CM.Root trigger={children}>
<CM.Item
@ -36,19 +39,19 @@ export const ContextMenu = ({
}
}}
icon={Plus}
label="New location"
label={t('new_location')}
/>
<CM.Item
onClick={() => {
navigate(`settings/library/locations/${locationId}`);
}}
icon={Pencil}
label="Edit"
label={t('edit')}
/>
<CM.Separator />
<CM.Item
icon={Trash}
label="Delete"
label={t('delete')}
variant="danger"
onClick={(e: { stopPropagation: () => void }) => {
e.stopPropagation();

View file

@ -5,22 +5,25 @@ import { Link } from 'react-router-dom';
import { ContextMenu as CM, dialogManager } from '@sd/ui';
import CreateDialog from '~/app/$libraryId/settings/library/tags/CreateDialog';
import DeleteDialog from '~/app/$libraryId/settings/library/tags/DeleteDialog';
import { useLocale } from '~/hooks';
export const ContextMenu = ({ children, tagId }: PropsWithChildren<{ tagId: number }>) => {
const navigate = useNavigate();
const { t } = useLocale();
return (
<CM.Root trigger={children}>
<CM.Item
icon={Plus}
label="New tag"
label={t('new_tag')}
onClick={() => {
dialogManager.create((dp) => <CreateDialog {...dp} />);
}}
/>
<CM.Item
icon={Pencil}
label="Edit"
label={t('edit')}
onClick={() => navigate(`settings/library/tags/${tagId}`)}
/>
<CM.Separator />
@ -36,7 +39,7 @@ export const ContextMenu = ({ children, tagId }: PropsWithChildren<{ tagId: numb
));
}}
icon={Trash}
label="Delete"
label={t('delete')}
variant="danger"
/>
</Link>

View file

@ -3,7 +3,7 @@ import clsx from 'clsx';
import { useEffect } from 'react';
import { useNavigate } from 'react-router';
import { Tooltip } from '@sd/ui';
import { useKeyMatcher, useOperatingSystem, useShortcut } from '~/hooks';
import { useKeyMatcher, useLocale, useOperatingSystem, useShortcut } from '~/hooks';
import { useRoutingContext } from '~/RoutingContext';
import { useExplorerDroppable } from '../Explorer/useExplorerDroppable';
@ -12,6 +12,8 @@ import TopBarButton from './TopBarButton';
export const NavigationButtons = () => {
const { currentIndex, maxIndex } = useRoutingContext();
const { t } = useLocale();
const navigate = useNavigate();
const os = useOperatingSystem();
const { icon } = useKeyMatcher('Meta');
@ -58,7 +60,7 @@ export const NavigationButtons = () => {
return (
<div data-tauri-drag-region={os === 'macOS'} className="flex">
<Tooltip keybinds={[icon, '[']} label="Navigate back">
<Tooltip keybinds={[icon, '[']} label={t('navigate_back')}>
<TopBarButton
rounding="left"
onClick={() => navigate(-1)}
@ -72,7 +74,7 @@ export const NavigationButtons = () => {
<ArrowLeft size={14} className="m-[4px]" weight="bold" />
</TopBarButton>
</Tooltip>
<Tooltip keybinds={[icon, ']']} label="Navigate forward">
<Tooltip keybinds={[icon, ']']} label={t('navigate_forward')}>
<TopBarButton
rounding="right"
onClick={() => navigate(1)}

View file

@ -5,7 +5,7 @@ import { useKey } from 'rooks';
import useResizeObserver from 'use-resize-observer';
import { useSelector } from '@sd/client';
import { Tooltip } from '@sd/ui';
import { useKeyMatcher, useOperatingSystem, useShowControls } from '~/hooks';
import { useKeyMatcher, useLocale, useOperatingSystem, useShowControls } from '~/hooks';
import { useRoutingContext } from '~/RoutingContext';
import { useTabsContext } from '~/TabsContext';
import { usePlatform } from '~/util/Platform';
@ -83,6 +83,8 @@ function Tabs() {
const ctx = useTabsContext()!;
const keybind = useKeyMatcher('Meta');
const { t } = useLocale();
function addTab() {
ctx.createTab();
}
@ -134,7 +136,7 @@ function Tabs() {
className="flex h-full flex-1 items-center justify-start border-t border-sidebar-divider bg-sidebar/30 px-2"
data-tauri-drag-region
>
<Tooltip keybinds={[keybind.icon, 'T']} label="New Tab">
<Tooltip keybinds={[keybind.icon, 'T']} label={t('new_tab')}>
<button
onClick={addTab}
className="duration-[50ms] flex flex-row items-center justify-center rounded p-1.5 transition-colors hover:bg-app/80"

View file

@ -19,6 +19,7 @@ import {
useDismissibleNoticeStore,
useIsDark,
useKeyDeleteFile,
useLocale,
useOperatingSystem,
useZodSearchParams
} from '~/hooks';
@ -59,6 +60,8 @@ const NOTICE_ITEMS: { icon: keyof typeof iconNames; name: string }[] = [
];
const EphemeralNotice = ({ path }: { path: string }) => {
const { t } = useLocale();
const isDark = useIsDark();
const { ephemeral: dismissed } = useDismissibleNoticeStore();
@ -95,7 +98,7 @@ const EphemeralNotice = ({ path }: { path: string }) => {
</div>
<Tooltip
label="Add path as an indexed location"
label={t('add_location_tooltip')}
className="z-50 w-max min-w-0 shrink animate-pulse [animation-duration:_3000ms] hover:animate-none"
>
<AddLocationButton
@ -125,17 +128,18 @@ const EphemeralNotice = ({ path }: { path: string }) => {
<div className="p-3 pt-0">
<div className="py-4 text-center">
<h2 className="text-lg font-semibold text-ink">Local Locations</h2>
<h2 className="text-lg font-semibold text-ink">
{t('local_locations')}
</h2>
<p className="mt-px text-sm text-ink-dull">
Browse your files and folders directly from your device.
{t('ephemeral_notice_browse')}
</p>
</div>
<div className="flex items-center rounded-md border border-app-line bg-app-box px-3 py-2 text-ink-faint">
<Info size={20} weight="light" className="mr-2.5 shrink-0" />
<p className="text-sm font-light">
Consider indexing your local locations for a faster and more
efficient exploration.
{t('ephemeral_notice_consider_indexing')}
</p>
</div>
@ -145,7 +149,7 @@ const EphemeralNotice = ({ path }: { path: string }) => {
size="md"
onClick={dismiss}
>
Got it
{t('got_it')}
</Button>
</div>
</Dialog.Content>
@ -232,14 +236,13 @@ const EphemeralExplorer = memo((props: { args: PathParams }) => {
useKeyDeleteFile(explorer.selectedItems, null);
const { t } = useLocale();
return (
<ExplorerContextProvider explorer={explorer}>
<TopBarPortal
left={
<Tooltip
label="Add path as an indexed location"
className="w-max min-w-0 shrink"
>
<Tooltip label={t('add_location_tooltip')} className="w-max min-w-0 shrink">
<AddLocationButton path={path} />
</Tooltip>
}
@ -250,7 +253,7 @@ const EphemeralExplorer = memo((props: { args: PathParams }) => {
<EmptyNotice
loading={query.isFetching}
icon={<Icon name="FolderNoSpace" size={128} />}
message="No files found here"
message={t('no_files_found_here')}
/>
}
/>

View file

@ -22,6 +22,7 @@ import { Folder, Icon } from '~/components';
import {
useIsLocationIndexing,
useKeyDeleteFile,
useLocale,
useRouteTitle,
useShortcut,
useZodRouteParams
@ -130,6 +131,8 @@ const LocationExplorer = ({ location }: { location: Location; path?: string }) =
const isLocationIndexing = useIsLocationIndexing(location.id);
const { t } = useLocale();
return (
<ExplorerContextProvider explorer={explorer}>
<SearchContextProvider search={search}>
@ -140,7 +143,7 @@ const LocationExplorer = ({ location }: { location: Location; path?: string }) =
<Folder size={22} className="mt-[-1px]" />
<span className="truncate text-sm font-medium">{title}</span>
{!locationOnline && (
<Tooltip label="Location is offline, you can still browse and organize.">
<Tooltip label={t('location_offline_tooltip')}>
<Info className="text-ink-faint" />
</Tooltip>
)}
@ -151,7 +154,7 @@ const LocationExplorer = ({ location }: { location: Location; path?: string }) =
<DefaultTopBarOptions
options={[
{
toolTipLabel: 'Reload',
toolTipLabel: t('reload'),
onClick: () => rescan(location.id),
icon: <ArrowClockwise className={TOP_BAR_ICON_STYLE} />,
individual: true,
@ -178,7 +181,7 @@ const LocationExplorer = ({ location }: { location: Location; path?: string }) =
emptyNotice={
<EmptyNotice
icon={<Icon name="FolderNoSpace" size={128} />}
message="No files found here"
message={t('no_files_found_here')}
/>
}
/>

View file

@ -24,6 +24,7 @@ import {
tw,
usePopover
} from '@sd/ui';
import { useLocale } from '~/hooks';
import TopBarButton from '../TopBar/TopBarButton';
@ -32,6 +33,8 @@ const OptionButton = tw(TopBarButton)`w-full gap-1 !px-1.5 !py-1`;
export default function LocationOptions({ location, path }: { location: Location; path: string }) {
const navigate = useNavigate();
const { t } = useLocale();
const [copied, setCopied] = useState(false);
const scanLocationSubPath = useLibraryMutation('locations.subPathRescan');
@ -64,7 +67,7 @@ export default function LocationOptions({ location, path }: { location: Location
value={currentPath ?? ''}
right={
<Tooltip
label={copied ? 'Copied' : 'Copy path to clipboard'}
label={copied ? t('copied') : t('copy_path_to_clipboard')}
className="flex"
>
<Button
@ -76,8 +79,11 @@ export default function LocationOptions({ location, path }: { location: Location
navigator.clipboard.writeText(currentPath);
toast.info({
title: 'Path copied to clipboard',
body: `Path for location "${location.name}" copied to clipboard.`
title: t('path_copied_to_clipboard_title'),
body: t(
'path_copied_to_clipboard_description',
{ location: location.name }
)
});
setCopied(true);
@ -99,7 +105,7 @@ export default function LocationOptions({ location, path }: { location: Location
}
>
<Gear />
Configure Location
{t('configure_location')}
</OptionButton>
</PopoverSection>
<PopoverDivider />
@ -113,20 +119,20 @@ export default function LocationOptions({ location, path }: { location: Location
}
>
<FolderDotted />
Re-index
{t('reindex')}
</OptionButton>
<OptionButton
onClick={() => regenThumbs.mutate({ id: location.id, path })}
>
<Image />
Regenerate Thumbs
{t('regenerate_thumbs')}
</OptionButton>
</PopoverSection>
<PopoverDivider />
<PopoverSection>
<OptionButton onClick={archiveLocation}>
<Archive />
Archive
{t('archive')}
</OptionButton>
</PopoverSection>
</PopoverContainer>

View file

@ -1,5 +1,7 @@
import { ScreenHeading } from '@sd/ui';
import { useLocale } from '~/hooks';
export const Component = () => {
return <ScreenHeading>Media</ScreenHeading>;
const { t } = useLocale();
return <ScreenHeading>{t('media')}</ScreenHeading>;
};

View file

@ -1,6 +1,7 @@
import { useMemo } from 'react';
import { useDiscoveredPeers } from '@sd/client';
import { Icon } from '~/components';
import { useLocale } from '~/hooks';
import { useRouteTitle } from '~/hooks/useRouteTitle';
import Explorer from './Explorer';
@ -13,6 +14,8 @@ import { TopBarPortal } from './TopBar/Portal';
export const Component = () => {
const title = useRouteTitle('Network');
const { t } = useLocale();
const discoveredPeers = useDiscoveredPeers();
const peers = useMemo(() => Array.from(discoveredPeers.values()), [discoveredPeers]);
@ -59,10 +62,9 @@ export const Component = () => {
emptyNotice={
<div className="flex h-full flex-col items-center justify-center text-white">
<Icon name="Globe" size={128} />
<h1 className="mt-4 text-lg font-bold">Your Local Network</h1>
<h1 className="mt-4 text-lg font-bold">{t('your_local_network')}</h1>
<p className="mt-1 max-w-sm text-center text-sm text-ink-dull">
Other Spacedrive nodes on your LAN will appear here, along with your
default OS network mounts.
{t("network_page_description")}
</p>
</div>
}

View file

@ -1,5 +1,7 @@
import { ScreenHeading } from '@sd/ui';
import { useLocale } from '~/hooks';
export const Component = () => {
return <ScreenHeading>People</ScreenHeading>;
const { t } = useLocale();
return <ScreenHeading>{t('people')}</ScreenHeading>;
};

View file

@ -17,7 +17,7 @@ import {
} from '@phosphor-icons/react';
import { useFeatureFlag } from '@sd/client';
import { tw } from '@sd/ui';
import { useOperatingSystem } from '~/hooks';
import { useLocale, useOperatingSystem } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import Icon from '../Layout/Sidebar/Icon';
@ -34,6 +34,8 @@ export default () => {
// const isPairingEnabled = useFeatureFlag('p2pPairing');
const isBackupsEnabled = useFeatureFlag('backups');
const { t } = useLocale();
return (
<div className="custom-scroll no-scrollbar h-full w-60 max-w-[180px] shrink-0 border-r border-app-line/50 pb-5">
{platform === 'tauri' ? (
@ -49,49 +51,49 @@ export default () => {
<div className="space-y-6 px-4 py-3">
<Section>
<Heading>Client</Heading>
<Heading>{t('client')}</Heading>
<SidebarLink to="client/general">
<Icon component={GearSix} />
General
{t('general')}
</SidebarLink>
<SidebarLink to="client/usage">
<Icon component={ChartBar} />
Usage
{t('usage')}
</SidebarLink>
<SidebarLink to="client/account">
<Icon component={User} />
Account
{t('account')}
</SidebarLink>
<SidebarLink to="node/libraries">
<Icon component={Books} />
Libraries
{t('libraries')}
</SidebarLink>
<SidebarLink to="client/privacy">
<Icon component={ShieldCheck} />
Privacy
{t('privacy')}
</SidebarLink>
<SidebarLink to="client/appearance">
<Icon component={PaintBrush} />
Appearance
{t('appearance')}
</SidebarLink>
<SidebarLink to="client/backups" disabled={!isBackupsEnabled}>
<Icon component={Database} />
Backups
{t('backups')}
</SidebarLink>
<SidebarLink to="client/keybindings">
<Icon component={KeyReturn} />
Keybinds
{t('keybinds')}
</SidebarLink>
<SidebarLink to="client/extensions" disabled>
<Icon component={PuzzlePiece} />
Extensions
{t('extensions')}
</SidebarLink>
</Section>
<Section>
<Heading>Library</Heading>
<Heading>{t('library')}</Heading>
<SidebarLink to="library/general">
<Icon component={GearSix} />
General
{t('general')}
</SidebarLink>
{/* <SidebarLink to="library/nodes" disabled={!isPairingEnabled}>
<Icon component={ShareNetwork} />
@ -99,11 +101,11 @@ export default () => {
</SidebarLink> */}
<SidebarLink to="library/locations">
<Icon component={HardDrive} />
Locations
{t('locations')}
</SidebarLink>
<SidebarLink to="library/tags">
<Icon component={TagSimple} />
Tags
{t('tags')}
</SidebarLink>
{/* <SidebarLink to="library/saved-searches">
<Icon component={MagnifyingGlass} />
@ -111,22 +113,22 @@ export default () => {
</SidebarLink> */}
<SidebarLink disabled to="library/clouds">
<Icon component={Cloud} />
Clouds
{t('clouds')}
</SidebarLink>
<SidebarLink to="library/keys" disabled>
<Icon component={Key} />
Keys
{t('keys')}
</SidebarLink>
</Section>
<Section>
<Heading>Resources</Heading>
<Heading>{t('resources')}</Heading>
<SidebarLink to="resources/about">
<Icon component={FlyingSaucer} />
About
{t('about')}
</SidebarLink>
<SidebarLink to="resources/changelog">
<Icon component={Receipt} />
Changelog
{t('changelog')}
</SidebarLink>
{/* <SidebarLink to="resources/dependencies">
<Icon component={Graph} />

View file

@ -5,10 +5,12 @@ import { auth, useBridgeMutation, useBridgeQuery, useFeatureFlag } from '@sd/cli
import { Button, Card, Input, toast } from '@sd/ui';
import { Icon, TruncatedText } from '~/components';
import { AuthRequiredOverlay } from '~/components/AuthRequiredOverlay';
import { useLocale } from '~/hooks';
import { Heading } from '../Layout';
export const Component = () => {
const { t } = useLocale();
const me = useBridgeQuery(['auth.me'], { retry: false });
const authStore = auth.useStateSnapshot();
return (
@ -25,8 +27,8 @@ export const Component = () => {
)}
</>
}
title="Your account"
description="Spacedrive account and information."
title={t('your_account')}
description={t('your_account_description')}
/>
<div className="flex flex-col justify-between gap-5 lg:flex-row">
<Profile authStore={authStore} email={me.data?.email} />

View file

@ -11,6 +11,7 @@ import {
useZodForm
} from '@sd/client';
import { Button, Divider, Form, Select, SelectOption, SwitchField, z } from '@sd/ui';
import { useLocale } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import { Heading } from '../Layout';
@ -64,6 +65,7 @@ export const Component = () => {
const { lockAppTheme } = usePlatform();
const themeStore = useThemeStore();
const formatStore = useUnitFormatStore();
const { t } = useLocale();
const [selectedTheme, setSelectedTheme] = useState<Theme['themeValue']>(
themeStore.syncThemeWithSystem === true ? 'system' : themeStore.theme
@ -121,8 +123,8 @@ export const Component = () => {
<>
<Form form={form} onSubmit={onSubmit}>
<Heading
title="Appearance"
description="Change the look of your client."
title={t('appearance')}
description={t('appearance_description')}
rightArea={
<div>
<Button
@ -134,7 +136,7 @@ export const Component = () => {
hueSliderHandler(235);
}}
>
Reset
{t('reset')}
</Button>
</div>
}
@ -197,9 +199,9 @@ export const Component = () => {
<div className="flex flex-col gap-4">
<Setting
mini
title="UI Animations"
title={t('ui_animations')}
className="opacity-30"
description="Dialogs and other UI elements will animate when opening and closing."
description={t('ui_animations_description')}
>
<SwitchField
disabled
@ -210,9 +212,9 @@ export const Component = () => {
<Setting
mini
title="Blur Effects"
title={t('blur_effects')}
className="opacity-30"
description="Some components will have a blur effect applied to them."
description={t('blur_effects_description')}
>
<SwitchField
disabled
@ -224,9 +226,9 @@ export const Component = () => {
</Form>
<Divider />
<div className="flex flex-col gap-4">
<h1 className="mb-3 text-lg font-bold text-ink">Display Formats</h1>
<h1 className="mb-3 text-lg font-bold text-ink">{t('display_formats')}</h1>
<Setting mini title="Coordinates">
<Setting mini title={t('coordinates')}>
<Select
onChange={(e) => (getUnitFormatStore().coordinatesFormat = e)}
value={formatStore.coordinatesFormat}
@ -236,23 +238,23 @@ export const Component = () => {
</Select>
</Setting>
<Setting mini title="Distance">
<Setting mini title={t('distance')}>
<Select
onChange={(e) => (getUnitFormatStore().distanceFormat = e)}
value={formatStore.distanceFormat}
>
<SelectOption value="km">Kilometers</SelectOption>
<SelectOption value="miles">Miles</SelectOption>
<SelectOption value="km">{t('kilometers')}</SelectOption>
<SelectOption value="miles">{t('miles')}</SelectOption>
</Select>
</Setting>
<Setting mini title="Temperature">
<Setting mini title={t('temperature')}>
<Select
onChange={(e) => (getUnitFormatStore().temperatureFormat = e)}
value={formatStore.temperatureFormat}
>
<SelectOption value="celsius">Celsius</SelectOption>
<SelectOption value="fahrenheit">Fahrenheit</SelectOption>
<SelectOption value="celsius">{t('celcius')}</SelectOption>
<SelectOption value="fahrenheit">{t('fahrenheit')}</SelectOption>
</Select>
</Setting>
</div>

View file

@ -2,6 +2,7 @@ import dayjs from 'dayjs';
import { useBridgeMutation, useBridgeQuery, useLibraryMutation } from '@sd/client';
import { Button, Card } from '@sd/ui';
import { Database } from '~/components';
import { useLocale } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import { Heading } from '../Layout';
@ -12,6 +13,7 @@ import { Heading } from '../Layout';
export const Component = () => {
const platform = usePlatform();
const { t } = useLocale();
const backups = useBridgeQuery(['backups.getAll']);
const doBackup = useLibraryMutation('backups.backup');
const doRestore = useBridgeMutation('backups.restore');
@ -20,8 +22,8 @@ export const Component = () => {
return (
<>
<Heading
title="Backups"
description="Manage your Spacedrive database backups."
title={t('backups')}
description={t("backups_description")}
rightArea={
<div className="flex flex-row items-center space-x-5">
<Button
@ -67,7 +69,7 @@ export const Component = () => {
onClick={() => doRestore.mutate(backup.path)}
variant="gray"
>
Restore
{t('restore')}
</Button>
<Button
disabled={doDelete.isLoading}
@ -76,7 +78,7 @@ export const Component = () => {
variant="colored"
className="border-red-500 bg-red-500"
>
Delete
{t('delete')}
</Button>
</div>
</Card>

View file

@ -1,4 +1,5 @@
import { Button, Card, GridLayout, SearchInput } from '@sd/ui';
import { useLocale } from '~/hooks';
import { Heading } from '../Layout';
@ -42,13 +43,15 @@ const extensions: ExtensionItemData[] = [
function ExtensionItem(props: { extension: ExtensionItemData }) {
const { installed, name, description } = props.extension;
const { t } = useLocale();
return (
<Card className="flex-col">
<h3 className="mt-2 text-sm font-bold">{name}</h3>
<p className="my-1 text-xs text-gray-300">{description}</p>
<div className="grow" />
<Button size="sm" className="my-2" variant={installed ? 'gray' : 'accent'}>
{installed ? 'Installed' : 'Install'}
{installed ? t('installed') : 'install'}
</Button>
</Card>
);
@ -57,12 +60,14 @@ function ExtensionItem(props: { extension: ExtensionItemData }) {
export const Component = () => {
// const { data: volumes } = useBridgeQuery('GetVolumes');
const { t } = useLocale();
return (
<>
<Heading
title="Extensions"
description="Install extensions to extend the functionality of this client."
rightArea={<SearchInput className="mt-1.5" placeholder="Search extensions" />}
title={t('extensions')}
description={t('extensions_description')}
rightArea={<SearchInput className="mt-1.5" placeholder={t('search_extensions')} />}
/>
<GridLayout>

View file

@ -10,7 +10,7 @@ import {
} from '@sd/client';
import { Button, Card, Input, Select, SelectOption, Slider, Switch, tw, z } from '@sd/ui';
import { Icon } from '~/components';
import { useDebouncedFormWatch } from '~/hooks';
import { useDebouncedFormWatch, useLocale } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import { Heading } from '../Layout';
@ -89,22 +89,28 @@ export const Component = () => {
}
});
const { t } = useLocale();
return (
<FormProvider {...form}>
<Heading
title="General Settings"
description="General settings related to this client."
title={t('general_settings')}
description={t('general_settings_description')}
/>
<Card className="px-5">
<div className="my-2 flex w-full flex-col">
<div className="flex flex-row items-center justify-between">
<span className="font-semibold">Local Node</span>
<span className="font-semibold">{t('local_node')}</span>
<div className="flex flex-row space-x-1">
<NodePill>{connectedPeers.size} Peers</NodePill>
<NodePill>
{connectedPeers.size} {t('peers')}
</NodePill>
{node.data?.p2p_enabled === true ? (
<NodePill className="!bg-accent text-white">Running</NodePill>
<NodePill className="!bg-accent text-white">
{t('running')}
</NodePill>
) : (
<NodePill className="text-white">Disabled</NodePill>
<NodePill className="text-white">{t('disabled')}</NodePill>
)}
</div>
</div>
@ -113,7 +119,7 @@ export const Component = () => {
<div className="flex w-full items-center gap-5">
<Icon name="Laptop" className="mt-2 h-14 w-14" />
<div className="flex flex-col">
<NodeSettingLabel>Node Name</NodeSettingLabel>
<NodeSettingLabel>{t('node_name')}</NodeSettingLabel>
<Input
{...form.register('name', { required: true })}
defaultValue={node.data?.name}
@ -137,7 +143,7 @@ export const Component = () => {
</div> */}
<div>
<NodeSettingLabel>Data Folder</NodeSettingLabel>
<NodeSettingLabel>{t('data_folder')}</NodeSettingLabel>
<div className="mt-2 flex w-full flex-row gap-2">
<Input className="grow" value={node.data?.data_path} disabled />
<Button
@ -179,11 +185,7 @@ export const Component = () => {
</div>
</Card>
<Setting
mini
title="Debug mode"
description="Enable extra debugging features within the app."
>
<Setting mini title={t('debug_mode')} description={t('debug_mode_description')}>
<Switch
size="md"
checked={debugState.enabled}
@ -193,8 +195,8 @@ export const Component = () => {
<Setting
mini
registerName="background_processing_percentage"
title="Thumbnailer CPU usage"
description="Limit how much CPU the thumbnailer can use for background processing."
title={t('thumbnailer_cpu_usage')}
description={t('thumbnailer_cpu_usage_description')}
>
<div className="flex h-[30px] w-80 items-center gap-2">
<Slider
@ -224,8 +226,8 @@ export const Component = () => {
</Setting>
<Setting
mini
title="Image label recognition AI model"
description="The model used to recognize objects in images. Larger models are more accurate but slower."
title={t('image_labeler_ai_model')}
description={t('image_labeler_ai_model_description')}
registerName="image_labeler_version"
>
<div className="flex h-[30px] gap-2">
@ -246,7 +248,7 @@ export const Component = () => {
</div>
</Setting>
<div className="flex flex-col gap-4">
<h1 className="mb-3 text-lg font-bold text-ink">Networking</h1>
<h1 className="mb-3 text-lg font-bold text-ink">{t('networking')}</h1>
{/* TODO: Add some UI for this stuff */}
{/* {node.data?.p2p.ipv4.status === 'Listening' ||
@ -260,7 +262,8 @@ export const Component = () => {
<Setting
mini
title="Enable Networking"
title={t('enable_networking')}
// TODO: i18n
description={
<>
<p className="text-sm text-gray-400">
@ -283,8 +286,8 @@ export const Component = () => {
</Setting>
<Setting
mini
title="Networking Port"
description="The port for Spacedrive's Peer-to-peer networking to communicate on. You should leave this disabled unless you have a restictive firewall. Do not expose to the internet!"
title={t('networking_port')}
description={t('networking_port_description')}
>
<div className="flex h-[30px] gap-2">
<Controller
@ -301,8 +304,8 @@ export const Component = () => {
form.setValue('p2p_port', 0);
}}
>
<SelectOption value="Default">Default</SelectOption>
<SelectOption value="Custom">Custom</SelectOption>
<SelectOption value="Default">{t('default')}</SelectOption>
<SelectOption value="Custom">{t('custom')}</SelectOption>
</Select>
)}
/>

View file

@ -7,7 +7,13 @@ import {
import clsx from 'clsx';
import { useState } from 'react';
import { Divider, ModifierKeys, Switch } from '@sd/ui';
import { keybindingsData, ShortcutCategories, ShortcutKeybinds, useOperatingSystem } from '~/hooks';
import {
keybindingsData,
ShortcutCategories,
ShortcutKeybinds,
useLocale,
useOperatingSystem
} from '~/hooks';
import { keybindForOs } from '~/util/keybinds';
import { OperatingSystem } from '~/util/Platform';
@ -16,13 +22,14 @@ import Setting from '../Setting';
export const Component = () => {
const [syncWithLibrary, setSyncWithLibrary] = useState(true);
const { t } = useLocale();
return (
<>
<Heading title="Keybinds" description="View and manage client keybinds" />{' '}
<Heading title={t('keybinds')} description={t('keybinds_description')} />{' '}
<Setting
mini
title="Sync with Library"
description="If enabled, your keybinds will be synced with library, otherwise they will apply only to this client."
title={t('sync_with_library')}
description={t('sync_with_library_description')}
>
<Switch
checked={syncWithLibrary}

View file

@ -1,5 +1,6 @@
import { telemetryStore, useTelemetryState } from '@sd/client';
import { Switch } from '@sd/ui';
import { useLocale } from '~/hooks';
import { Heading } from '../Layout';
import Setting from '../Setting';
@ -7,15 +8,17 @@ import Setting from '../Setting';
export const Component = () => {
const fullTelemetry = useTelemetryState().shareFullTelemetry;
const { t } = useLocale();
return (
<>
<Heading title="Privacy" description="" />
<Heading title={t('privacy')} description="" />
<Setting
mini
toolTipLabel="Learn more about telemetry"
toolTipLabel={t('learn_more_about_telemetry')}
infoUrl="https://www.spacedrive.com/docs/product/resources/privacy"
title="Share Additional Telemetry and Usage Data"
description="Toggle ON to provide developers with detailed usage and telemetry data to enhance the app. Toggle OFF to send only basic data: your activity status, app version, core version, and platform (e.g., mobile, web, or desktop)."
title={t('telemetry_title')}
description={t('telemetry_description')}
>
<Switch
checked={fullTelemetry}

View file

@ -3,11 +3,12 @@ import { memo, useEffect, useMemo, useState } from 'react';
import { byteSize, useDiscoveredPeers, useLibraryQuery, useNodes } from '@sd/client';
import { Card } from '@sd/ui';
import { Icon } from '~/components';
import { useCounter } from '~/hooks';
import { useCounter, useLocale } from '~/hooks';
import { Heading } from '../Layout';
export const Component = () => {
const { t } = useLocale();
const stats = useLibraryQuery(['library.statistics'], {
refetchOnWindowFocus: false,
initialData: { total_bytes_capacity: '0', library_db_size: '0' }
@ -77,7 +78,7 @@ export const Component = () => {
return (
<>
<Heading title="Usage" description="Your library usage and hardware information" />
<Heading title={t('usage')} description={t('usage_description')} />
<Card className="flex w-full flex-col justify-center !p-5">
<div className="grid grid-cols-1 justify-center gap-2 lg:grid-cols-2 xl:grid-cols-3">
{info?.map((i, index) => (

View file

@ -1,9 +1,13 @@
import { useLocale } from '~/hooks';
import { Heading } from '../Layout';
export const Component = () => {
const { t } = useLocale();
return (
<>
<Heading title="Contacts" description="Manage your contacts in Spacedrive." />
<Heading title={t('contacts')} description={t('contacts_description')} />
</>
);
};

View file

@ -1,6 +1,6 @@
import { MaybeUndefined, useBridgeMutation, useLibraryContext, useZodForm } from '@sd/client';
import { Button, dialogManager, Form, InputField, Switch, Tooltip, z } from '@sd/ui';
import { useDebouncedFormWatch } from '~/hooks';
import { useDebouncedFormWatch, useLocale } from '~/hooks';
import { Heading } from '../Layout';
import DeleteLibraryDialog from '../node/libraries/DeleteDialog';
@ -21,6 +21,8 @@ export const Component = () => {
const { library } = useLibraryContext();
const editLibrary = useBridgeMutation('library.edit');
const { t } = useLocale();
const form = useZodForm({
schema,
defaultValues: {
@ -44,8 +46,8 @@ export const Component = () => {
<Form form={form}>
<div className="flex w-full max-w-4xl flex-col space-y-6 pb-5">
<Heading
title="Library Settings"
description="General settings related to the currently active library."
title={t('library_settings')}
description={t('library_settings_description')}
/>
<input type="hidden" {...form.register('id')} />
@ -53,13 +55,13 @@ export const Component = () => {
<div className="flex flex-row space-x-5 pb-3">
<InputField
size="md"
label="Name"
label={t('name')}
formFieldClassName="flex-1"
defaultValue="My Default Library"
{...form.register('name', { required: true })}
/>
<InputField
label="Description"
label={t('description')}
size="md"
formFieldClassName="flex-1"
{...form.register('description')}
@ -68,21 +70,25 @@ export const Component = () => {
<Setting
mini
title="Encrypt Library"
description="Enable encryption for this library, this will only encrypt the Spacedrive database, not the files themselves."
title={t('encrypt_library')}
description={t('encrypt_library_description')}
>
<div className="ml-3 flex items-center">
<Tooltip label="Library encryption coming soon">
<Tooltip label={t('encrypt_library_coming_soon')}>
<Switch disabled size="md" checked={false} />
</Tooltip>
</div>
</Setting>
<Setting mini title="Export Library" description="Export this library to a file.">
<Setting
mini
title={t('export_library')}
description={t('export_library_description')}
>
<div className="mt-2">
<Tooltip label="Export Library coming soon">
<Tooltip label={t('export_library_coming_soon')}>
<Button disabled size="sm" variant="gray">
Export
{t('export')}
</Button>
</Tooltip>
</div>
@ -90,8 +96,8 @@ export const Component = () => {
<Setting
mini
title="Delete Library"
description="This is permanent, your files will not be deleted, only the Spacedrive library."
title={t('delete_library')}
description={t('delete_library_description')}
>
<div className="mt-2">
<Button
@ -104,7 +110,7 @@ export const Component = () => {
));
}}
>
Delete
{t('delete')}
</Button>
</div>
</Setting>

View file

@ -21,7 +21,7 @@ import {
} from '@sd/ui';
import ModalLayout from '~/app/$libraryId/settings/ModalLayout';
import { LocationIdParamsSchema } from '~/app/route-schemas';
import { useZodRouteParams } from '~/hooks';
import { useLocale, useZodRouteParams } from '~/hooks';
import DeleteDialog from './DeleteDialog';
import IndexerRuleEditor from './IndexerRuleEditor';
@ -75,7 +75,7 @@ const EditLocationForm = () => {
const updateLocation = useLibraryMutation('locations.update', {
onError: () => {
toast.error('Failed to update location settings');
toast.error(t('failed_to_update_location_settings'));
},
onSuccess: () => {
form.reset(form.getValues());
@ -95,15 +95,17 @@ const EditLocationForm = () => {
})
);
const { t } = useLocale();
return (
<Form form={form} onSubmit={onSubmit} className="h-full w-full">
<ModalLayout
title="Edit Location"
title={t('edit_location')}
topRight={
<div className="flex flex-row space-x-3">
{form.formState.isDirty && (
<Button onClick={() => form.reset()} variant="outline" size="sm">
Reset
{t('reset')}
</Button>
)}
<Button
@ -112,75 +114,58 @@ const EditLocationForm = () => {
variant={form.formState.isDirty ? 'accent' : 'outline'}
size="sm"
>
Save Changes
{t('save_changes')}
</Button>
</div>
}
>
<div className="flex space-x-4">
<FlexCol>
<InputField label="Display Name" {...form.register('name')} />
<InfoText className="mt-2">
The name of this Location, this is what will be displayed in the
sidebar. Will not rename the actual folder on disk.
</InfoText>
<InputField label={t('display_name')} {...form.register('name')} />
<InfoText className="mt-2">{t('location_display_name_info')}</InfoText>
</FlexCol>
<FlexCol>
<LocationPathInputField label="Path" {...form.register('path')} />
<InfoText className="mt-2">
The path to this Location, this is where the files will be stored on
disk.
</InfoText>
<LocationPathInputField label={t('path')} {...form.register('path')} />
<InfoText className="mt-2">{t('location_path_info')}</InfoText>
</FlexCol>
</div>
<Divider />
<div className="space-y-2">
<Label className="grow">Location Type</Label>
<Label className="grow">{t('location_type')}</Label>
<RadioGroupField.Root
className="flex flex-row !space-y-0 space-x-2"
{...form.register('locationType')}
>
<RadioGroupField.Item key="normal" value="normal">
<h1 className="font-bold">Normal</h1>
<p className="text-sm text-ink-faint">
Contents will be indexed as-is, new files will not be automatically
sorted.
</p>
<h1 className="font-bold">{t('normal')}</h1>
<p className="text-sm text-ink-faint">{t('location_type_normal')}</p>
</RadioGroupField.Item>
<RadioGroupField.Item disabled key="managed" value="managed">
<h1 className="font-bold">Managed</h1>
<p className="text-sm text-ink-faint">
Spacedrive will sort files for you. If Location isn't empty a
"spacedrive" folder will be created.
</p>
<h1 className="font-bold">{t('managed')}</h1>
<p className="text-sm text-ink-faint">{t('location_type_managed')}</p>
</RadioGroupField.Item>
<RadioGroupField.Item disabled key="replica" value="replica">
<h1 className="font-bold">Replica</h1>
<p className="text-sm text-ink-faint ">
This Location is a replica of another, its contents will be
automatically synchronized.
</p>
<h1 className="font-bold">{t('replica')}</h1>
<p className="text-sm text-ink-faint ">{t('location_type_replica')}</p>
</RadioGroupField.Item>
</RadioGroupField.Root>
</div>
<Divider />
<div className="space-y-2">
<ToggleSection>
<Label className="grow">Generate preview media for this Location</Label>
<Label className="grow">{t('generatePreviewMedia_label')}</Label>
<SwitchField {...form.register('generatePreviewMedia')} size="sm" />
</ToggleSection>
<ToggleSection>
<Label className="grow">
Sync preview media for this Location with your devices
</Label>
<Label className="grow">{t('syncPreviewMedia_label')}</Label>
<SwitchField {...form.register('syncPreviewMedia')} size="sm" />
</ToggleSection>
<ToggleSection>
<Label className="grow">
Hide location and contents from view{' '}
<Tooltip label='Prevents the location and its contents from appearing in summary categories, search and tags unless "Show hidden items" is enabled.'>
{t('hide_location_from_view')}{' '}
<Tooltip label={t('hidden_label')}>
<Info className="inline" />
</Tooltip>
</Label>
@ -193,9 +178,9 @@ const EditLocationForm = () => {
render={({ field }) => (
<IndexerRuleEditor
field={field}
label="Indexer rules"
label={t('indexer_rules')}
editable={true}
infoText="Indexer rules allow you to specify paths to ignore using globs."
infoText={t('indexer_rules_info')}
className="flex flex-col rounded-md border border-app-line bg-app-overlay p-5"
/>
)}
@ -216,29 +201,23 @@ const EditLocationForm = () => {
variant="outline"
>
<ArrowsClockwise className="-mt-0.5 mr-1.5 inline h-4 w-4" />
Full Reindex
{t('full_reindex')}
</Button>
</div>
<InfoText className="mt-2">
Perform a full rescan of this Location.
</InfoText>
<InfoText className="mt-2">{t('full_reindex_info')}</InfoText>
</FlexCol>
<FlexCol>
<div>
<Button
onClick={() => toast.info('Archiving locations is coming soon...')}
onClick={() => toast.info(t('archive_coming_soon'))}
size="sm"
variant="outline"
className=""
>
<Archive className="-mt-0.5 mr-1.5 inline h-4 w-4" />
Archive
{t('archive')}
</Button>
</div>
<InfoText className="mt-2">
Extract data from Library as an archive, useful to preserve Location
folder structure.
</InfoText>
<InfoText className="mt-2">{t('archive_info')}</InfoText>
</FlexCol>
<FlexCol>
<div>
@ -258,12 +237,10 @@ const EditLocationForm = () => {
}}
>
<Trash className="-mt-0.5 mr-1.5 inline h-4 w-4" />
Delete
{t('delete')}
</Button>
</div>
<InfoText className="mt-2">
This will not delete the actual folder on disk. Preview media will be
</InfoText>
<InfoText className="mt-2">{t('delete_info')}</InfoText>
</FlexCol>
</div>
<Divider />

View file

@ -24,7 +24,7 @@ import {
} from '@sd/ui';
import { explorerStore } from '~/app/$libraryId/Explorer/store';
import { Accordion, Icon } from '~/components';
import { useCallbackToWatchForm } from '~/hooks';
import { useCallbackToWatchForm, useLocale } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import IndexerRuleEditor from './IndexerRuleEditor';
@ -211,22 +211,19 @@ export const AddLocationDialog = ({
await listLocations.refetch();
});
const { t } = useLocale();
return (
<Dialog
form={form}
title="New Location"
title={t('new_location')}
dialog={useDialog(dialogProps)}
icon={<Icon name="NewLocation" size={28} />}
onSubmit={onSubmit}
ctaLabel="Add"
ctaLabel={t('add')}
formClassName="min-w-[375px]"
errorMessageException="Location is already linked"
description={
platform.platform === 'web'
? 'As you are using the browser version of Spacedrive you will (for now) ' +
'need to specify an absolute URL of a directory local to the remote node.'
: ''
}
errorMessageException={t('location_is_already_linked')}
description={platform.platform === 'web' ? t('new_location_web_description') : ''}
>
<div className="flex flex-col">
<ErrorMessage
@ -251,16 +248,18 @@ export const AddLocationDialog = ({
)}
control={form.control}
/>
<Label className="text-xs font-semibold">Open new location once added</Label>
<Label className="text-xs font-semibold">
{t('open_new_location_once_added')}
</Label>
</div>
<Accordion title="Advanced settings">
<Accordion title={t('advanced_settings')}>
<Controller
name="indexerRulesIds"
render={({ field }) => (
<IndexerRuleEditor
field={field}
label="File indexing rules"
label={t('file_indexing_rules')}
className="relative flex flex-col"
rulesContainerClass="grid grid-cols-2 gap-2"
ruleButtonClass="w-full"

View file

@ -1,6 +1,7 @@
import { useLibraryMutation, usePlausibleEvent, useZodForm } from '@sd/client';
import { Dialog, useDialog, UseDialogProps } from '@sd/ui';
import { Icon } from '~/components';
import { useLocale } from '~/hooks';
interface Props extends UseDialogProps {
onSuccess: () => void;
@ -19,16 +20,18 @@ export default (props: Props) => {
const form = useZodForm();
const { t } = useLocale();
return (
<Dialog
form={form}
onSubmit={form.handleSubmit(() => deleteLocation.mutateAsync(props.locationId))}
dialog={useDialog(props)}
title="Delete Location"
title={t('delete_location')}
icon={<Icon name="DeleteLocation" size={28} />}
description="Deleting a location will also remove all files associated with it from the Spacedrive database, the files themselves will not be deleted."
description={t('delete_location_description')}
ctaDanger
ctaLabel="Delete"
ctaLabel={t('delete')}
/>
);
};

View file

@ -13,6 +13,7 @@ import {
} from '@sd/client';
import { Button, Card, Divider, Input, Select, SelectOption, Tooltip } from '@sd/ui';
import { ErrorMessage, Form, z } from '@sd/ui/src/forms';
import { useLocale } from '~/hooks';
import { InputKinds, RuleInput, validateInput } from './RuleInput';
@ -129,6 +130,8 @@ const RulesForm = ({ onSubmitted }: Props) => {
if (form.formState.isSubmitSuccessful) onSubmitted?.();
}, [form.formState.isSubmitSuccessful, onSubmitted]);
const { t } = useLocale();
return (
// The portal is required for Form because this component can be nested inside another form element
<>
@ -154,11 +157,11 @@ const RulesForm = ({ onSubmitted }: Props) => {
}
>
<div className="mb-2 grid w-full grid-cols-4 items-center pt-2 text-center text-[11px] font-bold">
<h3>Type</h3>
<h3>Value</h3>
<h3>{t('type')}</h3>
<h3>{t('value')}</h3>
<h3 className="flex items-center justify-center gap-1">
Mode
<Tooltip label="By default, an indexer rule functions as a Reject list, resulting in the exclusion of any files that match its criteria. Enabling this option will transform it into a Allow list, allowing the location to solely index files that meet its specified rules.">
{t('mode')}
<Tooltip label={t('indexer_rule_reject_allow_label')}>
<Info />
</Tooltip>
</h3>
@ -253,7 +256,7 @@ const RulesForm = ({ onSubmitted }: Props) => {
variant="gray"
onClick={() => remove(index)}
>
<Tooltip label="Delete rule">
<Tooltip label={t('delete_rule')}>
<Trash size={14} />
</Tooltip>
</Button>
@ -281,7 +284,7 @@ const RulesForm = ({ onSubmitted }: Props) => {
</div>
<Divider className="my-[25px]" />
<Button form={formId} type="submit" variant="accent" className="mx-auto w-[90px]">
Save
{t('save')}
</Button>
<div className="text-center">
<ErrorMessage name={REMOTE_ERROR_FORM_FIELD} variant="large" className="mt-2" />

View file

@ -11,6 +11,7 @@ import {
} from '@sd/client';
import { Button, Card, dialogManager, Tooltip } from '@sd/ui';
import { Folder } from '~/components';
import { useLocale } from '~/hooks';
import DeleteDialog from './DeleteDialog';
@ -22,6 +23,8 @@ export default ({ location }: Props) => {
const navigate = useNavigate();
const [hide, setHide] = useState(false);
const { t } = useLocale();
const fullRescan = useLibraryMutation('locations.fullRescan');
const onlineLocations = useOnlineLocations();
@ -67,7 +70,7 @@ export default ({ location }: Props) => {
)}
/>
<span className="ml-1.5 text-xs text-ink-dull">
{online ? 'Online' : 'Offline'}
{online ? t('online') : t('offline')}
</span>
</Button>
<Button
@ -77,7 +80,7 @@ export default ({ location }: Props) => {
variant="gray"
className="pointer-events-none flex !px-2 !py-1.5"
>
<p className="text-ink-dull">Size:</p>
<p className="text-ink-dull">{t('size')}:</p>
<span className="ml-1.5 text-xs text-ink-dull">{`${byteSize(
location.size_in_bytes
)}`}</span>
@ -96,7 +99,7 @@ export default ({ location }: Props) => {
));
}}
>
<Tooltip label="Delete Location">
<Tooltip label={t('delete_location')}>
<Trash className="h-4 w-4" />
</Tooltip>
</Button>
@ -109,7 +112,7 @@ export default ({ location }: Props) => {
fullRescan.mutate({ location_id: location.id, reidentify_objects: false });
}}
>
<Tooltip label="Rescan Location">
<Tooltip label={t('rescan_location')}>
<Repeat className="h-4 w-4" />
</Tooltip>
</Button>

View file

@ -2,6 +2,7 @@ import { useMemo, useState } from 'react';
import { useDebounce } from 'use-debounce';
import { useCache, useLibraryQuery, useNodes } from '@sd/client';
import { SearchInput } from '@sd/ui';
import { useLocale } from '~/hooks';
import { Heading } from '../../Layout';
import { AddLocationButton } from './AddLocationButton';
@ -23,11 +24,13 @@ export const Component = () => {
[debouncedSearch, locations]
);
const { t } = useLocale();
return (
<>
<Heading
title="Locations"
description="Manage your storage locations."
title={t('locations')}
description={t('locations_description')}
rightArea={
<div className="flex flex-row items-center space-x-5">
<SearchInput

View file

@ -9,19 +9,18 @@ import {
} from '@sd/client';
import { Button } from '@sd/ui';
import { startPairing } from '~/app/p2p/pairing';
import { useLocale } from '~/hooks';
import { Heading } from '../Layout';
export const Component = () => {
const isPairingEnabled = useFeatureFlag('p2pPairing');
const node = useBridgeQuery(['nodeState']);
const { t } = useLocale();
return (
<>
<Heading
title="Nodes"
description="Manage the nodes connected to this library. A node is an instance of Spacedrive's backend, running on a device or server. Each node carries a copy of the database and synchronizes via peer-to-peer connections in realtime."
/>
<Heading title={t('nodes')} description={t('nodes_description')} />
{/* TODO: Show paired nodes + unpair button */}
{/* TODO: Replace with modal */}
@ -36,6 +35,7 @@ export const Component = () => {
// TODO: This entire component shows a UI which is pairing by node but that is just not how it works.
function IncorrectP2PPairingPane() {
const { t } = useLocale();
const onlineNodes = useDiscoveredPeers();
const connectedNodes = useConnectedPeers();
const p2pPair = useBridgeMutation('p2p.pair', {
@ -75,13 +75,13 @@ function IncorrectP2PPairingPane() {
);
}}
>
Pair
{t('pair')}
</Button>
</div>
))}
</div>
<div className="flex-[50%]">
<h1 className="mt-4">Connected</h1>
<h1 className="mt-4">{t('connected')}</h1>
{[...connectedNodes.entries()].map(([id, node]) => (
<div key={id} className="flex space-x-2">
<p>{id}</p>

View file

@ -12,7 +12,7 @@ import { Button, Card, Form, InputField, Label, Tooltip, z } from '@sd/ui';
import { SearchContextProvider, useSearch } from '~/app/$libraryId/Search';
import { AppliedFilters } from '~/app/$libraryId/Search/AppliedFilters';
import { Heading } from '~/app/$libraryId/settings/Layout';
import { useDebouncedFormWatch } from '~/hooks';
import { useDebouncedFormWatch, useLocale } from '~/hooks';
export const Component = () => {
const savedSearches = useLibraryQuery(['search.saved.list'], { suspense: true });
@ -64,6 +64,8 @@ const schema = z.object({
});
function EditForm({ savedSearch, onDelete }: { savedSearch: SavedSearch; onDelete: () => void }) {
const { t } = useLocale();
const updateSavedSearch = useLibraryMutation('search.saved.update');
const deleteSavedSearch = useLibraryMutation('search.saved.delete');
@ -92,7 +94,7 @@ function EditForm({ savedSearch, onDelete }: { savedSearch: SavedSearch; onDelet
<Form form={form}>
<div className="flex flex-col gap-4">
<div className="flex flex-row items-end gap-2">
<InputField label="Name" {...form.register('name')} />
<InputField label={t('name')} {...form.register('name')} />
<Button
variant="gray"
className="h-[38px]"
@ -102,13 +104,13 @@ function EditForm({ savedSearch, onDelete }: { savedSearch: SavedSearch; onDelet
onDelete();
}}
>
<Tooltip label="Delete Tag">
<Tooltip label={t('delete_tag')}>
<Trash className="h-4 w-4" />
</Tooltip>
</Button>
</div>
<div className="flex flex-col gap-1">
<Label className="font-medium">Filters</Label>
<Label className="font-medium">{t('filters')}</Label>
<div className="flex flex-col items-start gap-2">
<SearchContextProvider search={search}>
<AppliedFilters allowRemove={false} />

View file

@ -1,9 +1,12 @@
import { useLocale } from '~/hooks';
import { Heading } from '../Layout';
export const Component = () => {
const { t } = useLocale();
return (
<>
<Heading title="Security" description="Keep your client safe." />
<Heading title={t('security')} description={t('security_description')} />
</>
);
};

View file

@ -1,9 +1,12 @@
import { useLocale } from '~/hooks';
import { Heading } from '../Layout';
export const Component = () => {
const { t } = useLocale();
return (
<>
<Heading title="Sharing" description="Manage who has access to your libraries." />
<Heading title={t('sharing')} description={t('sharing_description')} />
</>
);
};

View file

@ -1,9 +1,12 @@
import { useLocale } from '~/hooks';
import { Heading } from '../Layout';
export const Component = () => {
const { t } = useLocale();
return (
<>
<Heading title="Sync" description="Manage how Spacedrive syncs." />
<Heading title={t('sync')} description={t('sync_description')} />
</>
);
};

View file

@ -9,6 +9,7 @@ import {
} from '@sd/client';
import { Dialog, InputField, useDialog, UseDialogProps, z } from '@sd/ui';
import { ColorPicker } from '~/components';
import { useLocale } from '~/hooks';
const schema = z.object({
name: z.string().trim().min(1).max(24),
@ -73,20 +74,22 @@ export default (
}
});
const { t } = useLocale();
return (
<Dialog
invertButtonFocus
form={form}
onSubmit={onSubmit}
dialog={useDialog(props)}
title="Create New Tag"
description="Choose a name and color."
ctaLabel="Create"
title={t('create_new_tag')}
description={t('create_new_tag_description')}
ctaLabel={t('create')}
>
<div className="relative mt-3 ">
<InputField
{...form.register('name', { required: true })}
placeholder="Name"
placeholder={t('name')}
maxLength={24}
icon={<ColorPicker control={form.control} name="color" />}
/>

View file

@ -1,5 +1,6 @@
import { useLibraryMutation, usePlausibleEvent, useZodForm } from '@sd/client';
import { Dialog, useDialog, UseDialogProps } from '@sd/ui';
import { useLocale } from '~/hooks';
interface Props extends UseDialogProps {
tagId: number;
@ -18,15 +19,17 @@ export default (props: Props) => {
const form = useZodForm();
const { t } = useLocale();
return (
<Dialog
form={form}
dialog={useDialog(props)}
onSubmit={form.handleSubmit(() => deleteTag.mutateAsync(props.tagId))}
title="Delete Tag"
description="Are you sure you want to delete this tag? This cannot be undone and tagged files will be unlinked."
title={t('delete_tag')}
description={t('delete_tag_description')}
ctaDanger
ctaLabel="Delete"
ctaLabel={t('delete')}
/>
);
};

View file

@ -2,7 +2,7 @@ import { Trash } from '@phosphor-icons/react';
import { Tag, useLibraryMutation, useZodForm } from '@sd/client';
import { Button, dialogManager, Form, InputField, Switch, Tooltip, z } from '@sd/ui';
import { ColorPicker } from '~/components';
import { useDebouncedFormWatch } from '~/hooks';
import { useDebouncedFormWatch, useLocale } from '~/hooks';
import Setting from '../../Setting';
import DeleteDialog from './DeleteDialog';
@ -26,6 +26,8 @@ interface Props {
export default ({ tag, onDelete }: Props) => {
const updateTag = useLibraryMutation('tags.update');
const { t } = useLocale();
const form = useZodForm({
schema,
mode: 'onChange',
@ -46,14 +48,14 @@ export default ({ tag, onDelete }: Props) => {
<div className="flex justify-between">
<div className="mb-10 flex flex-row space-x-3">
<InputField
label="Color"
label={t('color')}
maxLength={7}
value={form.watch('color')?.trim() ?? '#ffffff'}
icon={<ColorPicker control={form.control} name="color" />}
{...form.register('color')}
/>
<InputField maxLength={24} label="Name" {...form.register('name')} />
<InputField maxLength={24} label={t('name')} {...form.register('name')} />
</div>
<Button
variant="gray"
@ -64,7 +66,7 @@ export default ({ tag, onDelete }: Props) => {
))
}
>
<Tooltip label="Delete Tag">
<Tooltip label={t('delete_tag')}>
<Trash className="h-4 w-4" />
</Tooltip>
</Button>
@ -72,15 +74,15 @@ export default ({ tag, onDelete }: Props) => {
<div className="flex flex-col gap-2">
<Setting
mini
title="Hide in Library search"
description="Hide files with this tag from results when searching entire library."
title={t('hide_in_library_search')}
description={t('hide_in_library_search_description')}
>
<Switch />
</Setting>
<Setting
mini
title="Hide in sidebar"
description="Prevent this tag from showing in the sidebar of the app."
title={t('hide_in_sidebar')}
description={t('hide_in_sidebar_description')}
>
<Switch />
</Setting>

View file

@ -4,7 +4,7 @@ import { Tag, useCache, useLibraryQuery, useNodes } from '@sd/client';
import { Button, Card, dialogManager } from '@sd/ui';
import { Heading } from '~/app/$libraryId/settings/Layout';
import { TagsSettingsParamsSchema } from '~/app/route-schemas';
import { useZodRouteParams } from '~/hooks';
import { useLocale, useZodRouteParams } from '~/hooks';
import CreateDialog from './CreateDialog';
import EditForm from './EditForm';
@ -31,11 +31,13 @@ export const Component = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const { t } = useLocale();
return (
<>
<Heading
title="Tags"
description="Manage your tags."
title={t('tags')}
description={t('tags_description')}
rightArea={
<div className="flex-row space-x-2">
<Button
@ -45,7 +47,7 @@ export const Component = () => {
dialogManager.create((dp) => <CreateDialog {...dp} />);
}}
>
Create Tag
{t('create_tag')}
</Button>
</div>
}
@ -74,7 +76,7 @@ export const Component = () => {
onDelete={() => setSelectedTag(null)}
/>
) : (
<div className="text-sm font-medium text-gray-400">No Tag Selected</div>
<div className="text-sm font-medium text-gray-400">{t('no_tag_selected')}</div>
)}
</>
);

View file

@ -8,6 +8,7 @@ import {
useZodForm
} from '@sd/client';
import { Dialog, InputField, useDialog, UseDialogProps, z } from '@sd/ui';
import { useLocale } from '~/hooks';
import { usePlatform } from '~/util/Platform';
const schema = z.object({
@ -21,6 +22,8 @@ const schema = z.object({
});
export default (props: UseDialogProps) => {
const { t } = useLocale();
const navigate = useNavigate();
const queryClient = useQueryClient();
const submitPlausibleEvent = usePlausibleEvent();
@ -60,14 +63,14 @@ export default (props: UseDialogProps) => {
onSubmit={onSubmit}
dialog={useDialog(props)}
submitDisabled={!form.formState.isValid}
title="Create New Library"
description="Libraries are a secure, on-device database. Your files remain where they are, the Library catalogs them and stores all Spacedrive related data."
ctaLabel={form.formState.isSubmitting ? 'Creating library...' : 'Create library'}
title={t('create_new_library')}
description={t('create_new_library_description')}
ctaLabel={form.formState.isSubmitting ? t('creating_library') : t('create_library')}
>
<div className="mt-5 space-y-4">
<InputField
{...form.register('name')}
label="Library name"
label={t('library_name')}
placeholder={'e.g. "James\' Library"'}
size="md"
/>

View file

@ -2,6 +2,7 @@ import { useQueryClient } from '@tanstack/react-query';
import { useNavigate } from 'react-router';
import { useBridgeMutation, usePlausibleEvent, useZodForm } from '@sd/client';
import { Dialog, useDialog, UseDialogProps } from '@sd/ui';
import { useLocale } from '~/hooks';
import { usePlatform } from '~/util/Platform';
interface Props extends UseDialogProps {
@ -9,6 +10,8 @@ interface Props extends UseDialogProps {
}
export default function DeleteLibraryDialog(props: Props) {
const { t } = useLocale();
const submitPlausibleEvent = usePlausibleEvent();
const queryClient = useQueryClient();
const platform = usePlatform();
@ -43,10 +46,10 @@ export default function DeleteLibraryDialog(props: Props) {
form={form}
onSubmit={onSubmit}
dialog={useDialog(props)}
title="Delete Library"
description="Deleting a library will permanently the database, the files themselves will not be deleted."
title={t('delete_library')}
description={t('delete_library_description')}
ctaDanger
ctaLabel="Delete"
ctaLabel={t('delete')}
/>
);
}

View file

@ -2,6 +2,7 @@ import { Pencil, Trash } from '@phosphor-icons/react';
import { LibraryConfigWrapped } from '@sd/client';
import { Button, ButtonLink, Card, dialogManager, Tooltip } from '@sd/ui';
import { Icon } from '~/components';
import { useLocale } from '~/hooks';
import DeleteDialog from './DeleteDialog';
@ -11,6 +12,8 @@ interface Props {
}
export default (props: Props) => {
const { t } = useLocale();
return (
<Card className="items-center">
{/* <DotsSixVertical weight="bold" className="mt-[15px] mr-3 opacity-30" /> */}
@ -20,7 +23,7 @@ export default (props: Props) => {
{props.library.config.name}
{props.current && (
<span className="ml-2 rounded bg-accent px-1.5 py-[2px] text-xs font-medium text-white">
Current
{t('current')}
</span>
)}
</h3>
@ -37,7 +40,7 @@ export default (props: Props) => {
to={`/${props.library.uuid}/settings/library/general`}
variant="gray"
>
<Tooltip label="Edit Library">
<Tooltip label={t('edit_library')}>
<Pencil className="h-4 w-4" />
</Tooltip>
</ButtonLink>
@ -50,7 +53,7 @@ export default (props: Props) => {
));
}}
>
<Tooltip label="Delete Library">
<Tooltip label={t('delete_library')}>
<Trash className="h-4 w-4" />
</Tooltip>
</Button>

View file

@ -1,3 +1,4 @@
import { t } from 'i18next';
import { useBridgeQuery, useCache, useLibraryContext, useNodes } from '@sd/client';
import { Button, dialogManager } from '@sd/ui';
@ -15,8 +16,8 @@ export const Component = () => {
return (
<>
<Heading
title="Libraries"
description="The database contains all library data and file metadata."
title={t('libraries')}
description={t("libraries_description")}
rightArea={
<div className="flex-row space-x-2">
<Button
@ -26,7 +27,7 @@ export const Component = () => {
dialogManager.create((dp) => <CreateDialog {...dp} />);
}}
>
Add Library
{t("add_library")}
</Button>
</div>
}

View file

@ -1,9 +1,11 @@
import { Input, Switch } from '@sd/ui';
import { useLocale } from '~/hooks';
import { Heading } from '../Layout';
import Setting from '../Setting';
export const Component = () => {
const { t } = useLocale();
return (
<>
<Heading
@ -27,7 +29,7 @@ export const Component = () => {
<Input className="grow" disabled defaultValue="https://p2p.spacedrive.com" />
<div className="mt-1 flex justify-end">
<a className="p-1 text-sm font-bold text-accent hover:text-accent">
Change
{t('change')}
</a>
</div>
</div>

View file

@ -3,6 +3,7 @@ import { AppLogo } from '@sd/assets/images';
import { Discord, Github } from '@sd/assets/svgs/brands';
import { useBridgeQuery, useDebugStateEnabler } from '@sd/client';
import { Button, Divider } from '@sd/ui';
import { useLocale } from '~/hooks';
import { useOperatingSystem } from '~/hooks/useOperatingSystem';
import { usePlatform } from '~/util/Platform';
@ -14,6 +15,8 @@ export const Component = () => {
os === 'browser' ? 'Web' : os == 'macOS' ? os : os.charAt(0).toUpperCase() + os.slice(1);
const onClick = useDebugStateEnabler();
const { t } = useLocale();
return (
<div className="h-auto">
<div className="flex flex-row items-center">
@ -44,7 +47,7 @@ export const Component = () => {
variant="gray"
>
<Discord className="h-4 w-4 fill-ink" />
Join Discord
{t('join_discord')}
</Button>
<Button
href="https://github.com/spacedriveapp/spacedrive"
@ -53,7 +56,7 @@ export const Component = () => {
variant="accent"
>
<Github className="h-4 w-4 fill-white" />
Star on GitHub
{t('star_on_github')}
</Button>
<Button
onClick={() => {
@ -63,27 +66,18 @@ export const Component = () => {
variant="accent"
>
<Globe className="h-4 w-4" />
Website
{t('website')}
</Button>
</div>
<Divider />
<div className="my-5">
<h1 className="mb-3 text-lg font-bold text-ink">Vision</h1>
<p className="w-full text-sm text-ink-faint">
Many of us have multiple cloud accounts, drives that arent backed up and data
at risk of loss. We depend on cloud services like Google Photos and iCloud, but
are locked in with limited capacity and almost zero interoperability between
services and operating systems. Photo albums shouldnt be stuck in a device
ecosystem, or harvested for advertising data. They should be OS agnostic,
permanent and personally owned. Data we create is our legacy, that will long
outlive usopen source technology is the only way to ensure we retain absolute
control over the data that defines our lives, at unlimited scale.
</p>
<h1 className="mb-3 text-lg font-bold text-ink">{t('about_vision_title')}</h1>
<p className="w-full text-sm text-ink-faint">{t('about_vision_text')}</p>
</div>
<Divider />
<div>
<h1 className="my-5 text-lg font-bold text-ink">
Meet the contributors behind Spacedrive
{t('meet_contributors_behind_spacedrive')}
</h1>
<img
src="https://contrib.rocks/image?repo=spacedriveapp/spacedrive&columns=12&anon=1"

View file

@ -1,7 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import clsx from 'clsx';
import Markdown from 'react-markdown';
import { useIsDark } from '~/hooks';
import { useIsDark, useLocale } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import { Heading } from '../Layout';
@ -13,9 +13,14 @@ export const Component = () => {
fetch(`${platform.landingApiOrigin}/api/releases`).then((r) => r.json())
);
const { t } = useLocale();
return (
<>
<Heading title="Changelog" description="See what cool new features we're making" />
<Heading
title={t('changelog_page_title')}
description={t('changelog_page_description')}
/>
{changelog.data?.map((release: any) => (
<article
key={release.version}

View file

@ -1,9 +1,12 @@
import { useLocale } from '~/hooks';
import { Heading } from '../Layout';
export const Component = () => {
const { t } = useLocale();
return (
<>
<Heading title="Support" description="" />
<Heading title={t('support')} description="" />
</>
);
};

24
interface/app/I18n.ts Normal file
View file

@ -0,0 +1,24 @@
import i18n from 'i18next';
// import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
import * as resources from 'virtual:i18next-loader';
i18n
// // detect user language
// // learn more: https://github.com/i18next/i18next-browser-languageDetector
// .use(LanguageDetector)
// pass the i18n instance to react-i18next.
.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
resources,
load: 'languageOnly',
// resources,
fallbackLng: 'en',
ns: ['common'],
fallbackNS: 'common',
defaultNS: 'common'
});
export default i18n;

View file

@ -19,6 +19,8 @@ import onboardingRoutes from './onboarding';
import { RootContext } from './RootContext';
import './style.scss';
// I18n needs to be bundled here.
import './I18n';
function RenderSolid() {
const ref = useRef<HTMLDivElement>(null);

View file

@ -1,7 +1,7 @@
import { AlphaBg, AlphaBg_Light, AppLogo } from '@sd/assets/images';
import { Discord } from '@sd/assets/svgs/brands';
import { Button, ButtonLink } from '@sd/ui';
import { useIsDark } from '~/hooks';
import { useIsDark, useLocale } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import { OnboardingContainer } from './components';
@ -10,6 +10,8 @@ export default function OnboardingAlpha() {
const platform = usePlatform();
const isDark = useIsDark();
const { t } = useLocale();
return (
<OnboardingContainer>
<div className="relative w-screen text-center">
@ -23,13 +25,9 @@ export default function OnboardingAlpha() {
<img src={AppLogo} alt="Spacedrive" className="h-8 w-8" />
<h1 className="text-[25px] font-semibold">Spacedrive</h1>
</div>
<h1 className="text-[40px] font-bold">Alpha Release</h1>
<h1 className="text-[40px] font-bold">{t('alpha_release_title')}</h1>
<p className="mx-auto w-full max-w-[450px] text-sm text-ink-faint">
We are delighted for you to try Spacedrive, now in Alpha release, showcasing
exciting new features. As with any initial release, this version may contain
some bugs. We kindly request your assistance in reporting any issues you
encounter on our Discord channel. Your valuable feedback will greatly
contribute to enhancing the user experience.
{t('alpha_release_description')}
</p>
<div className="mt-0 flex w-full items-center justify-center gap-2">
<Button
@ -38,10 +36,10 @@ export default function OnboardingAlpha() {
variant="gray"
>
<Discord className="h-4 w-4 fill-ink" />
Join Discord
{t('join_discord')}
</Button>
<ButtonLink to="../new-library" replace variant="accent">
Continue
{t('continue')}
</ButtonLink>
</div>
</div>

View file

@ -1,13 +1,16 @@
import { Loader } from '@sd/ui';
import { useLocale } from '~/hooks';
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './components';
export default function OnboardingCreatingLibrary() {
const { t } = useLocale();
return (
<OnboardingContainer>
<span className="text-6xl">🛠</span>
<OnboardingTitle>Creating your library</OnboardingTitle>
<OnboardingDescription>Creating your library...</OnboardingDescription>
<OnboardingTitle>{t('creating_your_library')}</OnboardingTitle>
<OnboardingDescription>{t('creating_your_library')}...</OnboardingDescription>
<Loader className="mt-5" />
</OnboardingContainer>
);

View file

@ -2,6 +2,7 @@ import { fda } from '@sd/assets/videos';
import { useNavigate } from 'react-router';
import { Button } from '@sd/ui';
import { Icon } from '~/components';
import { useLocale } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './components';
@ -10,20 +11,19 @@ export const FullDisk = () => {
const { requestFdaMacos } = usePlatform();
const navigate = useNavigate();
const { t } = useLocale();
return (
<OnboardingContainer>
<Icon name="HDD" size={80} />
<OnboardingTitle>Full disk access</OnboardingTitle>
<OnboardingDescription>
To provide the best experience, we need access to your disk in order to index your
files. Your files are only available to you.
</OnboardingDescription>
<OnboardingTitle>{t('full_disk_access')}</OnboardingTitle>
<OnboardingDescription>{t('full_disk_access_description')}</OnboardingDescription>
<div className="mt-5 w-full max-w-[450px]">
<video className="rounded-md" autoPlay loop muted controls={false} src={fda} />
</div>
<div className="flex items-center gap-3">
<Button onClick={requestFdaMacos} variant="gray" size="sm" className="my-5">
Open Settings
{t('open_settings')}
</Button>
</div>
<div className="flex gap-3">
@ -35,7 +35,7 @@ export const FullDisk = () => {
size="sm"
className="mt-8"
>
Continue
{t('continue')}
</Button>
</div>
</OnboardingContainer>

View file

@ -9,22 +9,21 @@ import {
import { Button } from '@sd/ui';
import { Icon } from '~/components';
import { AuthRequiredOverlay } from '~/components/AuthRequiredOverlay';
import { useRouteTitle } from '~/hooks';
import { useLocale, useRouteTitle } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './components';
export function JoinLibrary() {
const { t } = useLocale();
useRouteTitle('Join Library');
return (
<OnboardingContainer>
<Icon name="Database" size={80} />
<OnboardingTitle>Join a Library</OnboardingTitle>
<OnboardingDescription>
Libraries are a secure, on-device database. Your files remain where they are, the
Library catalogs them and stores all Spacedrive related data.
</OnboardingDescription>
<OnboardingTitle>{t('join_library')}</OnboardingTitle>
<OnboardingDescription>{t('join_library_description')}</OnboardingDescription>
<div className="mt-2">
<span>Cloud Libraries</span>
@ -38,6 +37,8 @@ export function JoinLibrary() {
}
function CloudLibraries() {
const { t } = useLocale();
const cloudLibraries = useBridgeQuery(['cloud.library.list']);
const joinLibrary = useBridgeMutation(['cloud.library.join']);
@ -45,7 +46,7 @@ function CloudLibraries() {
const queryClient = useQueryClient();
const platform = usePlatform();
if (cloudLibraries.isLoading) return <span>Loading...</span>;
if (cloudLibraries.isLoading) return <span>{t('loading')}...</span>;
return (
<>

View file

@ -14,7 +14,7 @@ import { useNavigate } from 'react-router';
import { SystemLocations, useBridgeQuery } from '@sd/client';
import { Button, Form, RadixCheckbox } from '@sd/ui';
import { Icon, TruncatedText } from '~/components';
import { useIsDark, useOperatingSystem } from '~/hooks';
import { useIsDark, useLocale, useOperatingSystem } from '~/hooks';
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './components';
import { useOnboardingContext } from './context';
@ -54,6 +54,8 @@ const LocationIcon = (props: { location: SystemLocation; active?: boolean }) =>
};
export default function OnboardingLocations() {
const { t } = useLocale();
const navigate = useNavigate();
const os = useOperatingSystem(true);
@ -103,11 +105,8 @@ export default function OnboardingLocations() {
className="relative left-[-25px] z-[0] brightness-[0.6]"
/>
</div>
<OnboardingTitle>Add Locations</OnboardingTitle>
<OnboardingDescription>
Enhance your Spacedrive experience by adding your favorite locations to your
personal library, for seamless and efficient file management.
</OnboardingDescription>
<OnboardingTitle>{t('add_locations')}</OnboardingTitle>
<OnboardingDescription>{t('add_location_description')}</OnboardingDescription>
{systemLocations && (
<div className="my-6">
@ -115,7 +114,7 @@ export default function OnboardingLocations() {
name="toggle-all"
className="mb-1.5 justify-end"
labelClassName="!ml-1.5"
label="Toggle All"
label={t('toggle_all')}
checked={toggled}
onCheckedChange={(value) => {
if (typeof value !== 'boolean') return;
@ -179,7 +178,7 @@ export default function OnboardingLocations() {
)}
<Button type="submit" className="text-center" variant="accent" size="sm">
Continue
{t('continue')}
</Button>
</OnboardingContainer>
</Form>

View file

@ -3,10 +3,13 @@ import { useNavigate } from 'react-router';
import { auth, useBridgeQuery } from '@sd/client';
import { Button, ButtonLink, Loader } from '@sd/ui';
import { LoginButton } from '~/components/LoginButton';
import { useLocale } from '~/hooks';
import { OnboardingContainer } from './components';
export default function OnboardingLogin() {
const { t } = useLocale();
const authState = auth.useStateSnapshot();
const navigate = useNavigate();
@ -40,18 +43,18 @@ export default function OnboardingLogin() {
size="md"
className="text-center"
>
Continue
{t('continue')}
</ButtonLink>
<div className="space-x-2 text-center text-sm">
<span>Not you?</span>
<span>{t('not_you')}</span>
<Button
onClick={auth.logout}
variant="bare"
size="md"
className="border-none !p-0 font-normal text-accent-deep hover:underline"
>
Log out
{t('log_out')}
</Button>
</div>
</div>
@ -77,11 +80,11 @@ export default function OnboardingLogin() {
onLogin={() => navigate('../new-library', { replace: true })}
size="md"
>
Log in with browser
{t('log_in_with_browser')}
</LoginButton>
<div className="space-x-2 text-center text-sm">
<span>Want to do this later?</span>
<span>{t('want_to_do_this_later')}</span>
<ButtonLink
to="../new-library"
variant="bare"
@ -89,7 +92,7 @@ export default function OnboardingLogin() {
className="border-none !p-0 font-normal text-accent-deep hover:underline"
replace
>
Skip login
{t('skip_login')}
</ButtonLink>
</div>
</div>

View file

@ -3,12 +3,14 @@ import { useNavigate } from 'react-router';
import { useFeatureFlag } from '@sd/client';
import { Button, Form, InputField } from '@sd/ui';
import { Icon } from '~/components';
import { useOperatingSystem } from '~/hooks';
import { useLocale, useOperatingSystem } from '~/hooks';
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './components';
import { useOnboardingContext } from './context';
export default function OnboardingNewLibrary() {
const { t } = useLocale();
const navigate = useNavigate();
const os = useOperatingSystem();
const form = useOnboardingContext().forms.useForm('new-library');
@ -30,20 +32,17 @@ export default function OnboardingNewLibrary() {
>
<OnboardingContainer>
<Icon name="Database" size={80} />
<OnboardingTitle>Create a Library</OnboardingTitle>
<OnboardingDescription>
Libraries are a secure, on-device database. Your files remain where they are,
the Library catalogs them and stores all Spacedrive related data.
</OnboardingDescription>
<OnboardingTitle>{t('create_library')}</OnboardingTitle>
<OnboardingDescription>{t('create_library_description')}</OnboardingDescription>
{importMode ? (
<div className="mt-7 space-x-2">
<Button onClick={handleImport} variant="accent" size="sm">
Import
{t('import')}
</Button>
<span className="px-2 text-xs font-bold text-ink-faint">OR</span>
<Button onClick={() => setImportMode(false)} variant="outline" size="sm">
Create new library
{t('create_new_library')}
</Button>
</div>
) : (
@ -63,7 +62,7 @@ export default function OnboardingNewLibrary() {
size="sm"
disabled={!form.formState.isValid}
>
New library
{t('new_library')}
</Button>
{/* <span className="px-2 text-xs font-bold text-ink-faint">OR</span>
<Button onClick={() => setImportMode(true)} variant="outline" size="sm">
@ -72,13 +71,13 @@ export default function OnboardingNewLibrary() {
</div>
{cloudFeatureFlag && (
<>
<span className="my-4 text-sm text-ink-faint">OR</span>
<span className="my-4 text-sm text-ink-faint">{t('or')}</span>
<Button
onClick={() => {
navigate('../join-library');
}}
>
Join a Library
{t('join_library')}
</Button>
</>
)}

View file

@ -1,11 +1,13 @@
import { Info, Question } from '@phosphor-icons/react';
import { Button, Form, RadioGroupField } from '@sd/ui';
import { useLocale } from '~/hooks';
import { usePlatform } from '~/util/Platform';
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './components';
import { shareTelemetry, useOnboardingContext } from './context';
export default function OnboardingPrivacy() {
const { t } = useLocale();
const { forms, submit } = useOnboardingContext();
const platform = usePlatform();
@ -18,11 +20,8 @@ export default function OnboardingPrivacy() {
className="flex flex-col items-center"
>
<OnboardingContainer>
<OnboardingTitle>Your Privacy</OnboardingTitle>
<OnboardingDescription>
Spacedrive is built for privacy, that's why we're open source and local first.
So we'll make it very clear what data is shared with us.
</OnboardingDescription>
<OnboardingTitle>{t('your_privacy')}</OnboardingTitle>
<OnboardingDescription>{t('privacy_description')}</OnboardingDescription>
<div className="m-6">
<RadioGroupField.Root {...form.register('shareTelemetry')}>
{shareTelemetry.options.map(({ value, heading, description }) => (
@ -43,11 +42,11 @@ export default function OnboardingPrivacy() {
}}
>
<Info size={13} />
More info
{t('more_info')}
</Button>
</div>
<Button type="submit" className="mt-5 text-center" variant="accent" size="sm">
Continue
{t('continue')}
</Button>
</OnboardingContainer>
</Form>

View file

@ -18,6 +18,7 @@ import {
UseDialogProps,
z
} from '@sd/ui';
import { useLocale } from '~/hooks';
type Node = {
name: string;
@ -35,16 +36,18 @@ function OriginatorDialog({
}: { pairingId: number; node: Node } & UseDialogProps) {
const pairingStatus = usePairingStatus(pairingId);
const { t } = useLocale();
// TODO: If dialog closes before finished, cancel pairing
return (
<Dialog
form={useZodForm({ schema: z.object({}) })}
dialog={useDialog(props)}
title={`Pairing with ${node.name}`}
title={t('pairing_with_node', { node: node.name })}
loading={true}
submitDisabled={pairingStatus?.type !== 'PairingComplete'}
ctaLabel="Done"
ctaLabel={t('done')}
// closeLabel="Cancel"
onSubmit={async () => {
// TODO: Change into the new library
@ -87,6 +90,8 @@ function PairingResponder({ pairingId }: { pairingId: number }) {
);
const pairingResponse = useBridgeMutation('p2p.pairingResponse');
const { t } = useLocale();
return (
<>
{selectedLibrary ? (
@ -111,10 +116,10 @@ function PairingResponder({ pairingId }: { pairingId: number }) {
]);
}}
>
Accept
{t('accept')}
</Button>
<Button onClick={() => pairingResponse.mutate([pairingId, { decision: 'reject' }])}>
Reject
{t('reject')}
</Button>
</div>
</>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@ import { Clipboard } from '@phosphor-icons/react';
import { ReactNode } from 'react';
import { useZodForm } from '@sd/client';
import { Button, Dialog, dialogManager, Input, useDialog, UseDialogProps } from '@sd/ui';
import { useLocale } from '~/hooks';
interface Props extends UseDialogProps {
title: string; // dialog title
@ -14,13 +15,15 @@ interface Props extends UseDialogProps {
}
const AlertDialog = (props: Props) => {
const { t } = useLocale();
// maybe a copy-to-clipboard button would be beneficial too
return (
<Dialog
title={props.title}
form={useZodForm()}
dialog={useDialog(props)}
ctaLabel={props.label !== undefined ? props.label : 'Done'}
ctaLabel={props.label !== undefined ? props.label : t('done')}
cancelBtn={props.cancelBtn}
onCancelled={false}
>

View file

@ -26,3 +26,4 @@ export * from './useTheme';
export * from './useWindowState';
export * from './useZodRouteParams';
export * from './useZodSearchParams';
export * from './useLocale';

View file

@ -0,0 +1,12 @@
import { useTranslation } from 'react-i18next';
export const useLocale = (namespace: Parameters<typeof useTranslation>[0] = 'translation') => {
const { i18n, t } = useTranslation(namespace);
const isLocaleReady = Object.keys(i18n).length > 0;
return {
i18n,
t,
isLocaleReady
};
};

View file

@ -0,0 +1,349 @@
{
"meet_title": "Treffen Sie {{title}}",
"about": "Über",
"about_vision_text": "Viele von uns haben mehrere Cloud-Konten, Laufwerke, die nicht gesichert sind, und Daten, die von Verlust bedroht sind. Wir verlassen uns auf Cloud-Dienste wie Google Fotos und iCloud, sind aber mit begrenzter Kapazität eingesperrt und haben fast keine Interoperabilität zwischen Diensten und Betriebssystemen. Fotoalben sollten nicht in einem Geräte-Ökosystem feststecken oder für Werbedaten geerntet werden. Sie sollten betriebssystemunabhängig, dauerhaft und persönlich besessen sein. Daten, die wir erstellen, sind unser Erbe, das uns lange überleben wird - Open-Source-Technologie ist der einzige Weg, um sicherzustellen, dass wir absolute Kontrolle über die Daten behalten, die unser Leben definieren, in unbegrenztem Maßstab.",
"about_vision_title": "Vision",
"accept": "Akzeptieren",
"accessed": "Zugegriffen",
"account": "Konto",
"actions": "Aktionen",
"add": "Hinzufügen",
"add_device": "Gerät hinzufügen",
"add_library": "Bibliothek hinzufügen",
"add_location_description": "Verbessern Sie Ihr Spacedrive-Erlebnis, indem Sie Ihre bevorzugten Standorte zu Ihrer persönlichen Bibliothek hinzufügen, für eine nahtlose und effiziente Dateiverwaltung.",
"add_location_tooltip": "Pfad als indexierten Standort hinzufügen",
"add_locations": "Standorte hinzufügen",
"add_tag": "Tag hinzufügen",
"advanced_settings": "Erweiterte Einstellungen",
"all_jobs_have_been_cleared": "Alle Aufgaben wurden gelöscht.",
"alpha_release_description": "Wir freuen uns, dass Sie Spacedrive jetzt in der Alpha-Version testen, in der aufregende neue Funktionen vorgestellt werden. Wie bei jeder ersten Ausgabe kann diese Version einige Fehler enthalten. Wir bitten Sie freundlich, uns dabei zu helfen, alle auftretenden Probleme auf unserem Discord-Kanal zu melden. Ihr wertvolles Feedback wird wesentlich dazu beitragen, das Benutzererlebnis zu verbessern.",
"alpha_release_title": "Alpha-Version",
"appearance": "Erscheinungsbild",
"appearance_description": "Ändern Sie das Aussehen Ihres Clients.",
"archive": "Archiv",
"archive_coming_soon": "Das Archivieren von Standorten kommt bald...",
"archive_info": "Daten aus der Bibliothek als Archiv extrahieren, nützlich um die Ordnerstruktur des Standorts zu bewahren.",
"are_you_sure": "Sind Sie sicher?",
"assign_tag": "Tag zuweisen",
"audio_preview_not_supported": "Audio-Vorschau wird nicht unterstützt.",
"back": "Zurück",
"backups": "Backups",
"backups_description": "Verwalten Sie Ihre Spacedrive-Datenbank-Backups.",
"blur_effects": "Weichzeichnereffekte",
"blur_effects_description": "Einigen Komponenten wird ein Weichzeichnungseffekt angewendet.",
"cancel": "Abbrechen",
"celcius": "Celsius",
"change": "Ändern",
"changelog": "Änderungsprotokoll",
"changelog_page_description": "Sehen Sie, welche coolen neuen Funktionen wir machen",
"changelog_page_title": "Änderungsprotokoll",
"checksum": "Prüfsumme",
"clear_finished_jobs": "Beendete Aufgaben entfernen",
"client": "Client",
"close": "Schließen",
"clouds": "Clouds",
"color": "Farbe",
"coming_soon": "Demnächst",
"compress": "Komprimieren",
"configure_location": "Standort konfigurieren",
"connected": "Verbunden",
"contacts": "Kontakte",
"contacts_description": "Verwalten Sie Ihre Kontakte in Spacedrive.",
"content_id": "Inhalts-ID",
"continue": "Fortfahren",
"convert_to": "Umwandeln in",
"coordinates": "Koordinaten",
"copied": "Kopiert",
"copy": "Kopieren",
"copy_as_path": "Als Pfad kopieren",
"copy_path_to_clipboard": "Pfad in die Zwischenablage kopieren",
"create": "Erstellen",
"create_library": "Eine Bibliothek erstellen",
"create_library_description": "Bibliotheken sind eine sichere, auf dem Gerät befindliche Datenbank. Ihre Dateien bleiben dort, wo sie sind, die Bibliothek katalogisiert sie und speichert alle mit Spacedrive verbundenen Daten.",
"create_new_library": "Neue Bibliothek erstellen",
"create_new_library_description": "Bibliotheken sind eine sichere, auf dem Gerät befindliche Datenbank. Ihre Dateien bleiben dort, wo sie sind, die Bibliothek katalogisiert sie und speichert alle mit Spacedrive verbundenen Daten.",
"create_new_tag": "Neuen Tag erstellen",
"create_new_tag_description": "Wählen Sie einen Namen und eine Farbe.",
"create_tag": "Tag erstellen",
"created": "Erstellt",
"creating_library": "Bibliothek wird erstellt...",
"creating_your_library": "Ihre Bibliothek wird erstellt",
"current": "Aktuell",
"current_directory": "Aktuelles Verzeichnis",
"current_directory_with_descendants": "Aktuelles Verzeichnis mit Unterverzeichnissen",
"custom": "Benutzerdefiniert",
"cut": "Ausschneiden",
"data_folder": "Datenordner",
"debug_mode": "Debug-Modus",
"debug_mode_description": "Zusätzliche Debugging-Funktionen in der App aktivieren.",
"default": "Standard",
"delete": "Löschen",
"delete_dialog_title": "{{prefix}} {{type}} löschen",
"delete_info": "Damit wird der eigentliche Ordner auf der Festplatte nicht gelöscht. Vorschaumedien werden gelöscht.",
"delete_library": "Bibliothek löschen",
"delete_library_description": "Dies ist dauerhaft, Ihre Dateien werden nicht gelöscht, nur die Spacedrive-Bibliothek.",
"delete_location": "Standort löschen",
"delete_location_description": "Durch das Löschen eines Standorts werden auch alle damit verbundenen Dateien aus der Spacedrive-Datenbank entfernt, die Dateien selbst werden nicht gelöscht.",
"delete_rule": "Regel löschen",
"delete_tag": "Tag löschen",
"delete_tag_description": "Sind Sie sicher, dass Sie diesen Tag löschen möchten? Dies kann nicht rückgängig gemacht werden, und getaggte Dateien werden nicht mehr verlinkt.",
"delete_warning": "Warnung: Dies wird Ihre {{type}} für immer löschen, wir haben noch keinen Papierkorb...",
"description": "Beschreibung",
"deselect": "Abwählen",
"details": "Details",
"devices_coming_soon_tooltip": "Demnächst verfügbar! Diese Alpha-Version beinhaltet noch keinen Bibliothekssync, dieser wird aber sehr bald bereit sein.",
"direction": "Richtung",
"disabled": "Deaktiviert",
"display_formats": "Anzeigeformate",
"display_name": "Anzeigename",
"distance": "Distanz",
"done": "Fertig",
"dont_show_again": "Nicht wieder anzeigen",
"double_click_action": "Doppelklick-Aktion",
"download": "Herunterladen",
"duplicate": "Duplizieren",
"edit": "Bearbeiten",
"edit_library": "Bibliothek bearbeiten",
"edit_location": "Standort bearbeiten",
"enable_networking": "Netzwerk aktivieren",
"encrypt": "Verschlüsseln",
"encrypt_library": "Bibliothek verschlüsseln",
"encrypt_library_coming_soon": "Verschlüsselung der Bibliothek kommt bald",
"encrypt_library_description": "Verschlüsselung für diese Bibliothek aktivieren, dies wird nur die Spacedrive-Datenbank verschlüsseln, nicht die Dateien selbst.",
"ephemeral_notice_browse": "Durchsuchen Sie Ihre Dateien und Ordner direkt von Ihrem Gerät.",
"ephemeral_notice_consider_indexing": "Erwägen Sie, Ihre lokalen Standorte zu indizieren, für eine schnellere und effizientere Erkundung.",
"erase": "Löschen",
"erase_a_file": "Eine Datei löschen",
"erase_a_file_description": "Konfigurieren Sie Ihre Lösch-Einstellungen.",
"error": "Fehler",
"error_loading_original_file": "Fehler beim Laden der Originaldatei",
"expand": "Erweitern",
"explorer": "Explorer",
"export": "Exportieren",
"export_library": "Bibliothek exportieren",
"export_library_coming_soon": "Bibliotheksexport kommt bald",
"export_library_description": "Diese Bibliothek in eine Datei exportieren.",
"extensions": "Erweiterungen",
"extensions_description": "Installieren Sie Erweiterungen, um die Funktionalität dieses Clients zu erweitern.",
"fahrenheit": "Fahrenheit",
"failed_to_cancel_job": "Aufgabe konnte nicht abgebrochen werden.",
"failed_to_clear_all_jobs": "Alle Aufgaben konnten nicht gelöscht werden.",
"failed_to_copy_file_path": "Dateipfad konnte nicht kopiert werden",
"failed_to_duplicate_file": "Datei konnte nicht dupliziert werden",
"failed_to_generate_checksum": "Prüfsumme konnte nicht generiert werden",
"failed_to_generate_labels": "Labels konnten nicht generiert werden",
"failed_to_generate_thumbnails": "Vorschaubilder konnten nicht generiert werden",
"failed_to_load_tags": "Tags konnten nicht geladen werden",
"failed_to_pause_job": "Aufgabe konnte nicht pausiert werden.",
"failed_to_reindex_location": "Neuindizierung des Standorts fehlgeschlagen",
"failed_to_remove_file_from_recents": "Datei konnte nicht aus den letzten Dokumenten entfernt werden",
"failed_to_remove_job": "Aufgabe konnte nicht entfernt werden.",
"failed_to_rescan_location": "Standort konnte nicht erneut gescannt werden",
"failed_to_resume_job": "Aufgabe konnte nicht fortgesetzt werden.",
"failed_to_update_location_settings": "Standorteinstellungen konnten nicht aktualisiert werden",
"favorite": "Favorit",
"file_indexing_rules": "Dateiindizierungsregeln",
"filters": "Filter",
"forward": "Vorwärts",
"full_disk_access": "Vollzugriff auf die Festplatte",
"full_disk_access_description": "Um Ihnen die beste Erfahrung zu bieten, benötigen wir Zugriff auf Ihre Festplatte, um Ihre Dateien zu indizieren. Ihre Dateien sind nur für Sie verfügbar.",
"full_reindex": "Vollständiges Neuindizieren",
"full_reindex_info": "Führen Sie einen vollständigen Rescan dieses Standorts durch.",
"general": "Allgemein",
"general_settings": "Allgemeine Einstellungen",
"general_settings_description": "Allgemeine Einstellungen in Bezug auf diesen Client.",
"generatePreviewMedia_label": "Vorschaumedien für diesen Standort generieren",
"generate_checksums": "Prüfsummen erstellen",
"go_back": "Zurück gehen",
"got_it": "Verstanden",
"grid_gap": "Abstand",
"grid_view": "Rasteransicht",
"grid_view_notice_description": "Erhalten Sie einen visuellen Überblick über Ihre Dateien mit der Rasteransicht. Diese Ansicht zeigt Ihre Dateien und Ordner als Miniaturbilder an, was Ihnen das schnelle Auffinden der gesuchten Datei erleichtert.",
"hidden_label": "Verhindert, dass der Standort und dessen Inhalt in Zusammenfassungskategorien, der Suche und Tags erscheinen, es sei denn, die Option \"Versteckte Elemente anzeigen\" ist aktiviert.",
"hide_in_library_search": "In der Bibliotheksuche verstecken",
"hide_in_library_search_description": "Dateien mit diesem Tag in den Ergebnissen verstecken, wenn die gesamte Bibliothek durchsucht wird.",
"hide_in_sidebar": "In der Seitenleiste verstecken",
"hide_in_sidebar_description": "Verhindern, dass dieser Tag in der Seitenleiste der App angezeigt wird.",
"hide_location_from_view": "Standort und Inhalte aus der Ansicht ausblenden",
"image_labeler_ai_model": "KI-Modell zur Bildetikettierung",
"image_labeler_ai_model_description": "Das Modell, das zur Erkennung von Objekten in Bildern verwendet wird. Größere Modelle sind genauer, aber langsamer.",
"import": "Importieren",
"indexed": "Indiziert",
"indexer_rule_reject_allow_label": "Standardmäßig fungiert eine Indizierungsregel als Ablehnungsliste, wodurch alle Dateien ausgeschlossen werden, die ihren Kriterien entsprechen. Wenn diese Option aktiviert wird, verwandelt sie sich in eine Erlaubnisliste und lässt den Standort nur Dateien indizieren, die ihren spezifischen Regeln entsprechen.",
"indexer_rules": "Indizierungsregeln",
"indexer_rules_info": "Indizierungsregeln ermöglichen es Ihnen, Pfade zu ignorieren, indem Sie Glob-Muster verwenden.",
"install": "Installieren",
"install_update": "Update installieren",
"installed": "Installiert",
"item_size": "Elementgröße",
"job_has_been_canceled": "Der Job wurde abgebrochen.",
"job_has_been_paused": "Der Job wurde pausiert.",
"job_has_been_removed": "Der Job wurde entfernt.",
"job_has_been_resumed": "Der Job wurde fortgesetzt.",
"join": "Beitreten",
"join_discord": "Discord beitreten",
"join_library": "Einer Bibliothek beitreten",
"join_library_description": "Bibliotheken sind eine sichere, auf dem Gerät befindliche Datenbank. Ihre Dateien bleiben dort, wo sie sind, die Bibliothek katalogisiert sie und speichert alle mit Spacedrive verbundenen Daten.",
"key_manager": "Schlüsselverwaltung",
"key_manager_description": "Erstellen Sie Verschlüsselungsschlüssel, mounten und unmounten Sie Ihre Schlüssel, um Dateien entschlüsselt in Echtzeit zu sehen.",
"keybinds": "Tastenkombinationen",
"keybinds_description": "Client-Tastenkombinationen anzeigen und verwalten",
"keys": "Schlüssel",
"kilometers": "Kilometer",
"learn_more_about_telemetry": "Mehr über Telemetrie erfahren",
"libraries": "Bibliotheken",
"libraries_description": "Die Datenbank enthält alle Bibliotheksdaten und Dateimetadaten.",
"library": "Bibliothek",
"library_name": "Bibliotheksname",
"library_settings": "Bibliothekseinstellungen",
"library_settings_description": "Allgemeine Einstellungen in Bezug auf die aktuell aktive Bibliothek.",
"list_view": "Listenansicht",
"list_view_notice_description": "Navigieren Sie einfach durch Ihre Dateien und Ordner mit der Listenansicht. Diese Ansicht zeigt Ihre Dateien in einem einfachen, organisierten Listenformat an, sodass Sie schnell die benötigten Dateien finden und darauf zugreifen können.",
"loading": "Laden",
"local_locations": "Lokale Standorte",
"local_node": "Lokaler Knoten",
"location_display_name_info": "Der Name dieses Standorts, so wird er in der Seitenleiste angezeigt. Wird den eigentlichen Ordner auf der Festplatte nicht umbenennen.",
"location_is_already_linked": "Standort ist bereits verknüpft",
"location_offline_tooltip": "Standort ist offline, Sie können immer noch durchsuchen und organisieren.",
"location_path_info": "Der Pfad zu diesem Standort, hier werden die Dateien auf der Festplatte gespeichert.",
"location_type": "Standorttyp",
"location_type_managed": "Spacedrive wird Dateien für Sie sortieren. Wenn der Standort nicht leer ist, wird ein \"spacedrive\" Ordner erstellt.",
"location_type_normal": "Der Inhalt wird wie vorhanden indiziert, neue Dateien werden nicht automatisch sortiert.",
"location_type_replica": "Dieser Standort ist eine Kopie eines anderen, sein Inhalt wird automatisch synchronisiert.",
"locations": "Standorte",
"locations_description": "Verwalten Sie Ihre Speicherstandorte.",
"lock": "Sperren",
"log_in_with_browser": "Mit Browser anmelden",
"log_out": "Abmelden",
"manage_library": "Bibliothek verwalten",
"managed": "Verwaltet",
"media": "Medien",
"media_view_context": "Medienansichtskontext",
"media_view_notice_description": "Entdecken Sie Fotos und Videos leicht, die Medienansicht zeigt Resultate beginnend am aktuellen Standort einschließlich Unterordnern.",
"meet_contributors_behind_spacedrive": "Treffen Sie die Mitwirkenden hinter Spacedrive",
"miles": "Meilen",
"mode": "Modus",
"modified": "Geändert",
"more": "Mehr",
"more_actions": "Mehr Aktionen...",
"more_info": "Mehr Infos",
"move_files": "Dateien verschieben",
"name": "Name",
"navigate_back": "Zurück navigieren",
"navigate_forward": "Vorwärts navigieren",
"network_page_description": "Andere Spacedrive-Knoten in Ihrem LAN werden hier angezeigt, zusammen mit Ihren standardmäßigen Betriebssystem-Netzwerklaufwerken.",
"networking": "Netzwerk",
"networking_port": "Netzwerk-Port",
"networking_port_description": "Der Port für das Peer-to-Peer-Netzwerk von Spacedrive zur Kommunikation. Sie sollten dies deaktiviert lassen, es sei denn, Sie haben eine restriktive Firewall. Nicht ins Internet freigeben!",
"new_folder": "Neuer Ordner",
"new_library": "Neue Bibliothek",
"new_location": "Neuer Standort",
"new_location_web_description": "Da Sie die Browser-Version von Spacedrive verwenden, müssen Sie (vorerst) einen absoluten URL-Pfad eines Ordners angeben, der sich lokal auf dem entfernten Knoten befindet.",
"new_tab": "Neuer Tab",
"new_tag": "Neuer Tag",
"no_files_found_here": "Hier wurden keine Dateien gefunden",
"no_jobs": "Keine Aufgaben.",
"no_tag_selected": "Kein Tag ausgewählt",
"no_tags": "Keine Tags",
"node_name": "Knotenname",
"nodes": "Knoten",
"nodes_description": "Verwalten Sie die Knoten, die mit dieser Bibliothek verbunden sind. Ein Knoten ist eine Instanz von Spacedrives Backend, die auf einem Gerät oder Server läuft. Jeder Knoten führt eine Kopie der Datenbank und synchronisiert über Peer-to-Peer-Verbindungen in Echtzeit.",
"none": "Keine",
"normal": "Normal",
"not_you": "Nicht Sie?",
"number_of_passes": "Anzahl der Durchläufe",
"object_id": "Objekt-ID",
"offline": "Offline",
"online": "Online",
"open": "Öffnen",
"open_file": "Datei öffnen",
"open_new_location_once_added": "Neuen Standort öffnen, sobald hinzugefügt",
"open_settings": "Einstellungen öffnen",
"open_with": "Öffnen mit",
"or": "ODER",
"pair": "Verbinden",
"pairing_with_node": "Koppeln mit {{node}}",
"paste": "Einfügen",
"path": "Pfad",
"path_copied_to_clipboard_description": "Pfad für den Standort {{location}} wurde in die Zwischenablage kopiert.",
"path_copied_to_clipboard_title": "Pfad in Zwischenablage kopiert",
"pause": "Pausieren",
"peers": "Peers",
"people": "Personen",
"privacy": "Privatsphäre",
"privacy_description": "Spacedrive ist für Datenschutz entwickelt, deshalb sind wir Open Source und \"local first\". Deshalb werden wir sehr deutlich machen, welche Daten mit uns geteilt werden.",
"quick_preview": "Schnellvorschau",
"quick_view": "Schnellansicht",
"recent_jobs": "Aktuelle Aufgaben",
"regen_labels": "Labels erneuern",
"regen_thumbnails": "Vorschaubilder erneuern",
"regenerate_thumbs": "Vorschaubilder neu generieren",
"reindex": "Neu indizieren",
"reject": "Ablehnen",
"reload": "Neu laden",
"remove": "Entfernen",
"remove_from_recents": "Aus den aktuellen Dokumenten entfernen",
"rename": "Umbenennen",
"replica": "Replik",
"rescan_directory": "Verzeichnis neu scannen",
"rescan_location": "Standort neu scannen",
"reset": "Zurücksetzen",
"resources": "Ressourcen",
"restore": "Wiederherstellen",
"resume": "Fortsetzen",
"retry": "Erneut versuchen",
"revel_in_browser": "Im {{browser}} anzeigen",
"running": "Läuft",
"save": "Speichern",
"save_changes": "Änderungen speichern",
"search_extensions": "Erweiterungen suchen",
"secure_delete": "Sicheres Löschen",
"security": "Sicherheit",
"security_description": "Halten Sie Ihren Client sicher.",
"send": "Senden",
"settings": "Einstellungen",
"setup": "Einrichten",
"share": "Teilen",
"sharing": "Teilen",
"sharing_description": "Verwalten Sie, wer Zugriff auf Ihre Bibliotheken hat.",
"show_details": "Details anzeigen",
"show_hidden_files": "Versteckte Dateien anzeigen",
"show_object_size": "Objektgröße anzeigen",
"show_path_bar": "Pfadleiste anzeigen",
"show_slider": "Slider anzeigen",
"size": "Größe",
"skip_login": "Anmeldung überspringen",
"sort_by": "Sortieren nach",
"spacedrop_a_file": "Eine Datei Spacedropen",
"square_thumbnails": "Quadratische Vorschaubilder",
"star_on_github": "Auf GitHub als Favorit markieren",
"stop": "Stoppen",
"success": "Erfolg",
"support": "Unterstützung",
"sync": "Synchronisieren",
"syncPreviewMedia_label": "Vorschaumedien dieses Standorts mit Ihren Geräten synchronisieren",
"sync_description": "Verwaltung der Synchronisierung in Spacedrive.",
"sync_with_library": "Mit Bibliothek synchronisieren",
"sync_with_library_description": "Wenn aktiviert, werden Ihre Tastenkombinationen mit der Bibliothek synchronisiert, ansonsten gelten sie nur für diesen Client.",
"tags": "Tags",
"tags_description": "Verwalten Sie Ihre Tags.",
"telemetry_description": "Schalten Sie EIN, um den Entwicklern detaillierte Nutzung- und Telemetriedaten zur Verfügung zu stellen, die die App verbessern helfen. Schalten Sie AUS, um nur grundlegende Daten zu senden: Ihren Aktivitätsstatus, die App-Version, die Core-Version und die Plattform (z.B. Mobil, Web oder Desktop).",
"telemetry_title": "Zusätzliche Telemetrie- und Nutzungsdaten teilen",
"temperature": "Temperatur",
"thumbnailer_cpu_usage": "CPU-Nutzung des Thumbnailers",
"thumbnailer_cpu_usage_description": "Begrenzen Sie, wie viel CPU der Thumbnailer für Hintergrundverarbeitung verwenden kann.",
"toggle_all": "Alles umschalten",
"type": "Typ",
"ui_animations": "UI-Animationen",
"ui_animations_description": "Dialoge und andere UI-Elemente werden animiert, wenn sie geöffnet und geschlossen werden.",
"usage": "Verwendung",
"usage_description": "Ihre Bibliotheksnutzung und Hardwareinformationen",
"value": "Wert",
"video_preview_not_supported": "Videovorschau wird nicht unterstützt.",
"want_to_do_this_later": "Möchten Sie dies später erledigen?",
"website": "Webseite",
"your_account": "Ihr Konto",
"your_account_description": "Spacedrive-Konto und -Informationen.",
"your_local_network": "Ihr lokales Netzwerk",
"your_privacy": "Ihre Privatsphäre"
}

View file

@ -0,0 +1,349 @@
{
"meet_title": "Meet {{title}}",
"about": "About",
"about_vision_text": "Many of us have multiple cloud accounts, drives that arent backed up and data at risk of loss. We depend on cloud services like Google Photos and iCloud, but are locked in with limited capacity and almost zero interoperability between services and operating systems. Photo albums shouldnt be stuck in a device ecosystem, or harvested for advertising data. They should be OS agnostic, permanent and personally owned. Data we create is our legacy, that will long outlive us—open source technology is the only way to ensure we retain absolute control over the data that defines our lives, at unlimited scale.",
"about_vision_title": "Vision",
"accept": "Accept",
"accessed": "Accessed",
"account": "Account",
"actions": "Actions",
"add": "Add",
"add_device": "Add Device",
"add_library": "Add Library",
"add_location_description": "Enhance your Spacedrive experience by adding your favorite locations to your personal library, for seamless and efficient file management.",
"add_location_tooltip": "Add path as an indexed location",
"add_locations": "Add Locations",
"add_tag": "Add Tag",
"advanced_settings": "Advanced settings",
"all_jobs_have_been_cleared": "All jobs have been cleared.",
"alpha_release_description": "We are delighted for you to try Spacedrive, now in Alpha release, showcasing exciting new features. As with any initial release, this version may contain some bugs. We kindly request your assistance in reporting any issues you encounter on our Discord channel. Your valuable feedback will greatly contribute to enhancing the user experience.",
"alpha_release_title": "Alpha Release",
"appearance": "Appearance",
"appearance_description": "Change the look of your client.",
"archive": "Archive",
"archive_coming_soon": "Archiving locations is coming soon...",
"archive_info": "Extract data from Library as an archive, useful to preserve Location folder structure.",
"are_you_sure": "Are you sure?",
"assign_tag": "Assign tag",
"audio_preview_not_supported": "Audio preview is not supported.",
"back": "Back",
"backups": "Backups",
"backups_description": "Manage your Spacedrive database backups.",
"blur_effects": "Blur Effects",
"blur_effects_description": "Some components will have a blur effect applied to them.",
"cancel": "Cancel",
"celcius": "Celsius",
"change": "Change",
"changelog": "Changelog",
"changelog_page_description": "See what cool new features we're making",
"changelog_page_title": "Changelog",
"checksum": "Checksum",
"clear_finished_jobs": "Clear out finished jobs",
"client": "Client",
"close": "Close",
"clouds": "Clouds",
"color": "Color",
"coming_soon": "Coming soon",
"compress": "Compress",
"configure_location": "Configure Location",
"connected": "Connected",
"contacts": "Contacts",
"contacts_description": "Manage your contacts in Spacedrive.",
"content_id": "Content ID",
"continue": "Continue",
"convert_to": "Convert to",
"coordinates": "Coordinates",
"copied": "Copied",
"copy": "Copy",
"copy_as_path": "Copy as path",
"copy_path_to_clipboard": "Copy path to clipboard",
"create": "Create",
"create_library": "Create a Library",
"create_library_description": "Libraries are a secure, on-device database. Your files remain where they are, the Library catalogs them and stores all Spacedrive related data.",
"create_new_library": "Create new library",
"create_new_library_description": "Libraries are a secure, on-device database. Your files remain where they are, the Library catalogs them and stores all Spacedrive related data.",
"create_new_tag": "Create New Tag",
"create_new_tag_description": "Choose a name and color.",
"create_tag": "Create Tag",
"created": "Created",
"creating_library": "Creating library...",
"creating_your_library": "Creating your library",
"current": "Current",
"current_directory": "Current Directory",
"current_directory_with_descendants": "Current Directory With Descendants",
"custom": "Custom",
"cut": "Cut",
"data_folder": "Data Folder",
"debug_mode": "Debug mode",
"debug_mode_description": "Enable extra debugging features within the app.",
"default": "Default",
"delete": "Delete",
"delete_dialog_title": "Delete {{prefix}} {{type}}",
"delete_info": "This will not delete the actual folder on disk. Preview media will be deleted.",
"delete_library": "Delete Library",
"delete_library_description": "This is permanent, your files will not be deleted, only the Spacedrive library.",
"delete_location": "Delete Location",
"delete_location_description": "Deleting a location will also remove all files associated with it from the Spacedrive database, the files themselves will not be deleted.",
"delete_rule": "Delete rule",
"delete_tag": "Delete Tag",
"delete_tag_description": "Are you sure you want to delete this tag? This cannot be undone and tagged files will be unlinked.",
"delete_warning": "Warning: This will delete your {{type}} forever, we don't have a trash can yet...",
"description": "Description",
"deselect": "Deselect",
"details": "Details",
"devices_coming_soon_tooltip": "Coming soon! This alpha release doesn't include library sync, it will be ready very soon.",
"direction": "Direction",
"disabled": "Disabled",
"display_formats": "Display Formats",
"display_name": "Display Name",
"distance": "Distance",
"done": "Done",
"dont_show_again": "Don't show again",
"double_click_action": "Double click action",
"download": "Download",
"duplicate": "Duplicate",
"edit": "Edit",
"edit_library": "Edit Library",
"edit_location": "Edit Location",
"enable_networking": "Enable Networking",
"encrypt": "Encrypt",
"encrypt_library": "Encrypt Library",
"encrypt_library_coming_soon": "Library encryption coming soon",
"encrypt_library_description": "Enable encryption for this library, this will only encrypt the Spacedrive database, not the files themselves.",
"ephemeral_notice_browse": "Browse your files and folders directly from your device.",
"ephemeral_notice_consider_indexing": "Consider indexing your local locations for a faster and more efficient exploration.",
"erase": "Erase",
"erase_a_file": "Erase a file",
"erase_a_file_description": "Configure your erasure settings.",
"error": "Error",
"error_loading_original_file": "Error loading original file",
"expand": "Expand",
"explorer": "Explorer",
"export": "Export",
"export_library": "Export Library",
"export_library_coming_soon": "Export Library coming soon",
"export_library_description": "Export this library to a file.",
"extensions": "Extensions",
"extensions_description": "Install extensions to extend the functionality of this client.",
"fahrenheit": "Fahrenheit",
"failed_to_cancel_job": "Failed to cancel job.",
"failed_to_clear_all_jobs": "Failed to clear all jobs.",
"failed_to_copy_file_path": "Failed to copy file path",
"failed_to_duplicate_file": "Failed to duplicate file",
"failed_to_generate_checksum": "Failed to generate checksum",
"failed_to_generate_labels": "Failed to generate labels",
"failed_to_generate_thumbnails": "Failed to generate thumbnails",
"failed_to_load_tags": "Failed to load tags",
"failed_to_pause_job": "Failed to pause job.",
"failed_to_reindex_location": "Failed to re-index location",
"failed_to_remove_file_from_recents": "Failed to remove file from recents",
"failed_to_remove_job": "Failed to remove job.",
"failed_to_rescan_location": "Failed to rescan location",
"failed_to_resume_job": "Failed to resume job.",
"failed_to_update_location_settings": "Failed to update location settings",
"favorite": "Favorite",
"file_indexing_rules": "File indexing rules",
"filters": "Filters",
"forward": "Forward",
"full_disk_access": "Full disk access",
"full_disk_access_description": "To provide the best experience, we need access to your disk in order to index your files. Your files are only available to you.",
"full_reindex": "Full Reindex",
"full_reindex_info": "Perform a full rescan of this Location.",
"general": "General",
"general_settings": "General Settings",
"general_settings_description": "General settings related to this client.",
"generatePreviewMedia_label": "Generate preview media for this Location",
"generate_checksums": "Generate Checksums",
"go_back": "Go Back",
"got_it": "Got it",
"grid_gap": "Gap",
"grid_view": "Grid View",
"grid_view_notice_description": "Get a visual overview of your files with Grid View. This view displays your files and folders as thumbnail images, making it easy to quickly identify the file you're looking for.",
"hidden_label": "Prevents the location and its contents from appearing in summary categories, search and tags unless \"Show hidden items\" is enabled.",
"hide_in_library_search": "Hide in Library search",
"hide_in_library_search_description": "Hide files with this tag from results when searching entire library.",
"hide_in_sidebar": "Hide in sidebar",
"hide_in_sidebar_description": "Prevent this tag from showing in the sidebar of the app.",
"hide_location_from_view": "Hide location and contents from view",
"image_labeler_ai_model": "Image label recognition AI model",
"image_labeler_ai_model_description": "The model used to recognize objects in images. Larger models are more accurate but slower.",
"import": "Import",
"indexed": "Indexed",
"indexer_rule_reject_allow_label": "By default, an indexer rule functions as a Reject list, resulting in the exclusion of any files that match its criteria. Enabling this option will transform it into a Allow list, allowing the location to solely index files that meet its specified rules.",
"indexer_rules": "Indexer rules",
"indexer_rules_info": "Indexer rules allow you to specify paths to ignore using globs.",
"install": "Install",
"install_update": "Install Update",
"installed": "Installed",
"item_size": "Item size",
"job_has_been_canceled": "Job has been canceled.",
"job_has_been_paused": "Job has been paused.",
"job_has_been_removed": "Job has been removed.",
"job_has_been_resumed": "Job has been resumed.",
"join": "Join",
"join_discord": "Join Discord",
"join_library": "Join a Library",
"join_library_description": "Libraries are a secure, on-device database. Your files remain where they are, the Library catalogs them and stores all Spacedrive related data.",
"key_manager": "Key Manager",
"key_manager_description": "Create encryption keys, mount and unmount your keys to see files decrypted on the fly.",
"keybinds": "Keybinds",
"keybinds_description": "View and manage client keybinds",
"keys": "Keys",
"kilometers": "Kilometers",
"learn_more_about_telemetry": "Learn more about telemetry",
"libraries": "Libraries",
"libraries_description": "The database contains all library data and file metadata.",
"library": "Library",
"library_name": "Library name",
"library_settings": "Library Settings",
"library_settings_description": "General settings related to the currently active library.",
"list_view": "List View",
"list_view_notice_description": "Easily navigate through your files and folders with List View. This view displays your files in a simple, organized list format, allowing you to quickly locate and access the files you need.",
"loading": "Loading",
"local_locations": "Local Locations",
"local_node": "Local Node",
"location_display_name_info": "The name of this Location, this is what will be displayed in the sidebar. Will not rename the actual folder on disk.",
"location_is_already_linked": "Location is already linked",
"location_offline_tooltip": "Location is offline, you can still browse and organize.",
"location_path_info": "The path to this Location, this is where the files will be stored on disk.",
"location_type": "Location Type",
"location_type_managed": "Spacedrive will sort files for you. If Location isn't empty a \"spacedrive\" folder will be created.",
"location_type_normal": "Contents will be indexed as-is, new files will not be automatically sorted.",
"location_type_replica": "This Location is a replica of another, its contents will be automatically synchronized.",
"locations": "Locations",
"locations_description": "Manage your storage locations.",
"lock": "Lock",
"log_in_with_browser": "Log in with browser",
"log_out": "Log out",
"manage_library": "Manage Library",
"managed": "Managed",
"media": "Media",
"media_view_context": "Media View Context",
"media_view_notice_description": "Discover photos and videos easily, Media View will show results starting at the current location including sub directories.",
"meet_contributors_behind_spacedrive": "Meet the contributors behind Spacedrive",
"miles": "Miles",
"mode": "Mode",
"modified": "Modified",
"more": "More",
"more_actions": "More actions...",
"more_info": "More info",
"move_files": "Move Files",
"name": "Name",
"navigate_back": "Navigate back",
"navigate_forward": "Navigate forward",
"network_page_description": "Other Spacedrive nodes on your LAN will appear here, along with your default OS network mounts.",
"networking": "Networking",
"networking_port": "Networking Port",
"networking_port_description": "The port for Spacedrive's Peer-to-peer networking to communicate on. You should leave this disabled unless you have a restictive firewall. Do not expose to the internet!",
"new_folder": "New folder",
"new_library": "New library",
"new_location": "New location",
"new_location_web_description": "As you are using the browser version of Spacedrive you will (for now) need to specify an absolute URL of a directory local to the remote node.",
"new_tab": "New Tab",
"new_tag": "New tag",
"no_files_found_here": "No files found here",
"no_jobs": "No jobs.",
"no_tag_selected": "No Tag Selected",
"no_tags": "No tags",
"node_name": "Node Name",
"nodes": "Nodes",
"nodes_description": "Manage the nodes connected to this library. A node is an instance of Spacedrive's backend, running on a device or server. Each node carries a copy of the database and synchronizes via peer-to-peer connections in realtime.",
"none": "None",
"normal": "Normal",
"not_you": "Not you?",
"number_of_passes": "# of passes",
"object_id": "Object ID",
"offline": "Offline",
"online": "Online",
"open": "Open",
"open_file": "Open File",
"open_new_location_once_added": "Open new location once added",
"open_settings": "Open Settings",
"open_with": "Open with",
"or": "OR",
"pair": "Pair",
"pairing_with_node": "Pairing with {{node}}",
"paste": "Paste",
"path": "Path",
"path_copied_to_clipboard_description": "Path for location {{location}} copied to clipboard.",
"path_copied_to_clipboard_title": "Path copied to clipboard",
"pause": "Pause",
"peers": "Peers",
"people": "People",
"privacy": "Privacy",
"privacy_description": "Spacedrive is built for privacy, that's why we're open source and local first. So we'll make it very clear what data is shared with us.",
"quick_preview": "Quick Preview",
"quick_view": "Quick view",
"recent_jobs": "Recent Jobs",
"regen_labels": "Regen Labels",
"regen_thumbnails": "Regen Thumbnails",
"regenerate_thumbs": "Regenerate Thumbs",
"reindex": "Re-index",
"reject": "Reject",
"reload": "Reload",
"remove": "Remove",
"remove_from_recents": "Remove From Recents",
"rename": "Rename",
"replica": "Replica",
"rescan_directory": "Rescan Directory",
"rescan_location": "Rescan Location",
"reset": "Reset",
"resources": "Resources",
"restore": "Restore",
"resume": "Resume",
"retry": "Retry",
"revel_in_browser": "Reveal in {{browser}}",
"running": "Running",
"save": "Save",
"save_changes": "Save Changes",
"search_extensions": "Search extensions",
"secure_delete": "Secure delete",
"security": "Security",
"security_description": "Keep your client safe.",
"send": "Send",
"settings": "Settings",
"setup": "Set up",
"share": "Share",
"sharing": "Sharing",
"sharing_description": "Manage who has access to your libraries.",
"show_details": "Show details",
"show_hidden_files": "Show Hidden Files",
"show_object_size": "Show Object size",
"show_path_bar": "Show Path Bar",
"show_slider": "Show slider",
"size": "Size",
"skip_login": "Skip login",
"sort_by": "Sort by",
"spacedrop_a_file": "Spacedrop a File",
"square_thumbnails": "Square Thumbnails",
"star_on_github": "Star on GitHub",
"stop": "Stop",
"success": "Success",
"support": "Support",
"sync": "Sync",
"syncPreviewMedia_label": "Sync preview media for this Location with your devices",
"sync_description": "Manage how Spacedrive syncs.",
"sync_with_library": "Sync with Library",
"sync_with_library_description": "If enabled, your keybinds will be synced with library, otherwise they will apply only to this client.",
"tags": "Tags",
"tags_description": "Manage your tags.",
"telemetry_description": "Toggle ON to provide developers with detailed usage and telemetry data to enhance the app. Toggle OFF to send only basic data: your activity status, app version, core version, and platform (e.g., mobile, web, or desktop).",
"telemetry_title": "Share Additional Telemetry and Usage Data",
"temperature": "Temperature",
"thumbnailer_cpu_usage": "Thumbnailer CPU usage",
"thumbnailer_cpu_usage_description": "Limit how much CPU the thumbnailer can use for background processing.",
"toggle_all": "Toggle All",
"type": "Type",
"ui_animations": "UI Animations",
"ui_animations_description": "Dialogs and other UI elements will animate when opening and closing.",
"usage": "Usage",
"usage_description": "Your library usage and hardware information",
"value": "Value",
"video_preview_not_supported": "Video preview is not supported.",
"want_to_do_this_later": "Want to do this later?",
"website": "Website",
"your_account": "Your account\"",
"your_account_description": "Spacedrive account and information.",
"your_local_network": "Your Local Network",
"your_privacy": "Your Privacy"
}

View file

@ -0,0 +1,349 @@
{
"meet_title": "Conoce a {{title}}",
"about": "Acerca de",
"about_vision_text": "Muchos de nosotros tenemos varias cuentas en la nube, discos que no tienen copias de seguridad y datos en riesgo de pérdida. Dependemos de servicios en la nube como Google Photos e iCloud, pero estamos limitados con una capacidad reducida y casi cero interoperabilidad entre servicios y sistemas operativos. Los álbumes de fotos no deberían estar atascados en un ecosistema de dispositivos o ser utilizados para datos publicitarios. Deberían ser independientes del SO, permanentes y de propiedad personal. Los datos que creamos son nuestro legado, que nos sobrevivirá mucho tiempo: la tecnología de código abierto es la única forma de asegurarnos de mantener el control absoluto sobre los datos que definen nuestras vidas, a una escala ilimitada.",
"about_vision_title": "Visión",
"accept": "Aceptar",
"accessed": "Accedido",
"account": "Cuenta",
"actions": "Acciones",
"add": "Agregar",
"add_device": "Agregar Dispositivo",
"add_library": "Agregar Biblioteca",
"add_location_description": "Mejora tu experiencia en Spacedrive agregando tus ubicaciones favoritas a tu biblioteca personal, para una gestión de archivos eficiente y sin interrupciones.",
"add_location_tooltip": "Agregar ruta como una ubicación indexada",
"add_locations": "Agregar Ubicaciones",
"add_tag": "Agregar Etiqueta",
"advanced_settings": "Configuración avanzada",
"all_jobs_have_been_cleared": "Todos los trabajos han sido eliminados.",
"alpha_release_description": "Estamos encantados de que pruebes Spacedrive, ahora en lanzamiento Alpha, mostrando nuevas funciones emocionantes. Como con cualquier lanzamiento inicial, esta versión puede contener algunos errores. Amablemente solicitamos tu ayuda para informar cualquier problema que encuentres en nuestro canal de Discord. Tu valiosa retroalimentación contribuirá en gran medida a mejorar la experiencia del usuario.",
"alpha_release_title": "Lanzamiento Alpha",
"appearance": "Apariencia",
"appearance_description": "Cambia la apariencia de tu cliente.",
"archive": "Archivo",
"archive_coming_soon": "El archivado de ubicaciones estará disponible pronto...",
"archive_info": "Extrae datos de la Biblioteca como un archivo, útil para preservar la estructura de carpetas de la Ubicación.",
"are_you_sure": "¿Estás seguro?",
"assign_tag": "Asignar etiqueta",
"audio_preview_not_supported": "La vista previa de audio no está soportada.",
"back": "Atrás",
"backups": "Copias de seguridad",
"backups_description": "Administra tus copias de seguridad de la base de datos de Spacedrive.",
"blur_effects": "Efectos de desenfoque",
"blur_effects_description": "Algunos componentes tendrán un efecto de desenfoque aplicado.",
"cancel": "Cancelar",
"celcius": "Celsius",
"change": "Cambiar",
"changelog": "Registro de cambios",
"changelog_page_description": "Mira qué nuevas funciones geniales estamos creando",
"changelog_page_title": "Registro de cambios",
"checksum": "Suma de verificación",
"clear_finished_jobs": "Eliminar trabajos finalizados",
"client": "Cliente",
"close": "Cerrar",
"clouds": "Nubes",
"color": "Color",
"coming_soon": "Próximamente",
"compress": "Comprimir",
"configure_location": "Configurar Ubicación",
"connected": "Conectado",
"contacts": "Contactos",
"contacts_description": "Administra tus contactos en Spacedrive.",
"content_id": "ID de contenido",
"continue": "Continuar",
"convert_to": "Convertir a",
"coordinates": "Coordenadas",
"copied": "Copiado",
"copy": "Copiar",
"copy_as_path": "Copiar como ruta",
"copy_path_to_clipboard": "Copiar ruta al portapapeles",
"create": "Crear",
"create_library": "Crear una Biblioteca",
"create_library_description": "Las bibliotecas son una base de datos segura, en el dispositivo. Tus archivos permanecen donde están, la Biblioteca los cataloga y almacena todos los datos relacionados con Spacedrive.",
"create_new_library": "Crear nueva biblioteca",
"create_new_library_description": "Las bibliotecas son una base de datos segura, en el dispositivo. Tus archivos permanecen donde están, la Biblioteca los cataloga y almacena todos los datos relacionados con Spacedrive.",
"create_new_tag": "Crear Nueva Etiqueta",
"create_new_tag_description": "Elige un nombre y un color.",
"create_tag": "Crear Etiqueta",
"created": "Creado",
"creating_library": "Creando biblioteca...",
"creating_your_library": "Creando tu biblioteca",
"current": "Actual",
"current_directory": "Directorio Actual",
"current_directory_with_descendants": "Directorio Actual Con Descendientes",
"custom": "Personalizado",
"cut": "Cortar",
"data_folder": "Carpeta de datos",
"debug_mode": "Modo de depuración",
"debug_mode_description": "Habilitar funciones de depuración adicionales dentro de la aplicación.",
"default": "Predeterminado",
"delete": "Eliminar",
"delete_dialog_title": "Eliminar {{prefix}} {{type}}",
"delete_info": "Esto no eliminará la carpeta real en el disco. Los medios de vista previa serán eliminados.",
"delete_library": "Eliminar Biblioteca",
"delete_library_description": "Esto es permanente, tus archivos no serán eliminados, solo la biblioteca de Spacedrive.",
"delete_location": "Eliminar Ubicación",
"delete_location_description": "Eliminar una ubicación también removerá todos los archivos asociados con ella de la base de datos de Spacedrive, los archivos mismos no serán eliminados.",
"delete_rule": "Eliminar regla",
"delete_tag": "Eliminar Etiqueta",
"delete_tag_description": "¿Estás seguro de que quieres eliminar esta etiqueta? Esto no se puede deshacer y los archivos etiquetados serán desvinculados.",
"delete_warning": "Advertencia: Esto eliminará tu {{type}} para siempre, aún no tenemos papelera...",
"description": "Descripción",
"deselect": "Deseleccionar",
"details": "Detalles",
"devices_coming_soon_tooltip": "¡Próximamente! Esta versión alfa no incluye la sincronización de bibliotecas, estará lista muy pronto.",
"direction": "Dirección",
"disabled": "Deshabilitado",
"display_formats": "Formatos de visualización",
"display_name": "Nombre visible",
"distance": "Distancia",
"done": "Hecho",
"dont_show_again": "No mostrar de nuevo",
"double_click_action": "Acción de doble clic",
"download": "Descargar",
"duplicate": "Duplicar",
"edit": "Editar",
"edit_library": "Editar Biblioteca",
"edit_location": "Editar Ubicación",
"enable_networking": "Habilitar Redes",
"encrypt": "Encriptar",
"encrypt_library": "Encriptar Biblioteca",
"encrypt_library_coming_soon": "Encriptación de biblioteca próximamente",
"encrypt_library_description": "Habilitar la encriptación para esta biblioteca, esto solo encriptará la base de datos de Spacedrive, no los archivos en sí.",
"ephemeral_notice_browse": "Explora tus archivos y carpetas directamente desde tu dispositivo.",
"ephemeral_notice_consider_indexing": "Considera indexar tus ubicaciones locales para una exploración más rápida y eficiente.",
"erase": "Borrar",
"erase_a_file": "Borrar un archivo",
"erase_a_file_description": "Configura tus ajustes de borrado.",
"error": "Error",
"error_loading_original_file": "Error cargando el archivo original",
"expand": "Expandir",
"explorer": "Explorador",
"export": "Exportar",
"export_library": "Exportar Biblioteca",
"export_library_coming_soon": "Exportación de biblioteca próximamente",
"export_library_description": "Exporta esta biblioteca a un archivo.",
"extensions": "Extensiones",
"extensions_description": "Instala extensiones para extender la funcionalidad de este cliente.",
"fahrenheit": "Fahrenheit",
"failed_to_cancel_job": "Error al cancelar el trabajo.",
"failed_to_clear_all_jobs": "Error al eliminar todos los trabajos.",
"failed_to_copy_file_path": "Error al copiar la ruta del archivo",
"failed_to_duplicate_file": "Error al duplicar el archivo",
"failed_to_generate_checksum": "Error al generar suma de verificación",
"failed_to_generate_labels": "Error al generar etiquetas",
"failed_to_generate_thumbnails": "Error al generar miniaturas",
"failed_to_load_tags": "Error al cargar etiquetas",
"failed_to_pause_job": "Error al pausar el trabajo.",
"failed_to_reindex_location": "Error al reindexar ubicación",
"failed_to_remove_file_from_recents": "Error al eliminar archivo de recientes",
"failed_to_remove_job": "Error al eliminar el trabajo.",
"failed_to_rescan_location": "Error al volver a escanear ubicación",
"failed_to_resume_job": "Error al reanudar el trabajo.",
"failed_to_update_location_settings": "Error al actualizar configuraciones de ubicación",
"favorite": "Favorito",
"file_indexing_rules": "Reglas de indexación de archivos",
"filters": "Filtros",
"forward": "Adelante",
"full_disk_access": "Acceso completo al disco",
"full_disk_access_description": "Para proporcionar la mejor experiencia, necesitamos acceso a tu disco para indexar tus archivos. Tus archivos solo están disponibles para ti.",
"full_reindex": "Reindexación completa",
"full_reindex_info": "Realiza un escaneo completo de esta Ubicación.",
"general": "General",
"general_settings": "Configuraciones Generales",
"general_settings_description": "Configuraciones generales relacionadas con este cliente.",
"generatePreviewMedia_label": "Generar medios de vista previa para esta Ubicación",
"generate_checksums": "Generar Sumas de Verificación",
"go_back": "Regresar",
"got_it": "Entendido",
"grid_gap": "Espaciado",
"grid_view": "Vista de Cuadrícula",
"grid_view_notice_description": "Obtén una visión general visual de tus archivos con la Vista de Cuadrícula. Esta vista muestra tus archivos y carpetas como imágenes en miniatura, facilitando la identificación rápida del archivo que buscas.",
"hidden_label": "Evita que la ubicación y su contenido aparezcan en categorías resumen, búsqueda y etiquetas a menos que \"Mostrar elementos ocultos\" esté habilitado.",
"hide_in_library_search": "Ocultar en la búsqueda de la Biblioteca",
"hide_in_library_search_description": "Oculta archivos con esta etiqueta de los resultados al buscar en toda la biblioteca.",
"hide_in_sidebar": "Ocultar en la barra lateral",
"hide_in_sidebar_description": "Prevenir que esta etiqueta se muestre en la barra lateral de la aplicación.",
"hide_location_from_view": "Ocultar ubicación y contenido de la vista",
"image_labeler_ai_model": "Modelo AI de reconocimiento de etiquetas de imagen",
"image_labeler_ai_model_description": "El modelo utilizado para reconocer objetos en imágenes. Los modelos más grandes son más precisos pero más lentos.",
"import": "Importar",
"indexed": "Indexado",
"indexer_rule_reject_allow_label": "Por defecto, una regla de indexación funciona como una lista de Rechazo, resultando en la exclusión de cualquier archivo que coincida con sus criterios. Habilitar esta opción transformará en una lista de Permitir, permitiendo que la ubicación indexe solo archivos que cumplan con sus reglas especificadas.",
"indexer_rules": "Reglas de indexación",
"indexer_rules_info": "Las reglas de indexación te permiten especificar rutas a ignorar usando comodines.",
"install": "Instalar",
"install_update": "Instalar Actualización",
"installed": "Instalado",
"item_size": "Tamaño de elemento",
"job_has_been_canceled": "El trabajo ha sido cancelado.",
"job_has_been_paused": "El trabajo ha sido pausado.",
"job_has_been_removed": "El trabajo ha sido eliminado.",
"job_has_been_resumed": "El trabajo ha sido reanudado.",
"join": "Unirse",
"join_discord": "Unirse a Discord",
"join_library": "Unirse a una Biblioteca",
"join_library_description": "Las bibliotecas son una base de datos segura, en el dispositivo. Tus archivos permanecen donde están, la Biblioteca los cataloga y almacena todos los datos relacionados con Spacedrive.",
"key_manager": "Administrador de Claves",
"key_manager_description": "Crea claves de encriptación, monta y desmonta tus claves para ver archivos desencriptados al vuelo.",
"keybinds": "Atajos de Teclado",
"keybinds_description": "Ver y administrar atajos de teclado del cliente",
"keys": "Claves",
"kilometers": "Kilómetros",
"learn_more_about_telemetry": "Aprende más sobre la telemetría",
"libraries": "Bibliotecas",
"libraries_description": "La base de datos contiene todos los datos de la biblioteca y metadatos de archivos.",
"library": "Biblioteca",
"library_name": "Nombre de la Biblioteca",
"library_settings": "Configuraciones de la Biblioteca",
"library_settings_description": "Configuraciones generales relacionadas con la biblioteca activa actualmente.",
"list_view": "Vista de Lista",
"list_view_notice_description": "Navega fácilmente a través de tus archivos y carpetas con la Vista de Lista. Esta vista muestra tus archivos en un formato de lista simple y organizado, permitiéndote localizar y acceder rápidamente a los archivos que necesitas.",
"loading": "Cargando",
"local_locations": "Ubicaciones Locales",
"local_node": "Nodo Local",
"location_display_name_info": "El nombre de esta Ubicación, esto es lo que se mostrará en la barra lateral. No renombrará la carpeta real en el disco.",
"location_is_already_linked": "Ubicación ya está vinculada",
"location_offline_tooltip": "La ubicación está fuera de línea, aún puedes navegar y organizar.",
"location_path_info": "La ruta a esta Ubicación, aquí es donde los archivos serán almacenados en el disco.",
"location_type": "Tipo de Ubicación",
"location_type_managed": "Spacedrive ordenará los archivos por ti. Si la Ubicación no está vacía se creará una carpeta \"spacedrive\".",
"location_type_normal": "El contenido será indexado como está, los nuevos archivos no serán ordenados automáticamente.",
"location_type_replica": "Esta Ubicación es una réplica de otra, su contenido será sincronizado automáticamente.",
"locations": "Ubicaciones",
"locations_description": "Administra tus ubicaciones de almacenamiento.",
"lock": "Bloquear",
"log_in_with_browser": "Iniciar sesión con el navegador",
"log_out": "Cerrar sesión",
"manage_library": "Administrar Biblioteca",
"managed": "Gestionado",
"media": "Medios",
"media_view_context": "Contexto de Vista de Medios",
"media_view_notice_description": "Descubre fotos y videos fácilmente, la Vista de Medios mostrará resultados comenzando en la ubicación actual incluyendo subdirectorios.",
"meet_contributors_behind_spacedrive": "Conoce a los colaboradores detrás de Spacedrive",
"miles": "Millas",
"mode": "Modo",
"modified": "Modificado",
"more": "Más",
"more_actions": "Más acciones...",
"more_info": "Más información",
"move_files": "Mover Archivos",
"name": "Nombre",
"navigate_back": "Navegar hacia atrás",
"navigate_forward": "Navegar hacia adelante",
"network_page_description": "Otros nodos de Spacedrive en tu LAN aparecerán aquí, junto con tus montajes de red del sistema operativo por defecto.",
"networking": "Redes",
"networking_port": "Puerto de Redes",
"networking_port_description": "El puerto para que la red peer-to-peer de Spacedrive se comunique. Deberías dejar esto deshabilitado a menos que tengas un firewall restrictivo. ¡No expongas al internet!",
"new_folder": "Nueva carpeta",
"new_library": "Nueva biblioteca",
"new_location": "Nueva ubicación",
"new_location_web_description": "Como estás utilizando la versión de navegador de Spacedrive, por ahora tendrás que especificar una URL absoluta de un directorio local al nodo remoto.",
"new_tab": "Nueva pestaña",
"new_tag": "Nueva etiqueta",
"no_files_found_here": "No se encontraron archivos aquí",
"no_jobs": "No hay trabajos.",
"no_tag_selected": "No se seleccionó etiqueta",
"no_tags": "No hay etiquetas",
"node_name": "Nombre del Nodo",
"nodes": "Nodos",
"nodes_description": "Administra los nodos conectados a esta biblioteca. Un nodo es una instancia del backend de Spacedrive, ejecutándose en un dispositivo o servidor. Cada nodo lleva una copia de la base de datos y se sincroniza mediante conexiones peer-to-peer en tiempo real.",
"none": "Ninguno",
"normal": "Normal",
"not_you": "¿No eres tú?",
"number_of_passes": "# de pasadas",
"object_id": "ID de Objeto",
"offline": "Fuera de línea",
"online": "En línea",
"open": "Abrir",
"open_file": "Abrir Archivo",
"open_new_location_once_added": "Abrir nueva ubicación una vez agregada",
"open_settings": "Abrir Configuraciones",
"open_with": "Abrir con",
"or": "O",
"pair": "Emparejar",
"pairing_with_node": "Emparejando con {{node}}",
"paste": "Pegar",
"path": "Ruta",
"path_copied_to_clipboard_description": "Ruta para la ubicación {{location}} copiada al portapapeles.",
"path_copied_to_clipboard_title": "Ruta copiada al portapapeles",
"pause": "Pausar",
"peers": "Pares",
"people": "Gente",
"privacy": "Privacidad",
"privacy_description": "Spacedrive está construido para la privacidad, es por eso que somos de código abierto y primero locales. Por eso, haremos muy claro qué datos se comparten con nosotros.",
"quick_preview": "Vista rápida",
"quick_view": "Vista rápida",
"recent_jobs": "Trabajos recientes",
"regen_labels": "Regenerar Etiquetas",
"regen_thumbnails": "Regenerar Miniaturas",
"regenerate_thumbs": "Regenerar Miniaturas",
"reindex": "Reindexar",
"reject": "Rechazar",
"reload": "Recargar",
"remove": "Eliminar",
"remove_from_recents": "Eliminar de Recientes",
"rename": "Renombrar",
"replica": "Réplica",
"rescan_directory": "Reescanear Directorio",
"rescan_location": "Reescanear Ubicación",
"reset": "Restablecer",
"resources": "Recursos",
"restore": "Restaurar",
"resume": "Reanudar",
"retry": "Reintentar",
"revel_in_browser": "Mostrar en {{browser}}",
"running": "Ejecutando",
"save": "Guardar",
"save_changes": "Guardar Cambios",
"search_extensions": "Buscar extensiones",
"secure_delete": "Borrado seguro",
"security": "Seguridad",
"security_description": "Mantén tu cliente seguro.",
"send": "Enviar",
"settings": "Configuraciones",
"setup": "Configurar",
"share": "Compartir",
"sharing": "Compartiendo",
"sharing_description": "Administra quién tiene acceso a tus bibliotecas.",
"show_details": "Mostrar detalles",
"show_hidden_files": "Mostrar Archivos Ocultos",
"show_object_size": "Mostrar Tamaño del Objeto",
"show_path_bar": "Mostrar Barra de Ruta",
"show_slider": "Mostrar deslizador",
"size": "Tamaño",
"skip_login": "Saltar inicio de sesión",
"sort_by": "Ordenar por",
"spacedrop_a_file": "Soltar un Archivo",
"square_thumbnails": "Miniaturas Cuadradas",
"star_on_github": "Dar estrella en GitHub",
"stop": "Detener",
"success": "Éxito",
"support": "Soporte",
"sync": "Sincronizar",
"syncPreviewMedia_label": "Sincronizar medios de vista previa para esta Ubicación con tus dispositivos",
"sync_description": "Administra cómo se sincroniza Spacedrive.",
"sync_with_library": "Sincronizar con la Biblioteca",
"sync_with_library_description": "Si se habilita, tus atajos de teclado se sincronizarán con la biblioteca, de lo contrario solo se aplicarán a este cliente.",
"tags": "Etiquetas",
"tags_description": "Administra tus etiquetas.",
"telemetry_description": "Activa para proporcionar a los desarrolladores datos detallados de uso y telemetría para mejorar la aplicación. Desactiva para enviar solo datos básicos: tu estado de actividad, versión de la aplicación, versión central y plataforma (por ejemplo, móvil, web o escritorio).",
"telemetry_title": "Compartir datos adicionales de telemetría y uso",
"temperature": "Temperatura",
"thumbnailer_cpu_usage": "Uso de CPU del generador de miniaturas",
"thumbnailer_cpu_usage_description": "Limita cuánto CPU puede usar el generador de miniaturas para el procesamiento en segundo plano.",
"toggle_all": "Alternar todos",
"type": "Tipo",
"ui_animations": "Animaciones de la UI",
"ui_animations_description": "Los diálogos y otros elementos de la UI se animarán al abrirse y cerrarse.",
"usage": "Uso",
"usage_description": "Tu uso de la biblioteca e información del hardware",
"value": "Valor",
"video_preview_not_supported": "La vista previa de video no es soportada.",
"want_to_do_this_later": "¿Quieres hacer esto más tarde?",
"website": "Sitio web",
"your_account": "Tu cuenta",
"your_account_description": "Cuenta de Spacedrive e información.",
"your_local_network": "Tu Red Local",
"your_privacy": "Tu Privacidad"
}

View file

@ -0,0 +1,348 @@
{
"meet_title": "Rencontrer {{title}}",
"about": "À propos",
"about_vision_text": "Beaucoup d'entre nous ont plusieurs comptes cloud, des disques qui ne sont pas sauvegardés et des données à risque de perte. Nous dépendons de services cloud comme Google Photos et iCloud, mais sommes enfermés avec une capacité limitée et presque zéro interopérabilité entre les services et les systèmes d'exploitation. Les albums photo ne devraient pas être coincés dans un écosystème d'appareils, ou récoltés pour des données publicitaires. Ils devraient être indépendants du système d'exploitation, permanents et personnels. Les données que nous créons sont notre héritage, qui nous survivra longtemps - la technologie open source est le seul moyen d'assurer que nous conservons un contrôle absolu sur les données qui définissent nos vies, à une échelle illimitée.",
"about_vision_title": "Vision",
"accept": "Accepter",
"accessed": "Accédé",
"account": "Compte",
"actions": "Actions",
"add": "Ajouter",
"add_device": "Ajouter un appareil",
"add_library": "Ajouter une bibliothèque",
"add_location_description": "Améliorez votre expérience Spacedrive en ajoutant vos emplacements préférés à votre bibliothèque personnelle, pour une gestion des fichiers fluide et efficace.",
"add_location_tooltip": "Ajouter le chemin comme emplacement indexé",
"add_locations": "Ajouter des emplacements",
"add_tag": "Ajouter une étiquette",
"advanced_settings": "Paramètres avancés",
"all_jobs_have_been_cleared": "Tous les travaux ont été annulés.",
"alpha_release_description": "Nous sommes ravis que vous essayiez Spacedrive, maintenant en version Alpha, présentant de nouvelles fonctionnalités excitantes. Comme pour toute première version, cette version peut contenir des bugs. Nous vous demandons gentiment votre aide pour signaler tout problème rencontré sur notre chaîne Discord. Vos précieux retours contribueront grandement à améliorer l'expérience utilisateur.",
"alpha_release_title": "Version Alpha",
"appearance": "Apparence",
"appearance_description": "Changez l'apparence de votre client.",
"archive": "Archiver",
"archive_coming_soon": "L'archivage des emplacements arrive bientôt...",
"archive_info": "Extrayez les données de la bibliothèque sous forme d'archive, utile pour préserver la structure des dossiers de l'emplacement.",
"are_you_sure": "Êtes-vous sûr ?",
"assign_tag": "Attribuer une étiquette",
"audio_preview_not_supported": "L'aperçu audio n'est pas pris en charge.",
"back": "Retour",
"backups": "Sauvegardes",
"backups_description": "Gérez les sauvegardes de votre base de données Spacedrive.",
"blur_effects": "Effets de flou",
"blur_effects_description": "Certains composants se verront appliquer un effet de flou.",
"cancel": "Annuler",
"celcius": "Celsius",
"change": "Modifier",
"changelog": "Journal des modifications",
"changelog_page_description": "Découvrez les nouvelles fonctionnalités cool que nous développons",
"changelog_page_title": "Changelog",
"checksum": "Somme de contrôle",
"clear_finished_jobs": "Effacer les travaux terminés",
"client": "Client",
"close": "Fermer",
"clouds": "Nuages",
"color": "Couleur",
"coming_soon": "Bientôt",
"compress": "Compresser",
"configure_location": "Configurer l'emplacement",
"connected": "Connecté",
"contacts": "Contacts",
"contacts_description": "Gérez vos contacts dans Spacedrive.",
"content_id": "ID de contenu",
"continue": "Continuer",
"convert_to": "Convertir en",
"coordinates": "Coordonnées",
"copied": "Copié",
"copy": "Copier",
"copy_as_path": "Copier en tant que chemin",
"copy_path_to_clipboard": "Copier le chemin dans le presse-papiers",
"create": "Créer",
"create_library": "Créer une bibliothèque",
"create_library_description": "Les bibliothèques sont une base de données sécurisée, sur l'appareil. Vos fichiers restent où ils sont, la bibliothèque les catalogue et stocke toutes les données liées à Spacedrive.",
"create_new_library": "Créer une nouvelle bibliothèque",
"create_new_library_description": "Les bibliothèques sont une base de données sécurisée, sur l'appareil. Vos fichiers restent où ils sont, la bibliothèque les catalogue et stocke toutes les données liées à Spacedrive.",
"create_new_tag": "Créer une nouvelle étiquette",
"create_new_tag_description": "Choisissez un nom et une couleur.",
"create_tag": "Créer une étiquette",
"created": "Créé",
"creating_library": "Création de la bibliothèque...",
"creating_your_library": "Création de votre bibliothèque",
"current": "Actuel",
"current_directory": "Répertoire actuel",
"current_directory_with_descendants": "Répertoire actuel avec descendants",
"custom": "Personnalisé",
"cut": "Couper",
"data_folder": "Dossier de données",
"debug_mode": "Mode débogage",
"debug_mode_description": "Activez des fonctionnalités de débogage supplémentaires dans l'application.",
"default": "Défaut",
"delete": "Supprimer",
"delete_dialog_title": "Supprimer {{prefix}} {{type}}",
"delete_info": "Cela ne supprimera pas le dossier réel sur le disque. Les médias aperçus seront supprimés.",
"delete_library": "Supprimer la bibliothèque",
"delete_library_description": "Ceci est permanent, vos fichiers ne seront pas supprimés, seule la bibliothèque Spacedrive le sera.",
"delete_location": "Supprimer l'emplacement",
"delete_location_description": "Supprimer un emplacement supprimera également tous les fichiers associés de la base de données Spacedrive, les fichiers eux-mêmes ne seront pas supprimés.",
"delete_rule": "Supprimer la règle",
"delete_tag": "Supprimer l'étiquette",
"delete_tag_description": "Êtes-vous sûr de vouloir supprimer cette étiquette ? Cela ne peut pas être annulé et les fichiers étiquetés seront dissociés.",
"delete_warning": "Attention : Ceci supprimera votre {{type}} pour toujours, nous n'avons pas encore de corbeille...",
"description": "Description",
"deselect": "Désélectionner",
"details": "Détails",
"devices_coming_soon_tooltip": "Bientôt disponible ! Cette version alpha n'inclut pas la synchronisation des bibliothèques, cela sera prêt très prochainement.",
"direction": "Direction",
"disabled": "Désactivé",
"display_formats": "Formats d'affichage",
"display_name": "Nom affiché",
"distance": "Distance",
"done": "Terminé",
"dont_show_again": "Ne plus afficher",
"double_click_action": "Action double clic",
"download": "Télécharger",
"duplicate": "Dupliquer",
"edit": "Éditer",
"edit_library": "Éditer la bibliothèque",
"edit_location": "Éditer l'emplacement",
"enable_networking": "Activer le réseau",
"encrypt": "Chiffrer",
"encrypt_library": "Chiffrer la bibliothèque",
"encrypt_library_coming_soon": "Le chiffrement de la bibliothèque arrive bientôt",
"encrypt_library_description": "Activez le chiffrement pour cette bibliothèque, cela ne chiffrera que la base de données Spacedrive, pas les fichiers eux-mêmes.",
"ephemeral_notice_browse": "Parcourez vos fichiers et dossiers directement depuis votre appareil.",
"ephemeral_notice_consider_indexing": "Envisagez d'indexer vos emplacements locaux pour une exploration plus rapide et plus efficace.",
"erase": "Effacer",
"erase_a_file": "Effacer un fichier",
"erase_a_file_description": "Configurez vos paramètres d'effacement.",
"error": "Erreur",
"error_loading_original_file": "Erreur lors du chargement du fichier original",
"expand": "Étendre",
"explorer": "Explorateur",
"export": "Exporter",
"export_library": "Exporter la bibliothèque",
"export_library_coming_soon": "L'exportation de la bibliothèque arrivera bientôt",
"export_library_description": "Exporter cette bibliothèque dans un fichier.",
"extensions": "Extensions",
"extensions_description": "Installez des extensions pour étendre la fonctionnalité de ce client.",
"fahrenheit": "Fahrenheit",
"failed_to_cancel_job": "Échec de l'annulation du travail.",
"failed_to_clear_all_jobs": "Échec de l'effacement de tous les travaux.",
"failed_to_copy_file_path": "Échec de la copie du chemin du fichier",
"failed_to_duplicate_file": "Échec de la duplication du fichier",
"failed_to_generate_checksum": "Échec de la génération de la somme de contrôle",
"failed_to_generate_labels": "Échec de la génération des étiquettes",
"failed_to_generate_thumbnails": "Échec de la génération des vignettes",
"failed_to_load_tags": "Échec du chargement des étiquettes",
"failed_to_pause_job": "Échec de la mise en pause du travail.",
"failed_to_reindex_location": "Échec de la réindexation de l'emplacement",
"failed_to_remove_file_from_recents": "Échec de la suppression du fichier des éléments récents",
"failed_to_remove_job": "Échec de la suppression du travail.",
"failed_to_resume_job": "Échec de la reprise du travail.",
"failed_to_update_location_settings": "Échec de la mise à jour des paramètres de l'emplacement",
"favorite": "Favori",
"file_indexing_rules": "Règles d'indexation des fichiers",
"filters": "Filtres",
"forward": "Avancer",
"full_disk_access": "Accès complet au disque",
"full_disk_access_description": "Pour offrir la meilleure expérience, nous avons besoin d'accéder à votre disque afin d'indexer vos fichiers. Vos fichiers ne sont accessibles qu'à vous.",
"full_reindex": "Réindexation complète",
"full_reindex_info": "Effectuer un nouveau scan complet de cet emplacement.",
"general": "Général",
"general_settings": "Paramètres généraux",
"general_settings_description": "Paramètres généraux liés à ce client.",
"generatePreviewMedia_label": "Générer des médias d'aperçu pour cet emplacement",
"generate_checksums": "Générer des sommes de contrôle",
"go_back": "Revenir",
"got_it": "Compris",
"grid_gap": "Écartement de grille",
"grid_view": "Vue en grille",
"grid_view_notice_description": "Obtenez un aperçu visuel de vos fichiers avec la vue en grille. Cette vue affiche vos fichiers et dossiers sous forme d'images miniatures, facilitant l'identification rapide du fichier que vous recherchez.",
"hidden_label": "Empêche l'emplacement et son contenu d'apparaître dans les catégories résumées, la recherche et les étiquettes à moins que l'option \"Afficher les éléments cachés\" soit activée.",
"hide_in_library_search": "Masquer dans la recherche de la bibliothèque",
"hide_in_library_search_description": "Masquer les fichiers avec cette étiquette des résultats lors de la recherche dans toute la bibliothèque.",
"hide_in_sidebar": "Masquer dans la barre latérale",
"hide_in_sidebar_description": "Empêcher cette étiquette de s'afficher dans la barre latérale de l'application.",
"hide_location_from_view": "Masquer l'emplacement et le contenu de la vue",
"image_labeler_ai_model": "Modèle d'IA de reconnaissance d'étiquettes d'image",
"image_labeler_ai_model_description": "Le modèle utilisé pour reconnaître les objets dans les images. Les modèles plus grands sont plus précis mais plus lents.",
"import": "Importer",
"indexed": "Indexé",
"indexer_rule_reject_allow_label": "Par défaut, une règle d'indexeur fonctionne comme une liste de rejet, entraînant l'exclusion de tous les fichiers qui correspondent à ses critères. Activer cette option la transformera en une liste d'autorisation, permettant à l'emplacement d'indexer uniquement les fichiers qui répondent à ses règles spécifiées.",
"indexer_rules": "Règles de l'indexeur",
"indexer_rules_info": "Les règles de l'indexeur vous permettent de spécifier des chemins à ignorer en utilisant des glob patterns.",
"install": "Installer",
"install_update": "Installer la mise à jour",
"installed": "Installé",
"item_size": "Taille de l'élément",
"job_has_been_canceled": "Le travail a été annulé.",
"job_has_been_paused": "Le travail a été mis en pause.",
"job_has_been_removed": "Le travail a été supprimé.",
"job_has_been_resumed": "Le travail a été repris.",
"join": "Rejoindre",
"join_discord": "Rejoindre Discord",
"join_library": "Rejoindre une bibliothèque",
"join_library_description": "Les bibliothèques sont une base de données sécurisée, sur l'appareil. Vos fichiers restent où ils sont, la bibliothèque les catalogue et stocke toutes les données liées à Spacedrive.",
"key_manager": "Gestionnaire de clés",
"key_manager_description": "Créez des clés de chiffrement, montez et démontez vos clés pour voir les fichiers déchiffrés à la volée.",
"keybinds": "Raccourcis clavier",
"keybinds_description": "Afficher et gérer les raccourcis clavier du client",
"keys": "Clés",
"kilometers": "Kilomètres",
"learn_more_about_telemetry": "En savoir plus sur la télémesure",
"libraries": "Bibliothèques",
"libraries_description": "La base de données contient toutes les données de la bibliothèque et les métadonnées des fichiers.",
"library": "Bibliothèque",
"library_name": "Nom de la bibliothèque",
"library_settings": "Paramètres de la bibliothèque",
"library_settings_description": "Paramètres généraux liés à la bibliothèque actuellement active.",
"list_view": "Vue en liste",
"list_view_notice_description": "Naviguez facilement à travers vos fichiers et dossiers avec la vue en liste. Cette vue affiche vos fichiers dans un format de liste simple et organisé, vous permettant de localiser et d'accéder rapidement aux fichiers dont vous avez besoin.",
"loading": "Chargement",
"local_locations": "Emplacements locaux",
"local_node": "Nœud local",
"location_display_name_info": "Le nom de cet emplacement, c'est ce qui sera affiché dans la barre latérale. Ne renommera pas le dossier réel sur le disque.",
"location_is_already_linked": "L'emplacement est déjà lié",
"location_offline_tooltip": "L'emplacement est hors ligne, vous pouvez toujours parcourir et organiser.",
"location_path_info": "Le chemin vers cet emplacement, c'est là que les fichiers seront stockés sur le disque.",
"location_type": "Type d'emplacement",
"location_type_managed": "Spacedrive triera les fichiers pour vous. Si l'emplacement n'est pas vide, un dossier \"spacedrive\" sera créé.",
"location_type_normal": "Le contenu sera indexé tel quel, les nouveaux fichiers ne seront pas automatiquement triés.",
"location_type_replica": "Cet emplacement est une réplique d'un autre, son contenu sera automatiquement synchronisé.",
"locations": "Emplacements",
"locations_description": "Gérez vos emplacements de stockage.",
"lock": "Verrouiller",
"log_in_with_browser": "Se connecter avec le navigateur",
"log_out": "Se déconnecter",
"manage_library": "Gérer la bibliothèque",
"managed": "Géré",
"media": "Médias",
"media_view_context": "Contexte de vue média",
"media_view_notice_description": "Découvrez facilement les photos et vidéos, la vue média affichera les résultats en commençant par l'emplacement actuel, y compris les sous-répertoires.",
"meet_contributors_behind_spacedrive": "Rencontrez les contributeurs derrière Spacedrive",
"miles": "Miles",
"mode": "Mode",
"modified": "Modifié",
"more": "Plus",
"more_actions": "Plus d'actions...",
"more_info": "Plus d'info",
"move_files": "Déplacer les fichiers",
"name": "Nom",
"navigate_back": "Naviguer en arrière",
"navigate_forward": "Naviguer en avant",
"network_page_description": "Les autres nœuds Spacedrive de votre LAN apparaîtront ici, ainsi que vos montages réseau par défaut du système d'exploitation.",
"networking": "Réseautage",
"networking_port": "Port réseau",
"networking_port_description": "Le port pour la communication en peer-to-peer de Spacedrive. Vous devriez laisser ceci désactivé à moins que vous n'ayez un pare-feu restrictif. Ne pas exposer à internet !",
"new_folder": "Nouveau dossier",
"new_library": "Nouvelle bibliothèque",
"new_location": "Nouvel emplacement",
"new_location_web_description": "Comme vous utilisez la version navigateur de Spacedrive, vous devrez (pour l'instant) spécifier une URL absolue d'un répertoire local au nœud distant.",
"new_tab": "Nouvel onglet",
"new_tag": "Nouvelle étiquette",
"no_files_found_here": "Aucun fichier trouvé ici",
"no_jobs": "Aucun travail.",
"no_tag_selected": "Aucune étiquette sélectionnée",
"no_tags": "Aucune étiquette",
"node_name": "Nom du nœud",
"nodes": "Nœuds",
"nodes_description": "Gérez les nœuds connectés à cette bibliothèque. Un nœud est une instance du backend de Spacedrive, s'exécutant sur un appareil ou un serveur. Chaque nœud porte une copie de la base de données et se synchronise via des connexions peer-to-peer en temps réel.",
"none": "Aucun",
"normal": "Normal",
"not_you": "Pas vous ?",
"number_of_passes": "Nombre de passes",
"object_id": "ID de l'objet",
"offline": "Hors ligne",
"online": "En ligne",
"open": "Ouvrir",
"open_file": "Ouvrir le fichier",
"open_new_location_once_added": "Ouvrir le nouvel emplacement une fois ajouté",
"open_settings": "Ouvrir les paramètres",
"open_with": "Ouvrir avec",
"or": "OU",
"pair": "Associer",
"pairing_with_node": "Appairage avec {{node}}",
"paste": "Coller",
"path": "Chemin",
"path_copied_to_clipboard_description": "Chemin pour l'emplacement {{location}} copié dans le presse-papiers.",
"path_copied_to_clipboard_title": "Chemin copié dans le presse-papiers",
"pause": "Pause",
"peers": "Pairs",
"people": "Personnes",
"privacy": "Confidentialité",
"privacy_description": "Spacedrive est conçu pour la confidentialité, c'est pourquoi nous sommes open source et local d'abord. Nous serons donc très clairs sur les données partagées avec nous.",
"quick_preview": "Aperçu rapide",
"quick_view": "Vue rapide",
"recent_jobs": "Travaux récents",
"regen_labels": "Régénérer les étiquettes",
"regen_thumbnails": "Régénérer les vignettes",
"regenerate_thumbs": "Régénérer les miniatures",
"reindex": "Réindexer",
"reject": "Rejeter",
"reload": "Recharger",
"remove": "Retirer",
"remove_from_recents": "Retirer des récents",
"rename": "Renommer",
"replica": "Réplique",
"rescan_directory": "Réanalyser le répertoire",
"rescan_location": "Réanalyser l'emplacement",
"reset": "Réinitialiser",
"resources": "Ressources",
"restore": "Restaurer",
"resume": "Reprendre",
"retry": "Réessayer",
"revel_in_browser": "Révéler dans {{browser}}",
"running": "En cours",
"save": "Sauvegarder",
"save_changes": "Sauvegarder les modifications",
"search_extensions": "Rechercher des extensions",
"secure_delete": "Suppression sécurisée",
"security": "Sécurité",
"security_description": "Gardez votre client en sécurité.",
"send": "Envoyer",
"settings": "Paramètres",
"setup": "Configuration",
"share": "Partager",
"sharing": "Partage",
"sharing_description": "Gérer qui a accès à vos bibliothèques.",
"show_details": "Afficher les détails",
"show_hidden_files": "Afficher les fichiers cachés",
"show_object_size": "Afficher la taille de l'objet",
"show_path_bar": "Afficher la barre de chemin",
"show_slider": "Afficher le curseur",
"size": "Taille",
"skip_login": "Passer la connexion",
"sort_by": "Trier par",
"spacedrop_a_file": "Déposer un fichier dans Spacedrive",
"square_thumbnails": "Vignettes carrées",
"star_on_github": "Mettre une étoile sur GitHub",
"stop": "Arrêter",
"success": "Succès",
"support": "Support",
"sync": "Synchroniser",
"syncPreviewMedia_label": "Synchroniser les médias d'aperçu pour cet emplacement avec vos appareils",
"sync_description": "Gérer la manière dont Spacedrive se synchronise.",
"sync_with_library": "Synchroniser avec la bibliothèque",
"sync_with_library_description": "Si activé, vos raccourcis seront synchronisés avec la bibliothèque, sinon ils s'appliqueront uniquement à ce client.",
"tags": "Étiquettes",
"tags_description": "Gérer vos étiquettes.",
"telemetry_description": "Activez pour fournir aux développeurs des données détaillées d'utilisation et de télémesure afin d'améliorer l'application. Désactivez pour n'envoyer que les données de base : votre statut d'activité, la version de l'application, la version du noyau et la plateforme (par exemple, mobile, web ou ordinateur de bureau).",
"telemetry_title": "Partager des données de télémesure et d'utilisation supplémentaires",
"temperature": "Température",
"thumbnailer_cpu_usage": "Utilisation CPU du générateur de vignettes",
"thumbnailer_cpu_usage_description": "Limiter la quantité de CPU que le générateur de vignettes peut utiliser pour le traitement en arrière-plan.",
"toggle_all": "Basculer tous",
"type": "Type",
"ui_animations": "Animations de l'interface",
"ui_animations_description": "Les dialogues et autres éléments d'interface animeront lors de l'ouverture et de la fermeture.",
"usage": "Utilisation",
"usage_description": "Votre utilisation de la bibliothèque et les informations matérielles",
"value": "Valeur",
"video_preview_not_supported": "L'aperçu vidéo n'est pas pris en charge.",
"want_to_do_this_later": "Vous voulez faire ça plus tard ?",
"website": "Site web",
"your_account": "Votre compte",
"your_account_description": "Compte et informations Spacedrive.",
"your_local_network": "Votre réseau local",
"your_privacy": "Votre confidentialité"
}

1
interface/locales/i18nnext.d.ts vendored Normal file
View file

@ -0,0 +1 @@
declare module 'virtual:*';

View file

@ -0,0 +1,349 @@
{
"meet_title": "{{title}} ile Tanışın",
"about": "Hakkında",
"about_vision_text": "Birçoğumuzun birden fazla bulut hesabı, yedeklenmemiş sürücüleri ve kaybolma riski taşıyan verileri var. Google Fotoğraflar ve iCloud gibi bulut hizmetlerine bağımlıyız, ancak sınırlı kapasiteyle ve hizmetler ile işletim sistemleri arasında neredeyse sıfır geçiş yapabilirlikle kısıtlanmış durumdayız. Fotoğraf albümleri bir cihaz ekosisteminde sıkışıp kalmamalı veya reklam verileri için kullanılmamalıdır. OS bağımsız, kalıcı ve kişisel olarak sahip olunmalıdır. Oluşturduğumuz veriler, bizden uzun süre yaşayacak mirasımızdır - verilerimiz üzerinde mutlak kontrol sağlamak için açık kaynak teknolojisi tek yoludur, sınırsız ölçekte.",
"about_vision_title": "Vizyon",
"accept": "Kabul Et",
"accessed": "Erişildi",
"account": "Hesap",
"actions": "Eylemler",
"add": "Ekle",
"add_device": "Cihaz Ekle",
"add_library": "Kütüphane Ekle",
"add_location_description": "Favori konumlarınızı kişisel kütüphanenize ekleyerek Spacedrive deneyiminizi geliştirin, sorunsuz ve verimli dosya yönetimi için.",
"add_location_tooltip": "Dizin olarak ekleyin",
"add_locations": "Konumlar Ekle",
"add_tag": "Etiket Ekle",
"advanced_settings": "Gelişmiş Ayarlar",
"all_jobs_have_been_cleared": "Tüm işler temizlendi.",
"alpha_release_description": "Spacedrive'ı Alpha sürümünde denemeniz için heyecanlıyız, heyecan verici yeni özellikleri sergiliyoruz. Her ilk sürümde olduğu gibi, bu sürümde bazı hatalar içerebilir. Karşılaştığınız sorunları Discord kanalımızda bildirerek yardımcı olmanızı rica ediyoruz. Değerli geri bildirimleriniz, kullanıcı deneyimini geliştirmeye büyük katkıda bulunacak.",
"alpha_release_title": "Alfa Sürümü",
"appearance": "Görünüm",
"appearance_description": "İstemcinizin görünümünü değiştirin.",
"archive": "Arşiv",
"archive_coming_soon": "Arşivleme konumları yakında geliyor...",
"archive_info": "Verileri Kütüphane'den arşiv olarak çıkarın, Konum klasör yapısını korumak için kullanışlıdır.",
"are_you_sure": "Emin misiniz?",
"assign_tag": "Etiket Ata",
"audio_preview_not_supported": "Ses önizlemesi desteklenmiyor.",
"back": "Geri",
"backups": "Yedeklemeler",
"backups_description": "Spacedrive veritabanı yedeklerinizi yönetin.",
"blur_effects": "Bulanıklaştırma Efektleri",
"blur_effects_description": "Bazı bileşenlere bulanıklaştırma efekti uygulanacak.",
"cancel": "İptal",
"celcius": "Santigrat",
"change": "Değiştir",
"changelog": "Değişiklik Günlüğü",
"changelog_page_description": "Yaptığımız havalı yeni özellikleri görün",
"changelog_page_title": "Değişiklik Günlüğü",
"checksum": "Kontrol Toplamı",
"clear_finished_jobs": "Biten işleri temizle",
"client": "İstemci",
"close": "Kapat",
"clouds": "Bulutlar",
"color": "Renk",
"coming_soon": "Yakında",
"compress": "Sıkıştır",
"configure_location": "Konumu Yapılandır",
"connected": "Bağlı",
"contacts": "Kişiler",
"contacts_description": "Kişilerinizi Spacedrive'da yönetin.",
"content_id": "İçerik ID",
"continue": "Devam Et",
"convert_to": "Dönüştür",
"coordinates": "Koordinatlar",
"copied": "Kopyalandı",
"copy": "Kopyala",
"copy_as_path": "Yolu kopyala",
"copy_path_to_clipboard": "Yolu panoya kopyala",
"create": "Oluştur",
"create_library": "Kütüphane Oluştur",
"create_library_description": "Kütüphaneler güvenli, cihaz içi bir veritabanıdır. Dosyalarınız olduğu yerde kalır, Kütüphane onları kataloglar ve tüm Spacedrive ile ilgili verileri depolar.",
"create_new_library": "Yeni kütüphane oluştur",
"create_new_library_description": "Kütüphaneler güvenli, cihaz içi bir veritabanıdır. Dosyalarınız olduğu yerde kalır, Kütüphane onları kataloglar ve tüm Spacedrive ile ilgili verileri depolar.",
"create_new_tag": "Yeni Etiket Oluştur",
"create_new_tag_description": "Bir ad ve renk seçin.",
"create_tag": "Etiket Oluştur",
"created": "Oluşturuldu",
"creating_library": "Kütüphane oluşturuluyor...",
"creating_your_library": "Kütüphaneniz oluşturuluyor",
"current": "Mevcut",
"current_directory": "Mevcut Dizin",
"current_directory_with_descendants": "Alt Dizinler Dahil Mevcut Dizin",
"custom": "Özel",
"cut": "Kes",
"data_folder": "Veri Klasörü",
"debug_mode": "Hata Ayıklama Modu",
"debug_mode_description": "Uygulama içinde ek hata ayıklama özelliklerini etkinleştir.",
"default": "Varsayılan",
"delete": "Sil",
"delete_dialog_title": "{{prefix}} {{type}} Sil",
"delete_info": "Bu, diskteki gerçek klasörü silmeyecek. Önizleme medyası silinecek.",
"delete_library": "Kütüphaneyi Sil",
"delete_library_description": "Bu kalıcıdır, dosyalarınız silinmeyecek, sadece Spacedrive kütüphanesi silinecek.",
"delete_location": "Konumu Sil",
"delete_location_description": "Bir konumu silmek, Spacedrive veritabanından ilişkili tüm dosyaları da kaldıracaktır, dosyalar kendileri silinmez.",
"delete_rule": "Kuralı Sil",
"delete_tag": "Etiketi Sil",
"delete_tag_description": "Bu etiketi silmek istediğinizden emin misiniz? Bu geri alınamaz ve etiketli dosyalar bağlantısız kalacak.",
"delete_warning": "Uyarı: Bu, {{type}}'ınızı sonsuza dek silecek, henüz çöp kutumuz yok...",
"description": "Açıklama",
"deselect": "Seçimi Kaldır",
"details": "Detaylar",
"devices_coming_soon_tooltip": "Yakında geliyor! Bu alfa sürümü kütüphane senkronizasyonunu içermiyor, çok yakında hazır olacak.",
"direction": "Yön",
"disabled": "Devre Dışı",
"display_formats": "Görüntüleme Formatları",
"display_name": "Görünen İsim",
"distance": "Mesafe",
"done": "Tamam",
"dont_show_again": "Tekrar gösterme",
"double_click_action": "Çift tıklama eylemi",
"download": "İndir",
"duplicate": "Kopyala",
"edit": "Düzenle",
"edit_library": "Kütüphaneyi Düzenle",
"edit_location": "Konumu Düzenle",
"enable_networking": "Ağı Etkinleştir",
"encrypt": "Şifrele",
"encrypt_library": "Kütüphaneyi Şifrele",
"encrypt_library_coming_soon": "Kütüphane şifreleme yakında geliyor",
"encrypt_library_description": "Bu kütüphane için şifrelemeyi etkinleştirin, bu sadece Spacedrive veritabanını şifreler, dosyaların kendisini değil.",
"ephemeral_notice_browse": "Dosyalarınızı ve klasörlerinizi doğrudan cihazınızdan göz atın.",
"ephemeral_notice_consider_indexing": "Daha hızlı ve daha verimli bir keşif için yerel konumlarınızı indekslemeyi düşünün.",
"erase": "Sil",
"erase_a_file": "Bir dosyayı sil",
"erase_a_file_description": "Silme ayarlarınızı yapılandırın.",
"error": "Hata",
"error_loading_original_file": "Orijinal dosya yüklenirken hata",
"expand": "Genişlet",
"explorer": "Gezgin",
"export": "Dışa Aktar",
"export_library": "Kütüphaneyi Dışa Aktar",
"export_library_coming_soon": "Kütüphaneyi dışa aktarma yakında geliyor",
"export_library_description": "Bu kütüphaneyi bir dosya olarak dışa aktar.",
"extensions": "Uzantılar",
"extensions_description": "Bu istemcinin işlevselliğini genişletmek için uzantıları yükleyin.",
"fahrenheit": "Fahrenheit",
"failed_to_cancel_job": "İş iptal edilemedi.",
"failed_to_clear_all_jobs": "Tüm işler temizlenemedi.",
"failed_to_copy_file_path": "Dosya yolu kopyalanamadı",
"failed_to_duplicate_file": "Dosya kopyalanamadı",
"failed_to_generate_checksum": "Kontrol toplamı oluşturulamadı",
"failed_to_generate_labels": "Etiketler oluşturulamadı",
"failed_to_generate_thumbnails": "Küçük resimler oluşturulamadı",
"failed_to_load_tags": "Etiketler yüklenemedi",
"failed_to_pause_job": "İş duraklatılamadı.",
"failed_to_reindex_location": "Konum yeniden indekslenemedi",
"failed_to_remove_file_from_recents": "Dosya son kullanılanlardan kaldırılamadı",
"failed_to_remove_job": "İş kaldırılamadı.",
"failed_to_rescan_location": "Konum tekrar taranamadı",
"failed_to_resume_job": "İş devam ettirilemedi.",
"failed_to_update_location_settings": "Konum ayarları güncellenemedi",
"favorite": "Favori",
"file_indexing_rules": "Dosya İndeksleme Kuralları",
"filters": "Filtreler",
"forward": "İleri",
"full_disk_access": "Tam Disk Erişimi",
"full_disk_access_description": "En iyi deneyimi sağlamak için dosyalarınızı indekslemek üzere diskinize erişmemiz gerekiyor. Dosyalarınız yalnızca size açık.",
"full_reindex": "Tam Yeniden İndeksleme",
"full_reindex_info": "Bu Konumun tam taramasını gerçekleştirin.",
"general": "Genel",
"general_settings": "Genel Ayarlar",
"general_settings_description": "Bu müşteriye ilişkin genel ayarlar.",
"generatePreviewMedia_label": "Bu Konum için önizleme medyası oluştur",
"generate_checksums": "Kontrol Toplamları Oluştur",
"go_back": "Geri Dön",
"got_it": "Anladım",
"grid_gap": "Boşluk",
"grid_view": "Izgara Görünümü",
"grid_view_notice_description": "Dosyalarınıza Izgara Görünümü ile görsel bir genel bakış alın. Bu görünüm dosya ve klasörlerinizi küçük resim görüntüleri olarak gösterir, aradığınız dosyayı hızlıca tanımlamanızı sağlar.",
"hidden_label": "Konumun ve içeriğinin özet kategorilerde, aramalarda ve etiketlerde görünmesini engeller, 'Gizli öğeleri göster' etkinleştirilmedikçe.",
"hide_in_library_search": "Kütüphane aramasında gizle",
"hide_in_library_search_description": "Bu etiketle olan dosyaları kütüphanenin tamamında yapılan aramalarda sonuçlardan gizle.",
"hide_in_sidebar": "Kenar çubuğunda gizle",
"hide_in_sidebar_description": "Bu etiketin uygulamanın kenar çubuğunda gösterilmesini engelle.",
"hide_location_from_view": "Konumu ve içeriğini görünümden gizle",
"image_labeler_ai_model": "Resim etiket tanıma AI modeli",
"image_labeler_ai_model_description": "Resimlerdeki nesneleri tanımak için kullanılan model. Daha büyük modeller daha doğru ancak daha yavaştır.",
"import": "İçe Aktar",
"indexed": "İndekslenmiş",
"indexer_rule_reject_allow_label": "Varsayılan olarak, bir indeksleyici kuralı Red listesi olarak işlev görür ve kriterlerine uyan tüm dosyaları hariç tutar. Bu seçeneği etkinleştirerek onu bir İzin listesine dönüştürebilirsiniz, böylece yalnızca belirli kurallarını karşılayan dosyaları indeksler.",
"indexer_rules": "İndeksleyici Kuralları",
"indexer_rules_info": "Globları kullanarak göz ardı edilecek yolları belirtmenize olanak tanır.",
"install": "Yükle",
"install_update": "Güncellemeyi Yükle",
"installed": "Yüklendi",
"item_size": "Öğe Boyutu",
"job_has_been_canceled": "İş iptal edildi.",
"job_has_been_paused": "İş duraklatıldı.",
"job_has_been_removed": "İş kaldırıldı.",
"job_has_been_resumed": "İş devam ettirildi.",
"join": "Katıl",
"join_discord": "Discord'a Katıl",
"join_library": "Kütüphaneye Katıl",
"join_library_description": "Kütüphaneler güvenli, cihaz içi bir veritabanıdır. Dosyalarınız yerinde kalır, Kütüphane onları kataloglar ve tüm Spacedrive ile ilgili verileri saklar.",
"key_manager": "Anahtar Yöneticisi",
"key_manager_description": "Şifreleme anahtarları oluştur, anahtarlarınızı tak ve çıkararak dosyalarınızın uçuşta şifresini çözün.",
"keybinds": "Tuş Bağlamaları",
"keybinds_description": "İstemci tuş bağlamalarını görüntüleyin ve yönetin",
"keys": "Anahtarlar",
"kilometers": "Kilometreler",
"learn_more_about_telemetry": "Telemetri hakkında daha fazla bilgi edinin",
"libraries": "Kütüphaneler",
"libraries_description": "Veritabanı tüm kütüphane verilerini ve dosya metaverilerini içerir.",
"library": "Kütüphane",
"library_name": "Kütüphane adı",
"library_settings": "Kütüphane Ayarları",
"library_settings_description": "Şu anda aktif olan kütüphane ile ilgili genel ayarlar.",
"list_view": "Liste Görünümü",
"list_view_notice_description": "Dosyalarınızın ve klasörlerinizin arasında kolayca gezinmek için Liste Görünümünü kullanın. Bu görünüm dosyalarınızı basit, düzenli bir liste formatında gösterir, ihtiyacınız olan dosyalara hızla ulaşıp onlara erişmenizi sağlar.",
"loading": "Yükleniyor",
"local_locations": "Yerel Konumlar",
"local_node": "Yerel Düğüm",
"location_display_name_info": "Bu Konumun adı, kenar çubuğunda gösterilecek olan budur. Diskteki gerçek klasörü yeniden adlandırmaz.",
"location_is_already_linked": "Konum zaten bağlantılı",
"location_offline_tooltip": "Konum çevrimdışı, yine de göz atabilir ve düzenleyebilirsiniz.",
"location_path_info": "Bu Konumun yolu, dosyaların disk üzerinde saklandığı yerdir.",
"location_type": "Konum Türü",
"location_type_managed": "Spacedrive sizin için dosyaları sıralar. Eğer Konum boş değilse, bir \"spacedrive\" klasörü oluşturulacak.",
"location_type_normal": "İçerik olduğu gibi indekslenecek, yeni dosyalar otomatik olarak sıralanmayacak.",
"location_type_replica": "Bu Konum başka birinin kopyasıdır, içeriği otomatik olarak senkronize edilecektir.",
"locations": "Konumlar",
"locations_description": "Depolama konumlarınızı yönetin.",
"lock": "Kilitle",
"log_in_with_browser": "Tarayıcı ile Giriş Yap",
"log_out": ıkış Yap",
"manage_library": "Kütüphaneyi Yönet",
"managed": "Yönetilen",
"media": "Medya",
"media_view_context": "Medya Görünümü Bağlamı",
"media_view_notice_description": "Fotoğrafları ve videoları kolayca keşfedin, Medya Görünümü alt dizinler dahil olmak üzere mevcut konumdan itibaren sonuçları gösterecektir.",
"meet_contributors_behind_spacedrive": "Spacedrive'ın arkasındaki katkıda bulunanlarla tanışın",
"miles": "Mil",
"mode": "Mod",
"modified": "Değiştirildi",
"more": "Daha fazla",
"more_actions": "Daha fazla eylem...",
"more_info": "Daha fazla bilgi",
"move_files": "Dosyaları Taşı",
"name": "Ad",
"navigate_back": "Geri git",
"navigate_forward": "İleri git",
"network_page_description": "Yerel Ağınızda diğer Spacedrive düğümleri burada görünecek, varsayılan işletim sistemi ağ bağlantıları ile birlikte.",
"networking": "Ağ",
"networking_port": "Ağ Portu",
"networking_port_description": "Spacedrive'in Eşler Arası ağ iletişimi için kullanılacak port. Restriktif bir güvenlik duvarınız yoksa bunu devre dışı bırakmalısınız. İnternete açmayın!",
"new_folder": "Yeni Klasör",
"new_library": "Yeni kütüphane",
"new_location": "Yeni konum",
"new_location_web_description": "Spacedrive'ın tarayıcı versiyonunu kullanıyorsanız, şimdilik uzak düğüme yerel bir dizinin mutlak URL'sini belirtmeniz gerekecek.",
"new_tab": "Yeni Sekme",
"new_tag": "Yeni etiket",
"no_files_found_here": "Burada dosya bulunamadı",
"no_jobs": "İş yok.",
"no_tag_selected": "Etiket Seçilmedi",
"no_tags": "Etiket yok",
"node_name": "Düğüm Adı",
"nodes": "Düğümler",
"nodes_description": "Bu kütüphaneye bağlı düğümleri yönetin. Bir düğüm, bir cihazda veya sunucuda çalışan Spacedrive'ın arka ucunun bir örneğidir. Her düğüm bir veritabanı kopyası taşır ve eşler arası bağlantılar üzerinden gerçek zamanlı olarak senkronize olur.",
"none": "Hiçbiri",
"normal": "Normal",
"not_you": "Siz değil misiniz?",
"number_of_passes": "Geçiş sayısı",
"object_id": "Nesne ID",
"offline": "Çevrimdışı",
"online": "Çevrimiçi",
"open": "Aç",
"open_file": "Dosyayı Aç",
"open_new_location_once_added": "Yeni konum eklendikten sonra aç",
"open_settings": "Ayarları Aç",
"open_with": "İle aç",
"or": "VEYA",
"pair": "Eşle",
"pairing_with_node": "{{node}} ile eşleşiyor",
"paste": "Yapıştır",
"path": "Yol",
"path_copied_to_clipboard_description": "{{location}} konumu için yol panoya kopyalandı.",
"path_copied_to_clipboard_title": "Yol panoya kopyalandı",
"pause": "Durdur",
"peers": "Eşler",
"people": "İnsanlar",
"privacy": "Gizlilik",
"privacy_description": "Spacedrive gizlilik için tasarlandı, bu yüzden açık kaynaklı ve yerel ilkeliyiz. Bu yüzden hangi verilerin bizimle paylaşıldığı konusunda çok açık olacağız.",
"quick_preview": "Hızlı Önizleme",
"quick_view": "Hızlı bakış",
"recent_jobs": "Son İşler",
"regen_labels": "Etiketleri Yeniden Oluştur",
"regen_thumbnails": "Küçük Resimleri Yeniden Oluştur",
"regenerate_thumbs": "Küçük Resimleri Yeniden Oluştur",
"reindex": "Yeniden İndeksle",
"reject": "Reddet",
"reload": "Yeniden Yükle",
"remove": "Kaldır",
"remove_from_recents": "Son Kullanılanlardan Kaldır",
"rename": "Yeniden Adlandır",
"replica": "Replika",
"rescan_directory": "Dizini Yeniden Tara",
"rescan_location": "Konumu Yeniden Tara",
"reset": "Sıfırla",
"resources": "Kaynaklar",
"restore": "Geri Yükle",
"resume": "Devam Ettir",
"retry": "Yeniden Dene",
"revel_in_browser": "{{browser}}'da Göster",
"running": "Çalışıyor",
"save": "Kaydet",
"save_changes": "Değişiklikleri Kaydet",
"search_extensions": "Arama uzantıları",
"secure_delete": "Güvenli sil",
"security": "Güvenlik",
"security_description": "İstemcinizi güvende tutun.",
"send": "Gönder",
"settings": "Ayarlar",
"setup": "Kurulum",
"share": "Paylaş",
"sharing": "Paylaşım",
"sharing_description": "Kütüphanelerinize kimlerin erişim sağlayabileceğini yönetin.",
"show_details": "Detayları Göster",
"show_hidden_files": "Gizli Dosyaları Göster",
"show_object_size": "Nesne Boyutunu Göster",
"show_path_bar": "Yol Çubuğunu Göster",
"show_slider": "Kaydırıcıyı Göster",
"size": "Boyut",
"skip_login": "Girişi Atla",
"sort_by": "Sırala",
"spacedrop_a_file": "Bir Dosya Spacedropa",
"square_thumbnails": "Kare Küçük Resimler",
"star_on_github": "GitHub'da Yıldızla",
"stop": "Durdur",
"success": "Başarılı",
"support": "Destek",
"sync": "Senkronize Et",
"syncPreviewMedia_label": "Bu Konum için ön izleme medyasını cihazlarınızla senkronize et",
"sync_description": "Spacedrive'ın nasıl senkronize edileceğini yönetin.",
"sync_with_library": "Kütüphane ile Senkronize Et",
"sync_with_library_description": "Etkinleştirilirse tuş bağlamalarınız kütüphane ile senkronize edilecek, aksi takdirde yalnızca bu istemciye uygulanacak.",
"tags": "Etiketler",
"tags_description": "Etiketlerinizi yönetin.",
"telemetry_description": "Uygulamayı iyileştirmek için geliştiricilere detaylı kullanım ve telemetri verisi sağlamak için AÇIK konumuna getirin. Yalnızca temel verileri göndermek için KAPALI konumuna getirin: faaliyet durumunuz, uygulama sürümü, çekirdek sürümü ve platform (örn., mobil, web veya masaüstü).",
"telemetry_title": "Ek Telemetri ve Kullanım Verisi Paylaş",
"temperature": "Sıcaklık",
"thumbnailer_cpu_usage": "Küçükresim Oluşturucunun CPU kullanımı",
"thumbnailer_cpu_usage_description": "Küçükresim oluşturucunun arka planda işleme için ne kadar CPU kullanacağını sınırlayın.",
"toggle_all": "Hepsini Değiştir",
"type": "Tip",
"ui_animations": "UI Animasyonları",
"ui_animations_description": "Diyaloglar ve diğer UI elementleri açılırken ve kapanırken animasyon gösterecek.",
"usage": "Kullanım",
"usage_description": "Kütüphanenizi kullanımı ve donanım bilgileri",
"value": "Değer",
"video_preview_not_supported": "Video ön izlemesi desteklenmiyor.",
"want_to_do_this_later": "Bunu daha sonra yapmak ister misiniz?",
"website": "Web Sitesi",
"your_account": "Hesabınız",
"your_account_description": "Spacedrive hesabınız ve bilgileri.",
"your_local_network": "Yerel Ağınız",
"your_privacy": "Gizliliğiniz"
}

View file

@ -0,0 +1,349 @@
{
"meet_title": "遇见 {{title}}",
"about": "关于",
"about_vision_text": "我们中的许多人拥有多个云账户磁盘没有备份数据有丢失的风险。我们依赖像Google照片和iCloud这样的云服务但却受限于有限的容量和几乎零互操作性服务和操作系统之间无法相互配合。相册不应该被困在一个设备生态系统中或者被用来收割广告数据。它们应该是与操作系统无关永久的个人拥有的。我们创造的数据是我们的遗产将比我们活得更久——开源技术是确保我们对定义我们生活的数据拥有绝对控制权的唯一方式没有限制。",
"about_vision_title": "愿景",
"accept": "接受",
"accessed": "已访问",
"account": "账户",
"actions": "操作",
"add": "添加",
"add_device": "添加设备",
"add_library": "添加库",
"add_location_description": "通过将您喜爱的位置添加到您的个人库中增强您的Spacedrive体验以实现无缝高效的文件管理。",
"add_location_tooltip": "添加路径作为索引位置",
"add_locations": "添加位置",
"add_tag": "添加标签",
"advanced_settings": "高级设置",
"all_jobs_have_been_cleared": "所有任务已被清除。",
"alpha_release_description": "我们很高兴您尝试Spacedrive现在处于Alpha发布阶段展示了激动人心的新功能。与任何初始版本一样这个版本可能包含一些错误。我们恳请您在我们的Discord频道上报告您遇到的任何问题。您宝贵的反馈将极大地有助于增强用户体验。",
"alpha_release_title": "Alpha版本",
"appearance": "外观",
"appearance_description": "改变您的客户端外观。",
"archive": "存档",
"archive_coming_soon": "存档位置功能即将推出…",
"archive_info": "将库中的数据作为存档提取,有利于保留位置文件夹结构。",
"are_you_sure": "您确定吗?",
"assign_tag": "分配标签",
"audio_preview_not_supported": "不支持音频预览。",
"back": "返回",
"backups": "备份",
"backups_description": "管理您的Spacedrive数据库备份。",
"blur_effects": "模糊效果",
"blur_effects_description": "某些组件将应用模糊效果。",
"cancel": "取消",
"celcius": "摄氏度",
"change": "更改",
"changelog": "更新日志",
"changelog_page_description": "查看我们正在开发的酷炫新功能",
"changelog_page_title": "更新日志",
"checksum": "校验和",
"clear_finished_jobs": "清除已完成的任务",
"client": "客户端",
"close": "关闭",
"clouds": "云",
"color": "颜色",
"coming_soon": "即将推出",
"compress": "压缩",
"configure_location": "配置位置",
"connected": "已连接",
"contacts": "联系人",
"contacts_description": "在Spacedrive中管理您的联系人。",
"content_id": "内容ID",
"continue": "继续",
"convert_to": "转换为",
"coordinates": "坐标",
"copied": "已复制",
"copy": "复制",
"copy_as_path": "复制为路径",
"copy_path_to_clipboard": "复制路径到剪贴板",
"create": "创建",
"create_library": "创建库",
"create_library_description": "库是一个安全的设备上的数据库。您的文件保持原位库对其进行目录编制并存储所有Spacedrive相关数据。",
"create_new_library": "创建新库",
"create_new_library_description": "库是一个安全的设备上的数据库。您的文件保持原位库对其进行目录编制并存储所有Spacedrive相关数据。",
"create_new_tag": "创建新标签",
"create_new_tag_description": "选择一个名称和颜色。",
"create_tag": "创建标签",
"created": "已创建",
"creating_library": "正在创建库…",
"creating_your_library": "正在创建您的库",
"current": "当前的",
"current_directory": "当前目录",
"current_directory_with_descendants": "当前目录及其子目录",
"custom": "自定义",
"cut": "剪切",
"data_folder": "数据文件夹",
"debug_mode": "调试模式",
"debug_mode_description": "在应用内启用额外的调试功能。",
"default": "默认",
"delete": "删除",
"delete_dialog_title": "删除 {{prefix}} {{type}}",
"delete_info": "这不会删除磁盘上的实际文件夹。预览媒体将被删除。",
"delete_library": "删除库",
"delete_library_description": "这是永久性的您的文件不会被删除只有Spacedrive库会被删除。",
"delete_location": "删除位置",
"delete_location_description": "删除一个位置也将删除所有与之关联的文件从Spacedrive数据库中文件本身不会被删除。",
"delete_rule": "删除规则",
"delete_tag": "删除标签",
"delete_tag_description": "您确定要删除这个标签吗?这不能被撤销,打过标签的文件将会被取消链接。",
"delete_warning": "警告:这将永久删除您的{{type}},我们目前还没有回收站…",
"description": "描述",
"deselect": "取消选择",
"details": "详情",
"devices_coming_soon_tooltip": "即将推出这个Alpha版本不包括库同步很快就会准备好。",
"direction": "方向",
"disabled": "已禁用",
"display_formats": "显示格式",
"display_name": "显示名称",
"distance": "距离",
"done": "完成",
"dont_show_again": "不再显示",
"double_click_action": "双击操作",
"download": "下载",
"duplicate": "复制",
"edit": "编辑",
"edit_library": "编辑库",
"edit_location": "编辑位置",
"enable_networking": "启用网络",
"encrypt": "加密",
"encrypt_library": "加密库",
"encrypt_library_coming_soon": "库加密即将推出",
"encrypt_library_description": "为这个库启用加密这只会加密Spacedrive数据库不会加密文件本身。",
"ephemeral_notice_browse": "直接从您的设备浏览您的文件和文件夹。",
"ephemeral_notice_consider_indexing": "考虑索引您本地的位置,以获得更快和更高效的浏览。",
"erase": "擦除",
"erase_a_file": "擦除一个文件",
"erase_a_file_description": "配置您的擦除设置。",
"error": "错误",
"error_loading_original_file": "加载原始文件出错",
"expand": "展开",
"explorer": "资源管理器",
"export": "导出",
"export_library": "导出库",
"export_library_coming_soon": "导出库功能即将推出",
"export_library_description": "将这个库导出到一个文件。",
"extensions": "扩展",
"extensions_description": "安装扩展来扩展这个客户端的功能。",
"fahrenheit": "华氏度",
"failed_to_cancel_job": "取消任务失败。",
"failed_to_clear_all_jobs": "清除所有任务失败。",
"failed_to_copy_file_path": "复制文件路径失败",
"failed_to_duplicate_file": "复制文件失败",
"failed_to_generate_checksum": "生成校验和失败",
"failed_to_generate_labels": "生成标签失败",
"failed_to_generate_thumbnails": "生成缩略图失败",
"failed_to_load_tags": "加载标签失败",
"failed_to_pause_job": "暂停任务失败。",
"failed_to_reindex_location": "重索引位置失败",
"failed_to_remove_file_from_recents": "从最近文档中删除文件失败",
"failed_to_remove_job": "删除任务失败。",
"failed_to_rescan_location": "重新扫描位置失败",
"failed_to_resume_job": "恢复任务失败。",
"failed_to_update_location_settings": "更新位置设置失败",
"favorite": "收藏",
"file_indexing_rules": "文件索引规则",
"filters": "过滤器",
"forward": "前进",
"full_disk_access": "完全磁盘访问",
"full_disk_access_description": "为了提供最佳体验,我们需要访问您的磁盘以索引您的文件。您的文件只有您自己可以访问。",
"full_reindex": "完全重新索引",
"full_reindex_info": "执行对此位置的完全重新扫描。",
"general": "一般",
"general_settings": "一般设置",
"general_settings_description": "与此客户端相关的一般设置。",
"generatePreviewMedia_label": "为这个位置生成预览媒体",
"generate_checksums": "生成校验和",
"go_back": "返回",
"got_it": "明白了",
"grid_gap": "间隙",
"grid_view": "网格视图",
"grid_view_notice_description": "通过网格视图直观地了解您的文件。这种视图以缩略图形式显示您的文件和文件夹,方便您快速识别所寻找的文件。",
"hidden_label": "阻止位置及其内容出现在汇总分类、搜索和标签中,除非启用了“显示隐藏项目”。",
"hide_in_library_search": "在库搜索中隐藏",
"hide_in_library_search_description": "在搜索整个库时从结果中隐藏带有此标签的文件。",
"hide_in_sidebar": "在侧边栏中隐藏",
"hide_in_sidebar_description": "阻止此标签在应用的侧边栏中显示。",
"hide_location_from_view": "隐藏位置和内容的视图",
"image_labeler_ai_model": "图像标签识别AI模型",
"image_labeler_ai_model_description": "用于识别图像中对象的模型。较大的模型更准确但速度较慢。",
"import": "导入",
"indexed": "已索引",
"indexer_rule_reject_allow_label": "默认情况下,索引器规则作为拒绝列表,排除与其匹配的任何文件。启用此选项将其变成允许列表,仅索引符合其规定规则的位置的文件。",
"indexer_rules": "索引器规则",
"indexer_rules_info": "索引器规则允许您使用通配符指定要忽略的路径。",
"install": "安装",
"install_update": "安装更新",
"installed": "已安装",
"item_size": "项目大小",
"job_has_been_canceled": "作业已取消。",
"job_has_been_paused": "作业已暂停。",
"job_has_been_removed": "作业已移除。",
"job_has_been_resumed": "作业已恢复。",
"join": "加入",
"join_discord": "加入Discord",
"join_library": "加入一个库",
"join_library_description": "库是一个安全的设备上的数据库。您的文件保持原位库对其进行目录编制并存储所有Spacedrive相关数据。",
"key_manager": "密钥管理器",
"key_manager_description": "创建加密密钥,挂载和卸载密钥以即时查看解密文件。",
"keybinds": "键绑定",
"keybinds_description": "查看和管理客户端键绑定",
"keys": "密钥",
"kilometers": "千米",
"learn_more_about_telemetry": "了解更多有关遥测的信息",
"libraries": "库",
"libraries_description": "数据库包含所有库数据和文件元数据。",
"library": "库",
"library_name": "库名称",
"library_settings": "库设置",
"library_settings_description": "与当前活动库相关的一般设置。",
"list_view": "列表视图",
"list_view_notice_description": "通过列表视图轻松导航您的文件和文件夹。这种视图以简单、有组织的列表形式显示文件,让您能够快速定位和访问所需文件。",
"loading": "正在加载",
"local_locations": "本地位置",
"local_node": "本地节点",
"location_display_name_info": "此位置的名称,这是将显示在侧边栏的名称。不会重命名磁盘上的实际文件夹。",
"location_is_already_linked": "位置已经链接",
"location_offline_tooltip": "位置离线,您仍然可以浏览和组织。",
"location_path_info": "此位置的路径,这是文件在磁盘上的存储位置。",
"location_type": "位置类型",
"location_type_managed": "Spacedrive将为您排序文件。如果位置不为空将创建一个“spacedrive”文件夹。",
"location_type_normal": "内容将按原样索引,新文件不会自动排序。",
"location_type_replica": "此位置是另一个位置的副本,其内容将自动同步。",
"locations": "位置",
"locations_description": "管理您的存储位置。",
"lock": "锁定",
"log_in_with_browser": "用浏览器登录",
"log_out": "退出登录",
"manage_library": "管理库",
"managed": "已管理",
"media": "媒体",
"media_view_context": "媒体视图上下文",
"media_view_notice_description": "轻松发现照片和视频,媒体视图将从当前位置开始显示结果,包括子目录。",
"meet_contributors_behind_spacedrive": "结识Spacedrive背后的贡献者",
"miles": "英里",
"mode": "模式",
"modified": "已修改",
"more": "更多",
"more_actions": "更多操作…",
"more_info": "更多信息",
"move_files": "移动文件",
"name": "名称",
"navigate_back": "回退",
"navigate_forward": "前进",
"network_page_description": "您的局域网上的其他Spacedrive节点将显示在这里以及您的默认操作系统网络挂载。",
"networking": "网络",
"networking_port": "网络端口",
"networking_port_description": "Spacedrive的点对点网络通信使用的端口。除非您有限制性防火墙否则应保持禁用状态。不要暴露到互联网上",
"new_folder": "新文件夹",
"new_library": "新库",
"new_location": "新位置",
"new_location_web_description": "由于您正在使用Spacedrive的浏览器版本您将目前需要指定远程节点本地目录的绝对URL。",
"new_tab": "新标签",
"new_tag": "新标签",
"no_files_found_here": "这里没有找到文件",
"no_jobs": "没有作业。",
"no_tag_selected": "没有选中的标签",
"no_tags": "没有标签",
"node_name": "节点名称",
"nodes": "节点",
"nodes_description": "管理连接到此库的节点。节点是在设备或服务器上运行的Spacedrive后端的实例。每个节点都携带数据库副本并通过点对点连接实时同步。",
"none": "无",
"normal": "普通",
"not_you": "不是您?",
"number_of_passes": "通过次数",
"object_id": "对象ID",
"offline": "离线",
"online": "在线",
"open": "打开",
"open_file": "打开文件",
"open_new_location_once_added": "添加新位置后立即打开",
"open_settings": "打开设置",
"open_with": "打开方式",
"or": "或",
"pair": "配对",
"pairing_with_node": "正在与{{node}}配对",
"paste": "粘贴",
"path": "路径",
"path_copied_to_clipboard_description": "位置{{location}}的路径已复制到剪贴板。",
"path_copied_to_clipboard_title": "路径已复制到剪贴板",
"pause": "暂停",
"peers": "对等点",
"people": "人们",
"privacy": "隐私",
"privacy_description": "Spacedrive是为隐私而构建的这就是为什么我们是开源的以本地优先。因此我们会非常明确地告诉您与我们分享了什么数据。",
"quick_preview": "快速预览",
"quick_view": "快速查看",
"recent_jobs": "最近的作业",
"regen_labels": "重新生成标签",
"regen_thumbnails": "重新生成缩略图",
"regenerate_thumbs": "重新生成缩略图",
"reindex": "重新索引",
"reject": "拒绝",
"reload": "重新加载",
"remove": "移除",
"remove_from_recents": "从最近使用中移除",
"rename": "重命名",
"replica": "副本",
"rescan_directory": "重新扫描目录",
"rescan_location": "重新扫描位置",
"reset": "重置",
"resources": "资源",
"restore": "恢复",
"resume": "恢复",
"retry": "重试",
"revel_in_browser": "在{{browser}}中显示",
"running": "运行中",
"save": "保存",
"save_changes": "保存更改",
"search_extensions": "搜索扩展",
"secure_delete": "安全删除",
"security": "安全",
"security_description": "确保您的客户端安全。",
"send": "发送",
"settings": "设置",
"setup": "设置",
"share": "分享",
"sharing": "共享",
"sharing_description": "管理有权访问您的库的人。",
"show_details": "显示详情",
"show_hidden_files": "显示隐藏文件",
"show_object_size": "显示对象大小",
"show_path_bar": "显示路径栏",
"show_slider": "显示滑块",
"size": "大小",
"skip_login": "跳过登录",
"sort_by": "排序依据",
"spacedrop_a_file": "Spacedrop文件",
"square_thumbnails": "方形缩略图",
"star_on_github": "在GitHub上给星标",
"stop": "停止",
"success": "成功",
"support": "支持",
"sync": "同步",
"syncPreviewMedia_label": "将此位置的预览媒体与您的设备同步",
"sync_description": "管理Spacedrive的同步方式。",
"sync_with_library": "与库同步",
"sync_with_library_description": "如果启用,您的键绑定将与库同步,否则它们只适用于此客户端。",
"tags": "标签",
"tags_description": "管理您的标签。",
"telemetry_description": "切换为开以向开发者提供详细的使用情况和遥测数据,以增强应用程序。切换为关只发送基本数据:您的活动状态、应用版本、核心版本和平台(例如移动、网络或桌面)。",
"telemetry_title": "共享额外的遥测和使用数据",
"temperature": "温度",
"thumbnailer_cpu_usage": "缩略图生成器CPU使用",
"thumbnailer_cpu_usage_description": "限制缩略图生成器在后台处理时可以使用的CPU量。",
"toggle_all": "切换全部",
"type": "类型",
"ui_animations": "用户界面动画",
"ui_animations_description": "打开和关闭时对话框和其他用户界面元素将产生动画效果。",
"usage": "使用情况",
"usage_description": "您的库使用情况和硬件信息",
"value": "值",
"video_preview_not_supported": "不支持视频预览。",
"want_to_do_this_later": "想稍后再做吗?",
"website": "网站",
"your_account": "您的账户",
"your_account_description": "Spacedrive账号和信息。",
"your_local_network": "您的本地网络",
"your_privacy": "您的隐私"
}

View file

@ -0,0 +1,348 @@
{
"meet_title": "會見 {{title}}",
"about": "關於",
"about_vision_text": "我們中的許多人都擁有數個雲帳戶這些雲帳戶中的硬碟未備份且資料面臨丟失的風險。我們依賴諸如Google照片和iCloud之類的雲服務但這些服務容量有限且幾乎不能在不同的服務及作業系統間進行互通。相簿不應僅限於某個裝置生態系統內或被用於收集廣告數據。它們應該是與作業系統無關永久且屬於個人所有的。我們創建的數據是我們的遺產它們將比我們存活得更久——開源技術是確保我們對定義我們生活的數據擁有絕對控制權的唯一方式並且無限規模地延伸。",
"about_vision_title": "遠景",
"accept": "接受",
"accessed": "訪問的",
"account": "帳號",
"actions": "行動",
"add": "新增",
"add_device": "新增裝置",
"add_library": "新增圖書館",
"add_location_description": "通過將您喜歡的位置添加到您的個人圖書館可增強您的Spacedrive體驗實現無縫高效的文件管理。",
"add_location_tooltip": "添加路徑作為索引位置",
"add_locations": "添加位置",
"add_tag": "添加標籤",
"advanced_settings": "高級設置",
"all_jobs_have_been_cleared": "所有工作已被清除。",
"alpha_release_description": "我們很高興您嘗試Spacedrive現在以Alpha版發布展示激動人心的新功能。與任何初始版本一樣這個版本可能包含一些錯誤。我們誠懇請求您協助我們在Discord頻道報告您遇到的任何問題。您寶貴的反饋將極大地促進用戶體驗的提升。",
"alpha_release_title": "Alpha版本",
"appearance": "外觀",
"appearance_description": "改變您的客戶端外觀。",
"archive": "存檔",
"archive_coming_soon": "存檔位置功能即將開通...",
"archive_info": "將數據從圖書館中提取出來作為存檔,用途是保存位置文件夾結構。",
"are_you_sure": "您確定嗎?",
"assign_tag": "指定標籤",
"audio_preview_not_supported": "不支援音頻預覽。",
"back": "返回",
"backups": "備份",
"backups_description": "管理您的Spacedrive數據庫備份。",
"blur_effects": "模糊效果",
"blur_effects_description": "某些組件將應用模糊效果。",
"cancel": "取消",
"celcius": "攝氏",
"change": "更改",
"changelog": "變更日誌",
"changelog_page_description": "了解我們正在創建的酷炫新功能",
"changelog_page_title": "變更日誌",
"checksum": "校驗和",
"clear_finished_jobs": "清除已完成的工作",
"client": "客戶端",
"close": "關閉",
"clouds": "雲",
"color": "顏色",
"coming_soon": "即將推出",
"compress": "壓縮",
"configure_location": "配置位置",
"connected": "已連接",
"contacts": "聯繫人",
"contacts_description": "在Spacedrive中管理您的聯繫人。",
"content_id": "內容 ID",
"continue": "繼續",
"convert_to": "轉換為",
"coordinates": "坐標",
"copied": "已複製",
"copy": "複製",
"copy_as_path": "作為路徑複製",
"copy_path_to_clipboard": "複製路徑到剪貼簿",
"create": "創建",
"create_library": "創建圖書館",
"create_library_description": "圖書館是一個安全的、設備上的數據庫。您的文件保留在原地圖書館對其進行目錄整理並存儲所有Spacedrive相關數據。",
"create_new_library": "創建新圖書館",
"create_new_library_description": "圖書館是一個安全的、設備上的數據庫。您的文件保留在原地圖書館對其進行目錄整理並存儲所有Spacedrive相關數據。",
"create_new_tag": "創建新標籤",
"create_new_tag_description": "選擇一個名稱和顏色。",
"create_tag": "創建標籤",
"created": "已創建",
"creating_library": "正在創建圖書館...",
"creating_your_library": "正在創建您的圖書館",
"current": "當前",
"current_directory": "當前目錄",
"current_directory_with_descendants": "當前目錄及其子目錄",
"custom": "自定義",
"cut": "剪切",
"data_folder": "數據文件夾",
"debug_mode": "除錯模式",
"debug_mode_description": "在應用程序中啟用額外的除錯功能。",
"default": "默認",
"delete": "刪除",
"delete_dialog_title": "刪除 {{prefix}} {{type}}",
"delete_info": "這不會刪除磁盤上的實際文件夾。預覽媒體將被刪除。",
"delete_library": "刪除圖書館",
"delete_library_description": "這是永久性的您的文件不會被刪除只有Spacedrive圖書館會被刪除。",
"delete_location": "刪除位置",
"delete_location_description": "刪除一個位置還將從Spacedrive數據庫中移除與其相關的所有文件文件本身不會被刪除。",
"delete_rule": "刪除規則",
"delete_tag": "刪除標籤",
"delete_tag_description": "您確定要刪除這個標籤嗎?這不能撤銷,並且帶有標籤的文件將被取消鏈接。",
"delete_warning": "警告:這將永遠刪除您的{{type}},我們還沒有垃圾箱...",
"description": "描述",
"deselect": "取消選擇",
"details": "詳情",
"devices_coming_soon_tooltip": "即將推出這個alpha版本不包括圖書館同步它將很快準備好。",
"direction": "方向",
"disabled": "已禁用",
"display_formats": "顯示格式",
"display_name": "顯示名稱",
"distance": "距離",
"done": "完成",
"dont_show_again": "不再顯示",
"double_click_action": "雙擊操作",
"download": "下載",
"duplicate": "複製",
"edit": "編輯",
"edit_library": "編輯圖書館",
"edit_location": "編輯位置",
"enable_networking": "啟用網絡",
"encrypt": "加密",
"encrypt_library": "加密圖書館",
"encrypt_library_coming_soon": "圖書館加密即將推出",
"encrypt_library_description": "為這個圖書館啟用加密這將僅加密Spacedrive數據庫而不是文件本身。",
"ephemeral_notice_browse": "直接從您的設備瀏覽您的文件和文件夾。",
"ephemeral_notice_consider_indexing": "考慮索引您的本地位置,以實現更快更高效的探索。",
"erase": "擦除",
"erase_a_file": "擦除文件",
"erase_a_file_description": "配置您的擦除設置。",
"error": "錯誤",
"error_loading_original_file": "加載原始文件出錯",
"expand": "展開",
"explorer": "資源管理器",
"export": "導出",
"export_library": "導出圖書館",
"export_library_coming_soon": "導出圖書館即將推出",
"export_library_description": "將此圖書館導出到文件。",
"extensions": "擴展",
"extensions_description": "安裝擴展以擴展此客戶端的功能。",
"fahrenheit": "華氏",
"failed_to_cancel_job": "取消工作失敗。",
"failed_to_clear_all_jobs": "清除所有工作失敗。",
"failed_to_copy_file_path": "複製文件路徑失敗",
"failed_to_duplicate_file": "複製文件失敗",
"failed_to_generate_checksum": "生成校驗和失敗",
"failed_to_generate_labels": "生成標籤失敗",
"failed_to_generate_thumbnails": "生成縮略圖失敗",
"failed_to_load_tags": "加載標籤失敗",
"failed_to_pause_job": "暫停工作失敗。",
"failed_to_reindex_location": "重新索引位置失敗",
"failed_to_remove_file_from_recents": "從最近文件中移除文件失敗",
"failed_to_remove_job": "移除工作失敗。",
"failed_to_resume_job": "恢復工作失敗。",
"failed_to_update_location_settings": "更新位置設置失敗",
"favorite": "最愛",
"file_indexing_rules": "文件索引規則",
"filters": "篩選器",
"forward": "前進",
"full_disk_access": "完全磁碟訪問",
"full_disk_access_description": "為了提供最佳體驗,我們需要訪問您的磁碟以索引您的文件。您的文件僅供您使用。",
"full_reindex": "完全重新索引",
"full_reindex_info": "對此位置進行完整重新掃描。",
"general": "通用",
"general_settings": "通用設置",
"general_settings_description": "與此客戶端相關的一般設置。",
"generatePreviewMedia_label": "為這個位置生成預覽媒體",
"generate_checksums": "生成校驗和",
"go_back": "返回",
"got_it": "知道了",
"grid_gap": "間隙",
"grid_view": "網格視圖",
"grid_view_notice_description": "使用網格視圖來視覺化你的文件概覽。這個視圖以縮略圖形式顯示你的文件和文件夾,讓你快速辨認你正在尋找的文件。",
"hidden_label": "隱藏位置和其內容從概要分類、搜索和標籤中顯示,除非啟用了“顯示隱藏項目”。",
"hide_in_library_search": "在圖書館搜索中隱藏",
"hide_in_library_search_description": "在搜索整個圖書館時從結果中隱藏帶有此標籤的文件。",
"hide_in_sidebar": "在側邊欄中隱藏",
"hide_in_sidebar_description": "防止此標籤在應用的側欄中顯示。",
"hide_location_from_view": "從視圖中隱藏位置和內容",
"image_labeler_ai_model": "圖像標籤識別AI模型",
"image_labeler_ai_model_description": "用於識別圖像中對象的模型。模型越大,準確性越高,但速度越慢。",
"import": "導入",
"indexed": "已索引",
"indexer_rule_reject_allow_label": "默認情況下,索引器規則作為拒絕列表,導致與其匹配的任何文件被排除在外。啟用此選項將其變為允許列表,使位置僅索引符合其規定規則的文件。",
"indexer_rules": "索引器規則",
"indexer_rules_info": "索引器規則允許您使用通配符指定要忽略的路徑。",
"install": "安裝",
"install_update": "安裝更新",
"installed": "已安裝",
"item_size": "項目大小",
"job_has_been_canceled": "工作已取消。",
"job_has_been_paused": "工作已暫停。",
"job_has_been_removed": "工作已移除。",
"job_has_been_resumed": "工作已恢復。",
"join": "加入",
"join_discord": "加入Discord",
"join_library": "加入圖書館",
"join_library_description": "圖書館是一個安全的、設備上的數據庫。您的文件保留在原地圖書館對其進行目錄整理並存儲所有Spacedrive相關數據。",
"key_manager": "密鑰管理器",
"key_manager_description": "創建加密密鑰,掛載和卸載您的密鑰,即時查看解密後的文件。",
"keybinds": "鍵綁定",
"keybinds_description": "查看和管理客戶端鍵綁定",
"keys": "鍵",
"kilometers": "公里",
"learn_more_about_telemetry": "了解更多關於遙測的信息",
"libraries": "圖書館",
"libraries_description": "數據庫包含所有圖書館數據和文件元數據。",
"library": "圖書館",
"library_name": "圖書館名稱",
"library_settings": "圖書館設置",
"library_settings_description": "與當前活躍圖書館相關的通用設置。",
"list_view": "列表視圖",
"list_view_notice_description": "使用列表視圖輕鬆導航您的文件和文件夾。這個視圖以簡單、有組織的列表格式顯示您的文件,幫助您快速定位和訪問所需文件。",
"loading": "加載中",
"local_locations": "本地位置",
"local_node": "本地節點",
"location_display_name_info": "這個位置的名稱,這是在側邊欄中顯示的內容。不會重命名磁碟上的實際文件夾。",
"location_is_already_linked": "位置已經被連接",
"location_offline_tooltip": "位置離線,您可以繼續瀏覽和組織。",
"location_path_info": "這是位置的路徑,這是在磁碟上存儲文件的位置。",
"location_type": "位置類型",
"location_type_managed": "Spacedrive將為你分類文件。如果位置不是空的將創建一個“spacedrive”文件夾。",
"location_type_normal": "內容將按原樣被索引,新文件不會自動排序。",
"location_type_replica": "這個位置是另一個位置的副本,其內容將自動同步。",
"locations": "位置",
"locations_description": "管理您的存儲位置。",
"lock": "鎖定",
"log_in_with_browser": "使用瀏覽器登入",
"log_out": "登出",
"manage_library": "管理圖書館",
"managed": "已管理",
"media": "媒體",
"media_view_context": "媒體視圖上下文",
"media_view_notice_description": "輕鬆發現照片和視頻,媒體視圖會從當前位置(包括子目錄)顯示結果。",
"meet_contributors_behind_spacedrive": "認識Spacedrive背後的貢獻者",
"miles": "英里",
"mode": "模式",
"modified": "已修改",
"more": "更多",
"more_actions": "更多操作...",
"more_info": "更多信息",
"move_files": "移動文件",
"name": "名稱",
"navigate_back": "後退",
"navigate_forward": "前進",
"network_page_description": "您局域網上的其他Spacedrive節點將顯示在這裡以及您預設的OS網絡掛載。",
"networking": "網絡",
"networking_port": "網絡端口",
"networking_port_description": "Spacedrive的點對點網絡通信使用的端口。除非您有嚴格的防火牆否則應該禁用此功能。不要暴露於互聯網",
"new_folder": "新文件夾",
"new_library": "新圖書館",
"new_location": "新位置",
"new_location_web_description": "由於您正在使用Spacedrive的瀏覽器版本您將目前需要指定一個位於遠端節點本地的目錄的絕對URL。",
"new_tab": "新標籤",
"new_tag": "新標籤",
"no_files_found_here": "這裡未找到文件",
"no_jobs": "沒有工作。",
"no_tag_selected": "沒有選擇標籤",
"no_tags": "沒有標籤",
"node_name": "節點名稱",
"nodes": "節點",
"nodes_description": "管理連接到此圖書館的節點。一個節點是在設備或服務器上運行的Spacedrive後端實例。每個節點帶有數據庫副本通過點對點連接實時同步。",
"none": "無",
"normal": "常規",
"not_you": "不是您?",
"number_of_passes": "通過次數",
"object_id": "對象ID",
"offline": "離線",
"online": "在線",
"open": "打開",
"open_file": "打開文件",
"open_new_location_once_added": "添加後打開新位置",
"open_settings": "打開設置",
"open_with": "打開方式",
"or": "或者",
"pair": "配對",
"pairing_with_node": "正在與{{node}}配對",
"paste": "貼上",
"path": "路徑",
"path_copied_to_clipboard_description": "位置{{location}}的路徑已復製到剪貼簿。",
"path_copied_to_clipboard_title": "路徑已複製到剪貼簿",
"pause": "暫停",
"peers": "對等",
"people": "人們",
"privacy": "隱私",
"privacy_description": "Spacedrive是為隱私而構建的這就是為什麼我們是開源的並且首先在本地。所以我們將非常清楚地告知我們分享了哪些數據。",
"quick_preview": "快速預覽",
"quick_view": "快速查看",
"recent_jobs": "最近的工作",
"regen_labels": "重新生成標籤",
"regen_thumbnails": "重新生成縮略圖",
"regenerate_thumbs": "重新生成縮略圖",
"reindex": "重新索引",
"reject": "拒絕",
"reload": "重載",
"remove": "移除",
"remove_from_recents": "從最近的文件中移除",
"rename": "重命名",
"replica": "複寫",
"rescan_directory": "重新掃描目錄",
"rescan_location": "重新掃描位置",
"reset": "重置",
"resources": "資源",
"restore": "恢復",
"resume": "恢復",
"retry": "重試",
"revel_in_browser": "在{{browser}}中顯示",
"running": "運行",
"save": "保存",
"save_changes": "保存變更",
"search_extensions": "搜索擴展",
"secure_delete": "安全刪除",
"security": "安全",
"security_description": "保護您的客戶端安全。",
"send": "發送",
"settings": "設置",
"setup": "設定",
"share": "分享",
"sharing": "共享",
"sharing_description": "管理誰可以訪問您的圖書館。",
"show_details": "顯示詳情",
"show_hidden_files": "顯示隱藏文件",
"show_object_size": "顯示對象大小",
"show_path_bar": "顯示路徑條",
"show_slider": "顯示滑塊",
"size": "大小",
"skip_login": "跳過登錄",
"sort_by": "排序依據",
"spacedrop_a_file": "Spacedrop文件",
"square_thumbnails": "方形縮略圖",
"star_on_github": "在GitHub上給星",
"stop": "停止",
"success": "成功",
"support": "支持",
"sync": "同步",
"syncPreviewMedia_label": "將此位置的預覽媒體與您的設備同步",
"sync_description": "管理Spacedrive如何進行同步。",
"sync_with_library": "與圖書館同步",
"sync_with_library_description": "如果啟用,您的鍵綁定將與圖書館同步,否則它們僅適用於此客戶端。",
"tags": "標籤",
"tags_description": "管理您的標籤。",
"telemetry_description": "切換到ON為開發者提供詳細的使用情況和遙測數據以增強應用程序。切換到OFF只發送基本數據您的活動狀態應用版本核心版本以及平台例如移動網絡或桌面。",
"telemetry_title": "分享額外的遙測和使用情況數據",
"temperature": "溫度",
"thumbnailer_cpu_usage": "縮略圖處理器使用情況",
"thumbnailer_cpu_usage_description": "限制縮略圖製作工具在後台處理時可以使用多少CPU。",
"toggle_all": "切換全部",
"type": "類型",
"ui_animations": "UI動畫",
"ui_animations_description": "對話框和其它UI元素在打開和關閉時會有動畫效果。",
"usage": "使用情況",
"usage_description": "您的圖書館使用情況和硬體資訊。",
"value": "值",
"video_preview_not_supported": "不支援視頻預覽。",
"want_to_do_this_later": "想要稍後進行這操作嗎?",
"website": "網站",
"your_account": "您的帳戶",
"your_account_description": "Spacedrive帳戶和資訊。",
"your_local_network": "您的本地網路",
"your_privacy": "您的隱私"
}

Some files were not shown because too many files have changed in this diff Show more