Merge remote-tracking branch 'origin/main' into auth-linux-fixes
This commit is contained in:
@@ -16,8 +16,8 @@ SetupIconFile={{SETUP_ICON_FILE}}
|
||||
WizardStyle=modern
|
||||
;PrivilegesRequired={{PRIVILEGES_REQUIRED}}
|
||||
PrivilegesRequiredOverridesAllowed=dialog
|
||||
ArchitecturesAllowed=x64
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
ArchitecturesAllowed=x64compatible
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
UninstallDisplayIcon={app}\auth.exe
|
||||
|
||||
[Languages]
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v1.7.5 (Unreleased)
|
||||
## v1.7.6 (Unreleased)
|
||||
|
||||
- Directly upload to selected album on drag and drop.
|
||||
- Include shared files in export.
|
||||
- .
|
||||
|
||||
## v1.7.5
|
||||
|
||||
- Face grouping (beta).
|
||||
- Include shared files in export.
|
||||
- Directly upload to selected album on drag and drop.
|
||||
- Improve heuristics for clubbing a photo and video into a live photo.
|
||||
|
||||
## v1.7.4
|
||||
|
||||
- Improved date search, including support for day of week and hour of day.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ente",
|
||||
"version": "1.7.5-beta",
|
||||
"version": "1.7.6-beta",
|
||||
"private": true,
|
||||
"description": "Desktop client for Ente Photos",
|
||||
"repository": "github:ente-io/photos-desktop",
|
||||
|
||||
@@ -7,45 +7,41 @@ description:
|
||||
|
||||
# Machine learning
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> This document describes a beta feature that will be present in an upcoming
|
||||
> release.
|
||||
|
||||
Ente supports on-device machine learning. This allows you to use the latest
|
||||
advances in AI in a privacy preserving manner.
|
||||
|
||||
- You can search for your photos by the **faces** of the people in them. Ente
|
||||
- You can search for your photos by the **Faces** of the people in them. Ente
|
||||
will show you all the faces in a photo, and will also try to group similar
|
||||
faces together to create clusters of people so that you can give them names,
|
||||
and quickly find all photos with a given person in them.
|
||||
|
||||
- You can search for your photos by typing natural language descriptions of
|
||||
them. For example, you can search for "night", "by the seaside", or "the red
|
||||
motorcycle next to a fountain". Within the app, this ability is sometimes
|
||||
referred to as **magic search**.
|
||||
motorcycle next to a fountain". Within the app, this ability is referred to
|
||||
as **Magic search**.
|
||||
|
||||
- We will build on this foundation to add more forms of advanced search.
|
||||
You can enable face recognition and magic search in the app's preferences on
|
||||
either the mobile app or the desktop app.
|
||||
|
||||
You can enable face and magic search in the app's preferences on either the
|
||||
mobile app or the desktop app.
|
||||
On mobile, this is available under `General > Advanced > Machine learning`.
|
||||
|
||||
If you have a big library, we recommend enabling this on the desktop app first,
|
||||
because it can index your existing photos faster (The app needs to download your
|
||||
originals to index them which can happen faster over WiFi, and indexing is also
|
||||
faster on your computer as compared to your mobile device).
|
||||
On desktop, this is available under `Preferences > Machine learning`.
|
||||
|
||||
Once your existing photos have been indexed, then you can use either. The mobile
|
||||
app is fast enough to easily and seamlessly index the new photos that you take.
|
||||
---
|
||||
|
||||
The app needs to download your original photos to index them. This is faster
|
||||
over WiFi. Indexing is also faster on your computer as compared to your mobile
|
||||
device.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Even for the initial indexing, you don't necessarily need the desktop app, it
|
||||
> just will be a bit faster.
|
||||
> If you have a large library on Ente, we recommend enabling this feature on the
|
||||
> desktop app first, because it can index your existing photos faster. Once your
|
||||
> existing photos have been indexed, then you can use either. The mobile app is
|
||||
> fast enough to index new photos as they are being backed up.
|
||||
|
||||
The indexes are synced across all your devices automatically using the same
|
||||
end-to-end encypted security that we use for syncing your photos.
|
||||
end-to-end encrypted security that we use for syncing your photos.
|
||||
|
||||
Note that the desktop app does not currently support viewing and modifying the
|
||||
automatically generated face groupings, that is only supported by the mobile
|
||||
app.
|
||||
Note that the desktop app does not currently support modifying the face
|
||||
groupings, that is only supported by the mobile app.
|
||||
|
||||
@@ -133,7 +133,7 @@ class MLService {
|
||||
}
|
||||
if (_mlControllerStatus == true) {
|
||||
// refresh discover section
|
||||
MagicCacheService.instance.updateCache().ignore();
|
||||
MagicCacheService.instance.updateCache(forced: force).ignore();
|
||||
}
|
||||
await indexAllImages();
|
||||
if ((await MLDataDB.instance.getUnclusteredFaceCount()) > 0) {
|
||||
|
||||
@@ -205,7 +205,7 @@ class MagicCacheService {
|
||||
queueUpdate("Prompts data updated");
|
||||
} else if (lastMagicCacheUpdateTime <
|
||||
DateTime.now()
|
||||
.subtract(const Duration(days: 1))
|
||||
.subtract(const Duration(hours: 12))
|
||||
.millisecondsSinceEpoch) {
|
||||
queueUpdate("Cache is old");
|
||||
}
|
||||
|
||||
@@ -342,7 +342,8 @@ Future<MediaUploadData> _getMediaUploadDataFromAppCache(EnteFile file) async {
|
||||
Map<String, int>? dimensions;
|
||||
if (file.fileType == FileType.image) {
|
||||
dimensions = await getImageHeightAndWith(imagePath: localPath);
|
||||
} else {
|
||||
} else if (thumbnailData != null) {
|
||||
// the thumbnail null check is to ensure that we are able to generate thum
|
||||
// for video, we need to use the thumbnail data with any max width/height
|
||||
final thumbnailFilePath = await VideoThumbnail.thumbnailFile(
|
||||
video: localPath,
|
||||
@@ -406,14 +407,19 @@ Future<Uint8List?> getThumbnailFromInAppCacheFile(EnteFile file) async {
|
||||
return null;
|
||||
}
|
||||
if (file.fileType == FileType.video) {
|
||||
final thumbnailFilePath = await VideoThumbnail.thumbnailFile(
|
||||
video: localFile.path,
|
||||
imageFormat: ImageFormat.JPEG,
|
||||
thumbnailPath: (await getTemporaryDirectory()).path,
|
||||
maxWidth: thumbnailLargeSize,
|
||||
quality: 80,
|
||||
);
|
||||
localFile = File(thumbnailFilePath!);
|
||||
try {
|
||||
final thumbnailFilePath = await VideoThumbnail.thumbnailFile(
|
||||
video: localFile.path,
|
||||
imageFormat: ImageFormat.JPEG,
|
||||
thumbnailPath: (await getTemporaryDirectory()).path,
|
||||
maxWidth: thumbnailLargeSize,
|
||||
quality: 80,
|
||||
);
|
||||
localFile = File(thumbnailFilePath!);
|
||||
} catch (e) {
|
||||
_logger.warning('Failed to generate video thumbnail', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
var thumbnailData = await localFile.readAsBytes();
|
||||
int compressionAttempts = 0;
|
||||
|
||||
@@ -158,7 +158,7 @@ const AuthNavbar: React.FC = () => {
|
||||
startIcon={<LogoutOutlined />}
|
||||
onClick={logout}
|
||||
>
|
||||
{t("LOGOUT")}
|
||||
{t("logout")}
|
||||
</OverflowMenuOption>
|
||||
</OverflowMenu>
|
||||
</HorizontalFlex>
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
type CollectionsSortBy,
|
||||
type CollectionSummaries,
|
||||
} from "@/new/photos/types/collection";
|
||||
import { ensure } from "@/utils/ensure";
|
||||
import { includes } from "@/utils/type-guards";
|
||||
import {
|
||||
getData,
|
||||
@@ -87,6 +86,7 @@ type CollectionsProps = Omit<
|
||||
*/
|
||||
export const GalleryBarAndListHeader: React.FC<CollectionsProps> = ({
|
||||
shouldHide,
|
||||
showPeopleSectionButton,
|
||||
mode,
|
||||
onChangeMode,
|
||||
collectionSummaries,
|
||||
@@ -95,7 +95,7 @@ export const GalleryBarAndListHeader: React.FC<CollectionsProps> = ({
|
||||
setActiveCollectionID,
|
||||
hiddenCollectionSummaries,
|
||||
people,
|
||||
activePersonID,
|
||||
activePerson,
|
||||
onSelectPerson,
|
||||
setCollectionNamerAttributes,
|
||||
setPhotoListHeader,
|
||||
@@ -171,14 +171,13 @@ export const GalleryBarAndListHeader: React.FC<CollectionsProps> = ({
|
||||
}
|
||||
onCollectionCast={() => setOpenAlbumCastDialog(true)}
|
||||
/>
|
||||
) : (
|
||||
) : activePerson ? (
|
||||
<PeopleHeader
|
||||
person={ensure(
|
||||
people.find((p) => p.id == activePersonID) ??
|
||||
people[0],
|
||||
)}
|
||||
person={activePerson}
|
||||
{...{ onSelectPerson, appContext }}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
itemType: ITEM_TYPE.HEADER,
|
||||
height: 68,
|
||||
@@ -189,8 +188,7 @@ export const GalleryBarAndListHeader: React.FC<CollectionsProps> = ({
|
||||
toShowCollectionSummaries,
|
||||
activeCollectionID,
|
||||
isActiveCollectionDownloadInProgress,
|
||||
people,
|
||||
activePersonID,
|
||||
activePerson,
|
||||
]);
|
||||
|
||||
if (shouldBeHidden) {
|
||||
@@ -201,11 +199,12 @@ export const GalleryBarAndListHeader: React.FC<CollectionsProps> = ({
|
||||
<>
|
||||
<GalleryBarImpl
|
||||
{...{
|
||||
showPeopleSectionButton,
|
||||
mode,
|
||||
onChangeMode,
|
||||
activeCollectionID,
|
||||
people,
|
||||
activePersonID,
|
||||
activePerson,
|
||||
onSelectPerson,
|
||||
collectionsSortBy,
|
||||
}}
|
||||
|
||||
@@ -66,7 +66,7 @@ export default function ExportFinished(props: Props) {
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button color="secondary" size="large" onClick={props.onHide}>
|
||||
{t("CLOSE")}
|
||||
{t("close")}
|
||||
</Button>
|
||||
<Button size="large" color="primary" onClick={props.onResync}>
|
||||
{t("EXPORT_AGAIN")}
|
||||
|
||||
@@ -91,7 +91,7 @@ export default function ExportInProgress(props: Props) {
|
||||
size="large"
|
||||
onClick={props.closeExportDialog}
|
||||
>
|
||||
{t("CLOSE")}
|
||||
{t("close")}
|
||||
</Button>
|
||||
<Button
|
||||
size="large"
|
||||
|
||||
@@ -65,7 +65,7 @@ const ExportPendingList = (props: Iprops) => {
|
||||
title: t("PENDING_ITEMS"),
|
||||
close: {
|
||||
action: props.onClose,
|
||||
text: t("CLOSE"),
|
||||
text: t("close"),
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -82,7 +82,7 @@ export const FilesDownloadProgress: React.FC<FilesDownloadProgressProps> = ({
|
||||
},
|
||||
},
|
||||
close: {
|
||||
text: t("NO"),
|
||||
text: t("no"),
|
||||
variant: "secondary",
|
||||
action: () => {},
|
||||
},
|
||||
|
||||
@@ -252,7 +252,7 @@ const Footer = ({ step, startFix, ...props }) => {
|
||||
)}
|
||||
{step == "completed" && (
|
||||
<Button color="primary" size="large" onClick={props.hide}>
|
||||
{t("CLOSE")}
|
||||
{t("close")}
|
||||
</Button>
|
||||
)}
|
||||
{(!step || step == "completed-with-errors") && (
|
||||
|
||||
@@ -8,7 +8,7 @@ import { PHOTOS_PAGES } from "@ente/shared/constants/pages";
|
||||
import { CustomError } from "@ente/shared/error";
|
||||
import useMemoSingleThreaded from "@ente/shared/hooks/useMemoSingleThreaded";
|
||||
import { styled } from "@mui/material";
|
||||
import PhotoViewer from "components/PhotoViewer";
|
||||
import PhotoViewer, { type PhotoViewerProps } from "components/PhotoViewer";
|
||||
import { useRouter } from "next/router";
|
||||
import { GalleryContext } from "pages/gallery";
|
||||
import PhotoSwipe from "photoswipe";
|
||||
@@ -72,6 +72,7 @@ interface Props {
|
||||
isInHiddenSection?: boolean;
|
||||
setFilesDownloadProgressAttributesCreator?: SetFilesDownloadProgressAttributesCreator;
|
||||
selectable?: boolean;
|
||||
onSelectPerson?: PhotoViewerProps["onSelectPerson"];
|
||||
}
|
||||
|
||||
const PhotoFrame = ({
|
||||
@@ -95,6 +96,7 @@ const PhotoFrame = ({
|
||||
isInHiddenSection,
|
||||
setFilesDownloadProgressAttributesCreator,
|
||||
selectable,
|
||||
onSelectPerson,
|
||||
}: Props) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
||||
@@ -580,6 +582,7 @@ const PhotoFrame = ({
|
||||
setFilesDownloadProgressAttributesCreator={
|
||||
setFilesDownloadProgressAttributesCreator
|
||||
}
|
||||
onSelectPerson={onSelectPerson}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
|
||||
@@ -510,7 +510,7 @@ export function PhotoList({
|
||||
itemType: ITEM_TYPE.OTHER,
|
||||
item: (
|
||||
<NothingContainer span={columns}>
|
||||
<div>{t("NOTHING_HERE")}</div>
|
||||
<div>{t("nothing_here")}</div>
|
||||
</NothingContainer>
|
||||
),
|
||||
id: "empty-list-banner",
|
||||
|
||||
@@ -12,11 +12,19 @@ import {
|
||||
type ParsedMetadataDate,
|
||||
} from "@/media/file-metadata";
|
||||
import { FileType } from "@/media/file-type";
|
||||
import { UnidentifiedFaces } from "@/new/photos/components/PeopleList";
|
||||
import {
|
||||
AnnotatedFacePeopleList,
|
||||
UnclusteredFaceList,
|
||||
} from "@/new/photos/components/PeopleList";
|
||||
import { PhotoDateTimePicker } from "@/new/photos/components/PhotoDateTimePicker";
|
||||
import { photoSwipeZIndex } from "@/new/photos/components/PhotoViewer";
|
||||
import { tagNumericValue, type RawExifTags } from "@/new/photos/services/exif";
|
||||
import { isMLEnabled } from "@/new/photos/services/ml";
|
||||
import {
|
||||
AnnotatedFacesForFile,
|
||||
getAnnotatedFacesForFile,
|
||||
isMLEnabled,
|
||||
type AnnotatedFaceID,
|
||||
} from "@/new/photos/services/ml";
|
||||
import { EnteFile } from "@/new/photos/types/file";
|
||||
import { formattedByteSize } from "@/new/photos/utils/units";
|
||||
import CopyButton from "@ente/shared/components/CodeBlock/CopyButton";
|
||||
@@ -61,7 +69,7 @@ export interface FileInfoExif {
|
||||
parsed: ParsedMetadata | undefined;
|
||||
}
|
||||
|
||||
interface FileInfoProps {
|
||||
export interface FileInfoProps {
|
||||
showInfo: boolean;
|
||||
handleCloseInfo: () => void;
|
||||
closePhotoViewer: () => void;
|
||||
@@ -73,6 +81,10 @@ interface FileInfoProps {
|
||||
fileToCollectionsMap?: Map<number, number[]>;
|
||||
collectionNameMap?: Map<number, string>;
|
||||
showCollectionChips: boolean;
|
||||
/**
|
||||
* Called when the user selects a person in the file info panel.
|
||||
*/
|
||||
onSelectPerson?: ((personID: string) => void) | undefined;
|
||||
}
|
||||
|
||||
export const FileInfo: React.FC<FileInfoProps> = ({
|
||||
@@ -87,6 +99,7 @@ export const FileInfo: React.FC<FileInfoProps> = ({
|
||||
collectionNameMap,
|
||||
showCollectionChips,
|
||||
closePhotoViewer,
|
||||
onSelectPerson,
|
||||
}) => {
|
||||
const { mapEnabled, updateMapEnabled, setDialogBoxAttributesV2 } =
|
||||
useContext(AppContext);
|
||||
@@ -97,6 +110,9 @@ export const FileInfo: React.FC<FileInfoProps> = ({
|
||||
|
||||
const [exifInfo, setExifInfo] = useState<ExifInfo | undefined>();
|
||||
const [openRawExif, setOpenRawExif] = useState(false);
|
||||
const [annotatedFaces, setAnnotatedFaces] = useState<
|
||||
AnnotatedFacesForFile | undefined
|
||||
>();
|
||||
|
||||
const location = useMemo(() => {
|
||||
if (file) {
|
||||
@@ -106,6 +122,21 @@ export const FileInfo: React.FC<FileInfoProps> = ({
|
||||
return exif?.parsed?.location;
|
||||
}, [file, exif]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!file) return;
|
||||
|
||||
let didCancel = false;
|
||||
|
||||
void (async () => {
|
||||
const result = await getAnnotatedFacesForFile(file);
|
||||
!didCancel && setAnnotatedFaces(result);
|
||||
})();
|
||||
|
||||
return () => {
|
||||
didCancel = true;
|
||||
};
|
||||
}, [file]);
|
||||
|
||||
useEffect(() => {
|
||||
setExifInfo(parseExifInfo(exif));
|
||||
}, [exif]);
|
||||
@@ -129,6 +160,13 @@ export const FileInfo: React.FC<FileInfoProps> = ({
|
||||
getMapDisableConfirmationDialog(() => updateMapEnabled(false)),
|
||||
);
|
||||
|
||||
const handleSelectFace = (annotatedFaceID: AnnotatedFaceID) => {
|
||||
if (onSelectPerson) {
|
||||
onSelectPerson(annotatedFaceID.personID);
|
||||
closePhotoViewer();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<FileInfoSidebar open={showInfo} onClose={handleCloseInfo}>
|
||||
<Titlebar onClose={handleCloseInfo} title={t("INFO")} backIsClose />
|
||||
@@ -267,10 +305,17 @@ export const FileInfo: React.FC<FileInfoProps> = ({
|
||||
</InfoItem>
|
||||
)}
|
||||
|
||||
{isMLEnabled() && (
|
||||
{isMLEnabled() && annotatedFaces && (
|
||||
<>
|
||||
{/* TODO-Cluster <PhotoPeopleList file={file} /> */}
|
||||
<UnidentifiedFaces enteFile={file} />
|
||||
<AnnotatedFacePeopleList
|
||||
enteFile={file}
|
||||
annotatedFaceIDs={annotatedFaces.annotatedFaceIDs}
|
||||
onSelectFace={handleSelectFace}
|
||||
/>
|
||||
<UnclusteredFaceList
|
||||
enteFile={file}
|
||||
faceIDs={annotatedFaces.otherFaceIDs}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -658,7 +658,7 @@ const ImageEditorOverlay = (props: IProps) => {
|
||||
/>
|
||||
</Tabs>
|
||||
</HorizontalFlex>
|
||||
<MenuSectionTitle title={t("RESET")} />
|
||||
<MenuSectionTitle title={t("reset")} />
|
||||
<MenuItemGroup
|
||||
style={{
|
||||
marginBottom: "0.5rem",
|
||||
|
||||
@@ -48,7 +48,7 @@ import { SetFilesDownloadProgressAttributesCreator } from "types/gallery";
|
||||
import { pauseVideo, playVideo } from "utils/photoFrame";
|
||||
import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery";
|
||||
import { getTrashFileMessage } from "utils/ui";
|
||||
import { FileInfo, type FileInfoExif } from "./FileInfo";
|
||||
import { FileInfo, type FileInfoExif, type FileInfoProps } from "./FileInfo";
|
||||
import ImageEditorOverlay from "./ImageEditorOverlay";
|
||||
import CircularProgressWithLabel from "./styledComponents/CircularProgressWithLabel";
|
||||
import { ConversionFailedNotification } from "./styledComponents/ConversionFailedNotification";
|
||||
@@ -98,7 +98,8 @@ const CaptionContainer = styled("div")(({ theme }) => ({
|
||||
backgroundColor: theme.colors.backdrop.faint,
|
||||
backdropFilter: `blur(${theme.colors.blur.base})`,
|
||||
}));
|
||||
interface Iprops {
|
||||
|
||||
export interface PhotoViewerProps {
|
||||
isOpen: boolean;
|
||||
items: any[];
|
||||
currentIndex?: number;
|
||||
@@ -115,9 +116,10 @@ interface Iprops {
|
||||
fileToCollectionsMap: Map<number, number[]>;
|
||||
collectionNameMap: Map<number, string>;
|
||||
setFilesDownloadProgressAttributesCreator: SetFilesDownloadProgressAttributesCreator;
|
||||
onSelectPerson?: FileInfoProps["onSelectPerson"];
|
||||
}
|
||||
|
||||
function PhotoViewer(props: Iprops) {
|
||||
function PhotoViewer(props: PhotoViewerProps) {
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const appContext = useContext(AppContext);
|
||||
const publicCollectionGalleryContext = useContext(
|
||||
@@ -812,7 +814,7 @@ function PhotoViewer(props: Iprops) {
|
||||
|
||||
<button
|
||||
className="pswp__button pswp__button--custom"
|
||||
title={t("CLOSE_OPTION")}
|
||||
title={t("close_key")}
|
||||
onClick={handleClose}
|
||||
>
|
||||
<CloseIcon />
|
||||
@@ -969,6 +971,7 @@ function PhotoViewer(props: Iprops) {
|
||||
refreshPhotoswipe={refreshPhotoswipe}
|
||||
fileToCollectionsMap={props.fileToCollectionsMap}
|
||||
collectionNameMap={props.collectionNameMap}
|
||||
onSelectPerson={props.onSelectPerson}
|
||||
/>
|
||||
<ImageEditorOverlay
|
||||
show={showImageEditorOverlay}
|
||||
|
||||
@@ -506,7 +506,7 @@ const UtilitySection: React.FC<UtilitySectionProps> = ({ closeSidebar }) => {
|
||||
<EnteMenuItem
|
||||
variant="secondary"
|
||||
onClick={openRecoveryKeyModal}
|
||||
label={t("RECOVERY_KEY")}
|
||||
label={t("recovery_key")}
|
||||
/>
|
||||
{isInternalUserViaEmailCheck() && (
|
||||
<EnteMenuItem
|
||||
@@ -600,13 +600,13 @@ const HelpSection: React.FC = () => {
|
||||
<>
|
||||
<EnteMenuItem
|
||||
onClick={requestFeature}
|
||||
label={t("REQUEST_FEATURE")}
|
||||
label={t("request_feature")}
|
||||
variant="secondary"
|
||||
/>
|
||||
<EnteMenuItem
|
||||
onClick={contactSupport}
|
||||
labelComponent={
|
||||
<span title="support@ente.io">{t("SUPPORT")}</span>
|
||||
<span title="support@ente.io">{t("support")}</span>
|
||||
}
|
||||
variant="secondary"
|
||||
/>
|
||||
@@ -634,9 +634,9 @@ const ExitSection: React.FC = () => {
|
||||
|
||||
const confirmLogout = () => {
|
||||
setDialogMessage({
|
||||
title: t("LOGOUT_MESSAGE"),
|
||||
title: t("logout_message"),
|
||||
proceed: {
|
||||
text: t("LOGOUT"),
|
||||
text: t("logout"),
|
||||
action: logout,
|
||||
variant: "critical",
|
||||
},
|
||||
@@ -649,7 +649,7 @@ const ExitSection: React.FC = () => {
|
||||
<EnteMenuItem
|
||||
onClick={confirmLogout}
|
||||
color="critical"
|
||||
label={t("LOGOUT")}
|
||||
label={t("logout")}
|
||||
variant="secondary"
|
||||
/>
|
||||
<EnteMenuItem
|
||||
|
||||
@@ -22,7 +22,7 @@ export function UploadProgressFooter() {
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant="contained" fullWidth onClick={onClose}>
|
||||
{t("CLOSE")}
|
||||
{t("close")}
|
||||
</Button>
|
||||
))}
|
||||
</DialogActions>
|
||||
|
||||
@@ -57,7 +57,7 @@ export default function UploadProgress({
|
||||
action: props.cancelUploads,
|
||||
},
|
||||
close: {
|
||||
text: t("NO"),
|
||||
text: t("no"),
|
||||
variant: "secondary",
|
||||
action: () => {},
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function UserNameInputDialog({
|
||||
<DialogBox maxWidth="xs" open={open} onClose={onClose}>
|
||||
<DialogIcon icon={<AutoAwesomeOutlinedIcon />} />
|
||||
|
||||
<DialogTitle>{t("ENTER_NAME")}</DialogTitle>
|
||||
<DialogTitle>{t("enter_name")}</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
<Typography color={"text.muted"} pb={1}>
|
||||
|
||||
@@ -162,7 +162,7 @@ function PlanSelectorCard(props: PlanSelectorCardProps) {
|
||||
appContext.setDialogMessage({
|
||||
title: t("OPEN_PLAN_SELECTOR_MODAL_FAILED"),
|
||||
content: t("UNKNOWN_ERROR"),
|
||||
close: { text: t("CLOSE"), variant: "secondary" },
|
||||
close: { text: t("close"), variant: "secondary" },
|
||||
proceed: {
|
||||
text: t("REOPEN_PLAN_SELECTOR_MODAL"),
|
||||
variant: "accent",
|
||||
|
||||
@@ -191,7 +191,7 @@ const SelectedFileOptions = ({
|
||||
<DownloadIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("ADD")}>
|
||||
<Tooltip title={t("add")}>
|
||||
<IconButton onClick={addToCollection}>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
@@ -225,7 +225,7 @@ const SelectedFileOptions = ({
|
||||
<DownloadIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("ADD")}>
|
||||
<Tooltip title={t("add")}>
|
||||
<IconButton onClick={addToCollection}>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
@@ -328,7 +328,7 @@ const SelectedFileOptions = ({
|
||||
<DownloadIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("ADD")}>
|
||||
<Tooltip title={t("add")}>
|
||||
<IconButton onClick={addToCollection}>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
|
||||
@@ -3,8 +3,12 @@ import { NavbarBase } from "@/base/components/Navbar";
|
||||
import { useIsMobileWidth } from "@/base/hooks";
|
||||
import log from "@/base/log";
|
||||
import type { Collection } from "@/media/collection";
|
||||
import { SearchResultsHeader } from "@/new/photos/components/Gallery";
|
||||
import {
|
||||
PeopleEmptyState,
|
||||
SearchResultsHeader,
|
||||
} from "@/new/photos/components/Gallery";
|
||||
import type { GalleryBarMode } from "@/new/photos/components/Gallery/BarImpl";
|
||||
import { GalleryPeopleState } from "@/new/photos/components/Gallery/PeopleHeader";
|
||||
import {
|
||||
SearchBar,
|
||||
type SearchBarProps,
|
||||
@@ -526,9 +530,16 @@ export default function Gallery() {
|
||||
);
|
||||
}, [collections, activeCollectionID]);
|
||||
|
||||
const filteredData = useMemoSingleThreaded(async (): Promise<
|
||||
EnteFile[]
|
||||
> => {
|
||||
// The derived UI state when we are in "people" mode.
|
||||
//
|
||||
// TODO: This spawns even more workarounds below. Move this to a
|
||||
// reducer/store.
|
||||
type DerivedState1 = {
|
||||
filteredData: EnteFile[];
|
||||
galleryPeopleState: GalleryPeopleState | undefined;
|
||||
};
|
||||
|
||||
const derived1: DerivedState1 = useMemoSingleThreaded(async () => {
|
||||
if (
|
||||
!files ||
|
||||
!user ||
|
||||
@@ -536,35 +547,56 @@ export default function Gallery() {
|
||||
!hiddenFiles ||
|
||||
!archivedCollections
|
||||
) {
|
||||
return;
|
||||
return { filteredData: [], galleryPeopleState: undefined };
|
||||
}
|
||||
|
||||
if (activeCollectionID === TRASH_SECTION && !selectedSearchOption) {
|
||||
return getUniqueFiles([
|
||||
const filteredData = getUniqueFiles([
|
||||
...trashedFiles,
|
||||
...files.filter((file) => tempDeletedFileIds?.has(file.id)),
|
||||
]);
|
||||
return { filteredData, galleryPeopleState: undefined };
|
||||
}
|
||||
|
||||
let filteredFiles: EnteFile[] = [];
|
||||
let galleryPeopleState: GalleryPeopleState;
|
||||
if (selectedSearchOption) {
|
||||
filteredFiles = await filterSearchableFiles(
|
||||
selectedSearchOption.suggestion,
|
||||
);
|
||||
} else if (barMode == "people") {
|
||||
const activePerson = ensure(
|
||||
people.find((p) => p.id == activePersonID) ?? people[0],
|
||||
);
|
||||
const pfSet = new Set(activePerson.fileIDs);
|
||||
let filteredPeople = people ?? [];
|
||||
if (tempDeletedFileIds?.size ?? tempHiddenFileIds?.size) {
|
||||
// Prune the in-memory temp updates from the actual state to
|
||||
// obtain the UI state. Kept inside an preflight check to so
|
||||
// that the common path remains fast.
|
||||
filteredPeople = filteredPeople
|
||||
.map((p) => ({
|
||||
...p,
|
||||
fileIDs: p.fileIDs.filter(
|
||||
(id) =>
|
||||
!tempDeletedFileIds?.has(id) &&
|
||||
!tempHiddenFileIds?.has(id),
|
||||
),
|
||||
}))
|
||||
.filter((p) => p.fileIDs.length > 0);
|
||||
}
|
||||
const activePerson =
|
||||
filteredPeople.find((p) => p.id == activePersonID) ??
|
||||
// We don't have an "All" pseudo-album in people mode currently,
|
||||
// so default to the first person in the list.
|
||||
filteredPeople[0];
|
||||
const pfSet = new Set(activePerson?.fileIDs ?? []);
|
||||
filteredFiles = getUniqueFiles(
|
||||
files.filter(({ id }) => {
|
||||
if (!pfSet.has(id)) return false;
|
||||
// TODO-Cluster
|
||||
// if (tempDeletedFileIds?.has(id)) return false;
|
||||
// if (tempHiddenFileIds?.has(id)) return false;
|
||||
return true;
|
||||
}),
|
||||
);
|
||||
galleryPeopleState = {
|
||||
activePerson,
|
||||
people: filteredPeople,
|
||||
};
|
||||
} else {
|
||||
const baseFiles = barMode == "hidden-albums" ? hiddenFiles : files;
|
||||
filteredFiles = getUniqueFiles(
|
||||
@@ -630,10 +662,10 @@ export default function Gallery() {
|
||||
}
|
||||
const sortAsc = activeCollection?.pubMagicMetadata?.data?.asc ?? false;
|
||||
if (sortAsc) {
|
||||
return sortFiles(filteredFiles, true);
|
||||
} else {
|
||||
return filteredFiles;
|
||||
filteredFiles = sortFiles(filteredFiles, true);
|
||||
}
|
||||
|
||||
return { filteredData: filteredFiles, galleryPeopleState };
|
||||
}, [
|
||||
barMode,
|
||||
files,
|
||||
@@ -649,6 +681,11 @@ export default function Gallery() {
|
||||
activePersonID,
|
||||
]);
|
||||
|
||||
const { filteredData, galleryPeopleState } = derived1 ?? {
|
||||
filteredData: [],
|
||||
galleryPeopleState: undefined,
|
||||
};
|
||||
|
||||
const selectAll = (e: KeyboardEvent) => {
|
||||
// ignore ctrl/cmd + a if the user is typing in a text field
|
||||
if (
|
||||
@@ -679,10 +716,13 @@ export default function Gallery() {
|
||||
count: 0,
|
||||
collectionID: activeCollectionID,
|
||||
context:
|
||||
barMode == "people" && activePersonID
|
||||
? { mode: "people" as const, personID: activePersonID }
|
||||
barMode == "people" && galleryPeopleState?.activePerson?.id
|
||||
? {
|
||||
mode: "people" as const,
|
||||
personID: galleryPeopleState.activePerson.id,
|
||||
}
|
||||
: {
|
||||
mode: "albums" as const,
|
||||
mode: barMode as "albums" | "hidden-albums",
|
||||
collectionID: ensure(activeCollectionID),
|
||||
},
|
||||
};
|
||||
@@ -1053,12 +1093,12 @@ export default function Gallery() {
|
||||
};
|
||||
|
||||
const handleSelectPerson = (person: Person | undefined) => {
|
||||
// The person bar currently does not have an "all" mode, so default to
|
||||
// the first person when no specific person is provided. This can happen
|
||||
// when the user clicks the "People" header in the search empty state (it
|
||||
// is guaranteed that this header will only be shown if there is at
|
||||
// least one person).
|
||||
setActivePersonID(person?.id ?? ensure(people[0]).id);
|
||||
setActivePersonID(person?.id);
|
||||
setBarMode("people");
|
||||
};
|
||||
|
||||
const handleSelectFileInfoPerson = (personID: string) => {
|
||||
setActivePersonID(personID);
|
||||
setBarMode("people");
|
||||
};
|
||||
|
||||
@@ -1066,6 +1106,10 @@ export default function Gallery() {
|
||||
return <div></div>;
|
||||
}
|
||||
|
||||
// `people` will be undefined only when ML is disabled, otherwise it'll be
|
||||
// an empty array (even if people are loading).
|
||||
const showPeopleSectionButton = people !== undefined;
|
||||
|
||||
return (
|
||||
<GalleryContext.Provider
|
||||
value={{
|
||||
@@ -1170,8 +1214,9 @@ export default function Gallery() {
|
||||
activeCollectionID,
|
||||
setActiveCollectionID,
|
||||
hiddenCollectionSummaries,
|
||||
people,
|
||||
activePersonID,
|
||||
showPeopleSectionButton,
|
||||
people: galleryPeopleState?.people ?? [],
|
||||
activePerson: galleryPeopleState?.activePerson,
|
||||
onSelectPerson: handleSelectPerson,
|
||||
setCollectionNamerAttributes,
|
||||
setPhotoListHeader,
|
||||
@@ -1234,6 +1279,11 @@ export default function Gallery() {
|
||||
!hiddenFiles?.length &&
|
||||
activeCollectionID === ALL_SECTION ? (
|
||||
<GalleryEmptyState openUploader={openUploader} />
|
||||
) : !isInSearchMode &&
|
||||
!isFirstLoad &&
|
||||
barMode == "people" &&
|
||||
!galleryPeopleState?.activePerson ? (
|
||||
<PeopleEmptyState />
|
||||
) : (
|
||||
<PhotoFrame
|
||||
page={PAGES.GALLERY}
|
||||
@@ -1247,7 +1297,7 @@ export default function Gallery() {
|
||||
setTempDeletedFileIds={setTempDeletedFileIds}
|
||||
setIsPhotoSwipeOpen={setIsPhotoSwipeOpen}
|
||||
activeCollectionID={activeCollectionID}
|
||||
activePersonID={activePersonID}
|
||||
activePersonID={galleryPeopleState?.activePerson?.id}
|
||||
enableDownload={true}
|
||||
fileToCollectionsMap={fileToCollectionsMap}
|
||||
collectionNameMap={collectionNameMap}
|
||||
@@ -1259,6 +1309,7 @@ export default function Gallery() {
|
||||
setFilesDownloadProgressAttributesCreator
|
||||
}
|
||||
selectable={true}
|
||||
onSelectPerson={handleSelectFileInfoPerson}
|
||||
/>
|
||||
)}
|
||||
{selected.count > 0 &&
|
||||
|
||||
@@ -318,9 +318,9 @@ const Slideshow: React.FC = () => {
|
||||
/images/onboarding-lock/3x.png 3x"
|
||||
/>
|
||||
<FeatureText>
|
||||
<Trans i18nKey={"HERO_SLIDE_1_TITLE"} />
|
||||
<Trans i18nKey={"intro_slide_1_title"} />
|
||||
</FeatureText>
|
||||
<TextContainer>{t("HERO_SLIDE_1")}</TextContainer>
|
||||
<TextContainer>{t("intro_slide_1")}</TextContainer>
|
||||
</Slide>
|
||||
<Slide index={1}>
|
||||
<SlideContents>
|
||||
@@ -330,9 +330,9 @@ const Slideshow: React.FC = () => {
|
||||
/images/onboarding-safe/3x.png 3x"
|
||||
/>
|
||||
<FeatureText>
|
||||
<Trans i18nKey={"HERO_SLIDE_2_TITLE"} />
|
||||
<Trans i18nKey={"intro_slide_2_title"} />
|
||||
</FeatureText>
|
||||
<TextContainer>{t("HERO_SLIDE_2")}</TextContainer>
|
||||
<TextContainer>{t("intro_slide_2")}</TextContainer>
|
||||
</SlideContents>
|
||||
</Slide>
|
||||
<Slide index={2}>
|
||||
@@ -343,9 +343,9 @@ const Slideshow: React.FC = () => {
|
||||
/images/onboarding-sync/3x.png 3x"
|
||||
/>
|
||||
<FeatureText>
|
||||
<Trans i18nKey={"HERO_SLIDE_3_TITLE"} />
|
||||
<Trans i18nKey={"intro_slide_3_title"} />
|
||||
</FeatureText>
|
||||
<TextContainer>{t("HERO_SLIDE_3")}</TextContainer>
|
||||
<TextContainer>{t("intro_slide_3")}</TextContainer>
|
||||
</SlideContents>
|
||||
</Slide>
|
||||
</Slider>
|
||||
|
||||
@@ -20,7 +20,7 @@ export const getDownloadAppMessage = (): DialogBoxAttributes => {
|
||||
variant: "accent",
|
||||
},
|
||||
close: {
|
||||
text: t("CLOSE"),
|
||||
text: t("close"),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -182,7 +182,7 @@ export const getEditorCloseConfirmationMessage = (
|
||||
content: t("CONFIRM_EDITOR_CLOSE_DESCRIPTION"),
|
||||
proceed: {
|
||||
action: doClose,
|
||||
text: t("CLOSE"),
|
||||
text: t("close"),
|
||||
variant: "critical",
|
||||
},
|
||||
close: { text: t("cancel") },
|
||||
|
||||
@@ -99,7 +99,7 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
|
||||
|
||||
const showNoRecoveryKeyMessage = () =>
|
||||
setDialogBoxAttributesV2({
|
||||
title: t("SORRY"),
|
||||
title: t("sorry"),
|
||||
close: {},
|
||||
content: t("NO_RECOVERY_KEY_MESSAGE"),
|
||||
});
|
||||
|
||||
@@ -149,7 +149,7 @@ const Page: React.FC<RecoverPageProps> = ({ appContext, twoFactorType }) => {
|
||||
dialogClose?: DialogBoxAttributesV2["close"],
|
||||
) => {
|
||||
appContext.setDialogBoxAttributesV2({
|
||||
title: t("CONTACT_SUPPORT"),
|
||||
title: t("contact_support"),
|
||||
close: dialogClose ?? {},
|
||||
content: (
|
||||
<Trans
|
||||
|
||||
@@ -33,6 +33,7 @@ import { t } from "i18next";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import { getSRPAttributes } from "../api/srp";
|
||||
import { putAttributes, sendOtt, verifyOtt } from "../api/user";
|
||||
import { PAGES } from "../constants/pages";
|
||||
import {
|
||||
@@ -42,7 +43,7 @@ import {
|
||||
import { unstashRedirect } from "../services/redirect";
|
||||
import { configureSRP } from "../services/srp";
|
||||
import type { PageProps } from "../types/page";
|
||||
import type { SRPSetupAttributes } from "../types/srp";
|
||||
import type { SRPAttributes, SRPSetupAttributes } from "../types/srp";
|
||||
|
||||
const Page: React.FC<PageProps> = ({ appContext }) => {
|
||||
const { logout, showNavBar, setDialogBoxAttributesV2 } = appContext;
|
||||
@@ -58,16 +59,9 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
|
||||
useEffect(() => {
|
||||
const main = async () => {
|
||||
const user: User = getData(LS_KEYS.USER);
|
||||
const keyAttributes: KeyAttributes = getData(
|
||||
LS_KEYS.KEY_ATTRIBUTES,
|
||||
);
|
||||
if (!user?.email) {
|
||||
router.push("/");
|
||||
} else if (
|
||||
keyAttributes?.encryptedKey &&
|
||||
(user.token || user.encryptedToken)
|
||||
) {
|
||||
router.push(PAGES.CREDENTIALS);
|
||||
const redirect = await redirectionIfNeeded(user);
|
||||
if (redirect) {
|
||||
router.push(redirect);
|
||||
} else {
|
||||
setEmail(user.email);
|
||||
}
|
||||
@@ -253,3 +247,45 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
/**
|
||||
* A function called during page load to see if a redirection is required
|
||||
*
|
||||
* @returns The slug to redirect to, if needed.
|
||||
*/
|
||||
const redirectionIfNeeded = async (user: User | undefined) => {
|
||||
const email = user?.email;
|
||||
if (!email) {
|
||||
return "/";
|
||||
}
|
||||
|
||||
const keyAttributes: KeyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
|
||||
|
||||
if (keyAttributes?.encryptedKey && (user.token || user.encryptedToken)) {
|
||||
return PAGES.CREDENTIALS;
|
||||
}
|
||||
|
||||
// The user might have email verification disabled, but after previously
|
||||
// entering their email on the login screen, they might've closed the tab
|
||||
// before proceeding (or opened a us in a new tab at this point).
|
||||
//
|
||||
// In such cases, we'll end up here with an email present.
|
||||
//
|
||||
// To distinguish this scenario from the normal email verification flow, we
|
||||
// can check to see the SRP attributes (the login page would've fetched and
|
||||
// saved them). If they are present and indicate that email verification is
|
||||
// not required, redirect to the password verification page.
|
||||
|
||||
const srpAttributes: SRPAttributes = getData(LS_KEYS.SRP_ATTRIBUTES);
|
||||
if (srpAttributes && !srpAttributes.isEmailMFAEnabled) {
|
||||
// Fetch the latest SRP attributes instead of relying on the potentially
|
||||
// stale stored values. This is an infrequent scenario path, so extra
|
||||
// API calls are fine.
|
||||
const latestSRPAttributes = await getSRPAttributes(email);
|
||||
if (latestSRPAttributes && !latestSRPAttributes.isEmailMFAEnabled) {
|
||||
return PAGES.CREDENTIALS;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>نسخ احتياطية خاصة</div><div>لذكرياتك</div>",
|
||||
"HERO_SLIDE_1": "تشفير من طرف إلى طرف بشكل افتراضي",
|
||||
"HERO_SLIDE_2_TITLE": "<div>يتم تخزينها بأمان</div><div>في ملجأ للطوارئ</div>",
|
||||
"HERO_SLIDE_2": "مصممة لتدوم",
|
||||
"HERO_SLIDE_3_TITLE": "<div>متاح</div><div> في كل مكان</div>",
|
||||
"HERO_SLIDE_3": "أندرويد، آي أو إس، ويب، سطح المكتب",
|
||||
"intro_slide_1_title": "<div>نسخ احتياطية خاصة</div><div>لذكرياتك</div>",
|
||||
"intro_slide_1": "تشفير من طرف إلى طرف بشكل افتراضي",
|
||||
"intro_slide_2_title": "<div>يتم تخزينها بأمان</div><div>في ملجأ للطوارئ</div>",
|
||||
"intro_slide_2": "مصممة لتدوم",
|
||||
"intro_slide_3_title": "<div>متاح</div><div> في كل مكان</div>",
|
||||
"intro_slide_3": "أندرويد، آي أو إس، ويب، سطح المكتب",
|
||||
"login": "تسجيل الدخول",
|
||||
"sign_up": "تسجيل",
|
||||
"NEW_USER": "جديد في Ente",
|
||||
"EXISTING_USER": "مستخدم موجود",
|
||||
"ENTER_NAME": "أدخل الاسم",
|
||||
"enter_name": "أدخل الاسم",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "أضف اسما حتى يتمكن أصدقاؤك من معرفة من يشكرون على هذه الصور الرائعة!",
|
||||
"ENTER_EMAIL": "أدخل عنوان البريد الإلكتروني",
|
||||
"EMAIL_ERROR": "أدخل بريد إلكتروني صالح",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "إنشاء ألبومات",
|
||||
"CREATE_COLLECTION": "ألبوم جديد",
|
||||
"enter_album_name": "اسم الألبوم",
|
||||
"CLOSE_OPTION": "إغلاق (Esc)",
|
||||
"close_key": "إغلاق (Esc)",
|
||||
"enter_file_name": "إسم الملف",
|
||||
"CLOSE": "إغلاق",
|
||||
"NO": "لا",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "إغلاق",
|
||||
"no": "لا",
|
||||
"nothing_here": "",
|
||||
"upload": "تحميل",
|
||||
"import": "استيراد",
|
||||
"add_photos": "إضافة صور",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "تم تغيير كلمة المرور في مكان آخر",
|
||||
"password_changed_elsewhere_message": "يرجى تسجيل الدخول مرة أخرى على هذا الجهاز لاستخدام كلمة المرور الجديدة للمصادقة.",
|
||||
"GO_BACK": "رجوع",
|
||||
"RECOVERY_KEY": "مفتاح الاستعادة",
|
||||
"SAVE_LATER": "قم بهذا لاحقا",
|
||||
"SAVE": "حفظ المفتاح",
|
||||
"recovery_key": "مفتاح الاستعادة",
|
||||
"do_this_later": "قم بهذا لاحقا",
|
||||
"save_key": "حفظ المفتاح",
|
||||
"RECOVERY_KEY_DESCRIPTION": "إذا نسيت كلمة المرور الخاصة بك، فالطريقة الوحيدة التي يمكنك بها استرداد البيانات الخاصة بك هي بهذا المفتاح.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "لا يمكن إنشاء رمز الاسترداد، الرجاء المحاولة مرة أخرى",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "لا يتم تخزين هذا المفتاح أليا، لذا يرجى حفظ هذا في مكان آمن",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "استعادة",
|
||||
"NO_RECOVERY_KEY": "ما من مفتاح استعادة؟",
|
||||
"INCORRECT_RECOVERY_KEY": "مفتاح استعادة غير صحيح",
|
||||
"SORRY": "عذرا",
|
||||
"sorry": "عذرا",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "بسبب طبيعة نظام التشفير التام بين الطرفين، لا يمكن فك تشفير بياناتك دون كلمة المرور أو مفتاح الاسترداد الخاص بك",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "الاتصال بالدعم",
|
||||
"REQUEST_FEATURE": "طلب ميزة",
|
||||
"SUPPORT": "الدعم",
|
||||
"CONFIRM": "تأكيد",
|
||||
"contact_support": "الاتصال بالدعم",
|
||||
"request_feature": "طلب ميزة",
|
||||
"support": "الدعم",
|
||||
"cancel": "إلغاء",
|
||||
"LOGOUT": "تسجيل الخروج",
|
||||
"logout": "تسجيل الخروج",
|
||||
"logout_message": "هل أنت متأكد من أنك تريد تسجيل الخروج؟",
|
||||
"delete_account": "حذف الحساب",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "هل أنت متأكد من أنك تريد تسجيل الخروج؟",
|
||||
"CHANGE_EMAIL": "تغيير البريد الإلكتروني",
|
||||
"ok": "حسنا",
|
||||
"success": "تم بنجاح",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "إخفاء الألبوم",
|
||||
"unhide_collection": "إلغاء إخفاء الألبوم",
|
||||
"MOVE": "نقل",
|
||||
"ADD": "إضافة",
|
||||
"add": "إضافة",
|
||||
"REMOVE": "ازالة",
|
||||
"YES_REMOVE": "نعم، إزالة",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Личен бекъп</div><div>на твоите спомени</div>",
|
||||
"HERO_SLIDE_1": "Криптиран от край до край по подразбиране",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "<div>Личен бекъп</div><div>на твоите спомени</div>",
|
||||
"intro_slide_1": "Криптиран от край до край по подразбиране",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
651
web/packages/base/locales/da-DK/translation.json
Normal file
651
web/packages/base/locales/da-DK/translation.json
Normal file
@@ -0,0 +1,651 @@
|
||||
{
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
"REQUIRED": "",
|
||||
"EMAIL_SENT": "",
|
||||
"CHECK_INBOX": "",
|
||||
"ENTER_OTT": "",
|
||||
"RESEND_MAIL": "",
|
||||
"VERIFY": "",
|
||||
"UNKNOWN_ERROR": "",
|
||||
"INVALID_CODE": "",
|
||||
"EXPIRED_CODE": "",
|
||||
"SENDING": "",
|
||||
"SENT": "",
|
||||
"password": "",
|
||||
"link_password_description": "",
|
||||
"unlock": "",
|
||||
"SET_PASSPHRASE": "",
|
||||
"VERIFY_PASSPHRASE": "",
|
||||
"INCORRECT_PASSPHRASE": "",
|
||||
"ENTER_ENC_PASSPHRASE": "",
|
||||
"PASSPHRASE_DISCLAIMER": "",
|
||||
"WELCOME_TO_ENTE_HEADING": "",
|
||||
"WELCOME_TO_ENTE_SUBHEADING": "",
|
||||
"WHERE_YOUR_BEST_PHOTOS_LIVE": "",
|
||||
"KEY_GENERATION_IN_PROGRESS_MESSAGE": "",
|
||||
"PASSPHRASE_HINT": "",
|
||||
"CONFIRM_PASSPHRASE": "",
|
||||
"REFERRAL_CODE_HINT": "",
|
||||
"REFERRAL_INFO": "",
|
||||
"PASSPHRASE_MATCH_ERROR": "",
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
"add_more_photos": "",
|
||||
"add_photos_count_one": "",
|
||||
"add_photos_count": "",
|
||||
"select_photos": "",
|
||||
"FILE_UPLOAD": "",
|
||||
"UPLOAD_STAGE_MESSAGE": {
|
||||
"0": "",
|
||||
"1": "",
|
||||
"2": "",
|
||||
"3": "",
|
||||
"4": "",
|
||||
"5": ""
|
||||
},
|
||||
"FILE_NOT_UPLOADED_LIST": "",
|
||||
"INITIAL_LOAD_DELAY_WARNING": "",
|
||||
"USER_DOES_NOT_EXIST": "",
|
||||
"NO_ACCOUNT": "",
|
||||
"ACCOUNT_EXISTS": "",
|
||||
"CREATE": "",
|
||||
"download": "",
|
||||
"download_album": "",
|
||||
"download_favorites": "",
|
||||
"download_uncategorized": "",
|
||||
"download_hidden_items": "",
|
||||
"download_key": "",
|
||||
"copy_key": "",
|
||||
"toggle_fullscreen_key": "",
|
||||
"zoom_in_out_key": "",
|
||||
"previous_key": "",
|
||||
"next_key": "",
|
||||
"title_photos": "",
|
||||
"title_auth": "",
|
||||
"title_accounts": "",
|
||||
"UPLOAD_FIRST_PHOTO": "",
|
||||
"IMPORT_YOUR_FOLDERS": "",
|
||||
"UPLOAD_DROPZONE_MESSAGE": "",
|
||||
"WATCH_FOLDER_DROPZONE_MESSAGE": "",
|
||||
"TRASH_FILES_TITLE": "",
|
||||
"TRASH_FILE_TITLE": "",
|
||||
"DELETE_FILES_TITLE": "",
|
||||
"DELETE_FILES_MESSAGE": "",
|
||||
"DELETE": "",
|
||||
"DELETE_OPTION": "",
|
||||
"FAVORITE_OPTION": "",
|
||||
"UNFAVORITE_OPTION": "",
|
||||
"MULTI_FOLDER_UPLOAD": "",
|
||||
"UPLOAD_STRATEGY_CHOICE": "",
|
||||
"UPLOAD_STRATEGY_SINGLE_COLLECTION": "",
|
||||
"OR": "",
|
||||
"UPLOAD_STRATEGY_COLLECTION_PER_FOLDER": "",
|
||||
"SESSION_EXPIRED_MESSAGE": "",
|
||||
"SESSION_EXPIRED": "",
|
||||
"PASSWORD_GENERATION_FAILED": "",
|
||||
"CHANGE_PASSWORD": "",
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
"FORGOT_PASSWORD": "",
|
||||
"RECOVER_ACCOUNT": "",
|
||||
"RECOVERY_KEY_HINT": "",
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
"error": "",
|
||||
"OFFLINE_MSG": "",
|
||||
"install": "",
|
||||
"install_mobile_app": "",
|
||||
"download_app": "",
|
||||
"download_app_message": "",
|
||||
"EXPORT": "",
|
||||
"SUBSCRIPTION": "",
|
||||
"SUBSCRIBE": "",
|
||||
"MANAGEMENT_PORTAL": "",
|
||||
"MANAGE_FAMILY_PORTAL": "",
|
||||
"LEAVE_FAMILY_PLAN": "",
|
||||
"LEAVE": "",
|
||||
"LEAVE_FAMILY_CONFIRM": "",
|
||||
"CHOOSE_PLAN": "",
|
||||
"MANAGE_PLAN": "",
|
||||
"CURRENT_USAGE": "",
|
||||
"TWO_MONTHS_FREE": "",
|
||||
"POPULAR": "",
|
||||
"free_plan_option": "",
|
||||
"free_plan_description": "",
|
||||
"active": "",
|
||||
"subscription_info_free": "",
|
||||
"subscription_info_family": "",
|
||||
"subscription_info_expired": "",
|
||||
"subscription_info_renewal_cancelled": "",
|
||||
"subscription_info_storage_quota_exceeded": "",
|
||||
"subscription_status_renewal_active": "",
|
||||
"subscription_status_renewal_cancelled": "",
|
||||
"add_on_valid_till": "",
|
||||
"subscription_expired": "",
|
||||
"storage_quota_exceeded": "",
|
||||
"SUBSCRIPTION_PURCHASE_SUCCESS": "",
|
||||
"SUBSCRIPTION_PURCHASE_CANCELLED": "",
|
||||
"SUBSCRIPTION_PURCHASE_FAILED": "",
|
||||
"SUBSCRIPTION_UPDATE_FAILED": "",
|
||||
"UPDATE_PAYMENT_METHOD_MESSAGE": "",
|
||||
"STRIPE_AUTHENTICATION_FAILED": "",
|
||||
"UPDATE_PAYMENT_METHOD": "",
|
||||
"MONTHLY": "",
|
||||
"YEARLY": "",
|
||||
"MONTH_SHORT": "",
|
||||
"YEAR": "",
|
||||
"update_subscription_title": "",
|
||||
"UPDATE_SUBSCRIPTION_MESSAGE": "",
|
||||
"UPDATE_SUBSCRIPTION": "",
|
||||
"CANCEL_SUBSCRIPTION": "",
|
||||
"CANCEL_SUBSCRIPTION_MESSAGE": "",
|
||||
"CANCEL_SUBSCRIPTION_WITH_ADDON_MESSAGE": "",
|
||||
"SUBSCRIPTION_CANCEL_FAILED": "",
|
||||
"SUBSCRIPTION_CANCEL_SUCCESS": "",
|
||||
"REACTIVATE_SUBSCRIPTION": "",
|
||||
"REACTIVATE_SUBSCRIPTION_MESSAGE": "",
|
||||
"SUBSCRIPTION_ACTIVATE_SUCCESS": "",
|
||||
"SUBSCRIPTION_ACTIVATE_FAILED": "",
|
||||
"SUBSCRIPTION_PURCHASE_SUCCESS_TITLE": "",
|
||||
"CANCEL_SUBSCRIPTION_ON_MOBILE": "",
|
||||
"CANCEL_SUBSCRIPTION_ON_MOBILE_MESSAGE": "",
|
||||
"MAIL_TO_MANAGE_SUBSCRIPTION": "",
|
||||
"rename": "",
|
||||
"rename_file": "",
|
||||
"rename_album": "",
|
||||
"delete_album": "",
|
||||
"delete_album_title": "",
|
||||
"delete_album_message": "",
|
||||
"delete_photos": "",
|
||||
"keep_photos": "",
|
||||
"share_album": "",
|
||||
"SHARE_WITH_SELF": "",
|
||||
"ALREADY_SHARED": "",
|
||||
"SHARING_BAD_REQUEST_ERROR": "",
|
||||
"SHARING_DISABLED_FOR_FREE_ACCOUNTS": "",
|
||||
"CREATE_ALBUM_FAILED": "",
|
||||
"search": "",
|
||||
"search_results": "",
|
||||
"no_results": "",
|
||||
"search_hint": "",
|
||||
"album": "",
|
||||
"date": "",
|
||||
"description": "",
|
||||
"file_type": "",
|
||||
"magic": "",
|
||||
"photos_count_zero": "",
|
||||
"photos_count_one": "",
|
||||
"photos_count": "",
|
||||
"TERMS_AND_CONDITIONS": "",
|
||||
"SELECTED": "",
|
||||
"people": "",
|
||||
"indexing_scheduled": "",
|
||||
"indexing_photos": "",
|
||||
"indexing_fetching": "",
|
||||
"indexing_people": "",
|
||||
"indexing_done": "",
|
||||
"UNIDENTIFIED_FACES": "",
|
||||
"OBJECTS": "",
|
||||
"TEXT": "",
|
||||
"INFO": "",
|
||||
"INFO_OPTION": "",
|
||||
"file_name": "",
|
||||
"CAPTION_PLACEHOLDER": "",
|
||||
"location": "",
|
||||
"SHOW_ON_MAP": "",
|
||||
"MAP": "",
|
||||
"MAP_SETTINGS": "",
|
||||
"ENABLE_MAPS": "",
|
||||
"ENABLE_MAP": "",
|
||||
"DISABLE_MAPS": "",
|
||||
"ENABLE_MAP_DESCRIPTION": "",
|
||||
"DISABLE_MAP_DESCRIPTION": "",
|
||||
"DISABLE_MAP": "",
|
||||
"DETAILS": "",
|
||||
"view_exif": "",
|
||||
"no_exif": "",
|
||||
"exif": "",
|
||||
"ISO": "",
|
||||
"TWO_FACTOR": "",
|
||||
"TWO_FACTOR_AUTHENTICATION": "",
|
||||
"TWO_FACTOR_QR_INSTRUCTION": "",
|
||||
"ENTER_CODE_MANUALLY": "",
|
||||
"TWO_FACTOR_MANUAL_CODE_INSTRUCTION": "",
|
||||
"SCAN_QR_CODE": "",
|
||||
"ENABLE_TWO_FACTOR": "",
|
||||
"enable": "",
|
||||
"enabled": "",
|
||||
"LOST_DEVICE": "",
|
||||
"INCORRECT_CODE": "",
|
||||
"TWO_FACTOR_INFO": "",
|
||||
"DISABLE_TWO_FACTOR_LABEL": "",
|
||||
"UPDATE_TWO_FACTOR_LABEL": "",
|
||||
"disable": "",
|
||||
"reconfigure": "",
|
||||
"UPDATE_TWO_FACTOR": "",
|
||||
"UPDATE_TWO_FACTOR_MESSAGE": "",
|
||||
"UPDATE": "",
|
||||
"DISABLE_TWO_FACTOR": "",
|
||||
"DISABLE_TWO_FACTOR_MESSAGE": "",
|
||||
"TWO_FACTOR_DISABLE_FAILED": "",
|
||||
"EXPORT_DATA": "",
|
||||
"select_folder": "",
|
||||
"select_zips": "",
|
||||
"faq": "",
|
||||
"takeout_hint": "",
|
||||
"DESTINATION": "",
|
||||
"START": "",
|
||||
"LAST_EXPORT_TIME": "",
|
||||
"EXPORT_AGAIN": "",
|
||||
"LOCAL_STORAGE_NOT_ACCESSIBLE": "",
|
||||
"LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE": "",
|
||||
"SEND_OTT": "",
|
||||
"EMAIl_ALREADY_OWNED": "",
|
||||
"ETAGS_BLOCKED": "",
|
||||
"LIVE_PHOTOS_DETECTED": "",
|
||||
"RETRY_FAILED": "",
|
||||
"FAILED_UPLOADS": "",
|
||||
"failed_uploads_hint": "",
|
||||
"SKIPPED_FILES": "",
|
||||
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "",
|
||||
"UNSUPPORTED_FILES": "",
|
||||
"SUCCESSFUL_UPLOADS": "",
|
||||
"SKIPPED_INFO": "",
|
||||
"UNSUPPORTED_INFO": "",
|
||||
"BLOCKED_UPLOADS": "",
|
||||
"INPROGRESS_METADATA_EXTRACTION": "",
|
||||
"INPROGRESS_UPLOADS": "",
|
||||
"TOO_LARGE_UPLOADS": "",
|
||||
"LARGER_THAN_AVAILABLE_STORAGE_UPLOADS": "",
|
||||
"LARGER_THAN_AVAILABLE_STORAGE_INFO": "",
|
||||
"TOO_LARGE_INFO": "",
|
||||
"THUMBNAIL_GENERATION_FAILED_INFO": "",
|
||||
"select_album": "",
|
||||
"upload_to_album": "",
|
||||
"add_to_album": "",
|
||||
"move_to_album": "",
|
||||
"unhide_to_album": "",
|
||||
"restore_to_album": "",
|
||||
"section_all": "",
|
||||
"section_uncategorized": "",
|
||||
"section_archive": "",
|
||||
"section_hidden": "",
|
||||
"section_trash": "",
|
||||
"favorites": "",
|
||||
"archive": "",
|
||||
"archive_album": "",
|
||||
"unarchive": "",
|
||||
"unarchive_album": "",
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
"MOVE_TO_TRASH": "",
|
||||
"TRASH_FILES_MESSAGE": "",
|
||||
"TRASH_FILE_MESSAGE": "",
|
||||
"DELETE_PERMANENTLY": "",
|
||||
"RESTORE": "",
|
||||
"empty_trash": "",
|
||||
"empty_trash_title": "",
|
||||
"empty_trash_message": "",
|
||||
"leave_album": "",
|
||||
"leave_shared_album_title": "",
|
||||
"leave_shared_album_message": "",
|
||||
"leave_shared_album": "",
|
||||
"NOT_FILE_OWNER": "",
|
||||
"CONFIRM_SELF_REMOVE_MESSAGE": "",
|
||||
"CONFIRM_SELF_AND_OTHER_REMOVE_MESSAGE": "",
|
||||
"sort_by_creation_time_ascending": "",
|
||||
"sort_by_updation_time_descending": "",
|
||||
"sort_by_name": "",
|
||||
"FIX_CREATION_TIME": "",
|
||||
"FIX_CREATION_TIME_IN_PROGRESS": "",
|
||||
"CREATION_TIME_UPDATED": "",
|
||||
"UPDATE_CREATION_TIME_NOT_STARTED": "",
|
||||
"UPDATE_CREATION_TIME_COMPLETED": "",
|
||||
"UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR": "",
|
||||
"CAPTION_CHARACTER_LIMIT": "",
|
||||
"DATE_TIME_ORIGINAL": "",
|
||||
"DATE_TIME_DIGITIZED": "",
|
||||
"METADATA_DATE": "",
|
||||
"CUSTOM_TIME": "",
|
||||
"REOPEN_PLAN_SELECTOR_MODAL": "",
|
||||
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "",
|
||||
"sharing_details": "",
|
||||
"modify_sharing": "",
|
||||
"ADD_COLLABORATORS": "",
|
||||
"ADD_NEW_EMAIL": "",
|
||||
"shared_with_people_count_zero": "",
|
||||
"shared_with_people_count_one": "",
|
||||
"shared_with_people_count": "",
|
||||
"participants_count_zero": "",
|
||||
"participants_count_one": "",
|
||||
"participants_count": "",
|
||||
"ADD_VIEWERS": "",
|
||||
"CHANGE_PERMISSIONS_TO_VIEWER": "",
|
||||
"CHANGE_PERMISSIONS_TO_COLLABORATOR": "",
|
||||
"CONVERT_TO_VIEWER": "",
|
||||
"CONVERT_TO_COLLABORATOR": "",
|
||||
"CHANGE_PERMISSION": "",
|
||||
"REMOVE_PARTICIPANT": "",
|
||||
"CONFIRM_REMOVE": "",
|
||||
"MANAGE": "",
|
||||
"ADDED_AS": "",
|
||||
"COLLABORATOR_RIGHTS": "",
|
||||
"REMOVE_PARTICIPANT_HEAD": "",
|
||||
"OWNER": "",
|
||||
"COLLABORATORS": "",
|
||||
"ADD_MORE": "",
|
||||
"VIEWERS": "",
|
||||
"OR_ADD_EXISTING": "",
|
||||
"REMOVE_PARTICIPANT_MESSAGE": "",
|
||||
"NOT_FOUND": "",
|
||||
"LINK_EXPIRED": "",
|
||||
"LINK_EXPIRED_MESSAGE": "",
|
||||
"MANAGE_LINK": "",
|
||||
"LINK_TOO_MANY_REQUESTS": "",
|
||||
"FILE_DOWNLOAD": "",
|
||||
"link_password_lock": "",
|
||||
"PUBLIC_COLLECT": "",
|
||||
"LINK_DEVICE_LIMIT": "",
|
||||
"NO_DEVICE_LIMIT": "",
|
||||
"LINK_EXPIRY": "",
|
||||
"NEVER": "",
|
||||
"DISABLE_FILE_DOWNLOAD": "",
|
||||
"DISABLE_FILE_DOWNLOAD_MESSAGE": "",
|
||||
"SHARED_USING": "",
|
||||
"SHARING_REFERRAL_CODE": "",
|
||||
"LIVE": "",
|
||||
"DISABLE_PASSWORD": "",
|
||||
"DISABLE_PASSWORD_MESSAGE": "",
|
||||
"PASSWORD_LOCK": "",
|
||||
"LOCK": "",
|
||||
"file": "",
|
||||
"folder": "",
|
||||
"google_takeout": "",
|
||||
"DEDUPLICATE_FILES": "",
|
||||
"NO_DUPLICATES_FOUND": "",
|
||||
"FILES": "",
|
||||
"EACH": "",
|
||||
"DEDUPLICATE_BASED_ON_SIZE": "",
|
||||
"STOP_ALL_UPLOADS_MESSAGE": "",
|
||||
"STOP_UPLOADS_HEADER": "",
|
||||
"YES_STOP_UPLOADS": "",
|
||||
"STOP_DOWNLOADS_HEADER": "",
|
||||
"YES_STOP_DOWNLOADS": "",
|
||||
"STOP_ALL_DOWNLOADS_MESSAGE": "",
|
||||
"albums": "",
|
||||
"albums_count_one": "",
|
||||
"albums_count": "",
|
||||
"all_albums": "",
|
||||
"all_hidden_albums": "",
|
||||
"hidden_albums": "",
|
||||
"hidden_items": "",
|
||||
"ENTER_TWO_FACTOR_OTP": "",
|
||||
"CREATE_ACCOUNT": "",
|
||||
"COPIED": "",
|
||||
"WATCH_FOLDERS": "",
|
||||
"upgrade_now": "",
|
||||
"renew_now": "",
|
||||
"STORAGE": "",
|
||||
"USED": "",
|
||||
"YOU": "",
|
||||
"FAMILY": "",
|
||||
"FREE": "",
|
||||
"OF": "",
|
||||
"WATCHED_FOLDERS": "",
|
||||
"NO_FOLDERS_ADDED": "",
|
||||
"FOLDERS_AUTOMATICALLY_MONITORED": "",
|
||||
"UPLOAD_NEW_FILES_TO_ENTE": "",
|
||||
"REMOVE_DELETED_FILES_FROM_ENTE": "",
|
||||
"ADD_FOLDER": "",
|
||||
"STOP_WATCHING": "",
|
||||
"STOP_WATCHING_FOLDER": "",
|
||||
"STOP_WATCHING_DIALOG_MESSAGE": "",
|
||||
"YES_STOP": "",
|
||||
"CHANGE_FOLDER": "",
|
||||
"FAMILY_PLAN": "",
|
||||
"debug_logs": "",
|
||||
"DOWNLOAD_LOGS": "",
|
||||
"DOWNLOAD_LOGS_MESSAGE": "",
|
||||
"WEAK_DEVICE": "",
|
||||
"drag_and_drop_hint": "",
|
||||
"AUTHENTICATE": "",
|
||||
"UPLOADED_TO_SINGLE_COLLECTION": "",
|
||||
"UPLOADED_TO_SEPARATE_COLLECTIONS": "",
|
||||
"NEVERMIND": "",
|
||||
"UPDATE_AVAILABLE": "",
|
||||
"UPDATE_INSTALLABLE_MESSAGE": "",
|
||||
"INSTALL_NOW": "",
|
||||
"INSTALL_ON_NEXT_LAUNCH": "",
|
||||
"UPDATE_AVAILABLE_MESSAGE": "",
|
||||
"DOWNLOAD_AND_INSTALL": "",
|
||||
"IGNORE_THIS_VERSION": "",
|
||||
"TODAY": "",
|
||||
"YESTERDAY": "",
|
||||
"NAME_PLACEHOLDER": "",
|
||||
"ROOT_LEVEL_FILE_WITH_FOLDER_NOT_ALLOWED": "",
|
||||
"ROOT_LEVEL_FILE_WITH_FOLDER_NOT_ALLOWED_MESSAGE": "",
|
||||
"CHOSE_THEME": "",
|
||||
"more_details": "",
|
||||
"ml_search": "",
|
||||
"ml_search_description": "",
|
||||
"ml_search_footnote": "",
|
||||
"indexing": "",
|
||||
"processed": "",
|
||||
"indexing_status_running": "",
|
||||
"indexing_status_fetching": "",
|
||||
"indexing_status_scheduled": "",
|
||||
"indexing_status_done": "",
|
||||
"ml_search_disable": "",
|
||||
"ml_search_disable_confirm": "",
|
||||
"ml_consent": "",
|
||||
"ml_consent_title": "",
|
||||
"ml_consent_description": "",
|
||||
"ml_consent_confirmation": "",
|
||||
"labs": "",
|
||||
"YOURS": "",
|
||||
"passphrase_strength_weak": "",
|
||||
"passphrase_strength_moderate": "",
|
||||
"passphrase_strength_strong": "",
|
||||
"preferences": "",
|
||||
"language": "",
|
||||
"advanced": "",
|
||||
"EXPORT_DIRECTORY_DOES_NOT_EXIST": "",
|
||||
"EXPORT_DIRECTORY_DOES_NOT_EXIST_MESSAGE": "",
|
||||
"SUBSCRIPTION_VERIFICATION_ERROR": "",
|
||||
"storage_unit": {
|
||||
"b": "",
|
||||
"kb": "",
|
||||
"mb": "",
|
||||
"gb": "",
|
||||
"tb": ""
|
||||
},
|
||||
"AFTER_TIME": {
|
||||
"HOUR": "",
|
||||
"DAY": "",
|
||||
"WEEK": "",
|
||||
"MONTH": "",
|
||||
"YEAR": ""
|
||||
},
|
||||
"COPY_LINK": "",
|
||||
"DONE": "",
|
||||
"LINK_SHARE_TITLE": "",
|
||||
"REMOVE_LINK": "",
|
||||
"CREATE_PUBLIC_SHARING": "",
|
||||
"PUBLIC_LINK_CREATED": "",
|
||||
"PUBLIC_LINK_ENABLED": "",
|
||||
"COLLECT_PHOTOS": "",
|
||||
"PUBLIC_COLLECT_SUBTEXT": "",
|
||||
"STOP_EXPORT": "",
|
||||
"EXPORT_PROGRESS": "",
|
||||
"MIGRATING_EXPORT": "",
|
||||
"RENAMING_COLLECTION_FOLDERS": "",
|
||||
"TRASHING_DELETED_FILES": "",
|
||||
"TRASHING_DELETED_COLLECTIONS": "",
|
||||
"CONTINUOUS_EXPORT": "",
|
||||
"PENDING_ITEMS": "",
|
||||
"EXPORT_STARTING": "",
|
||||
"delete_account_reason_label": "",
|
||||
"delete_account_reason_placeholder": "",
|
||||
"delete_reason": {
|
||||
"missing_feature": "",
|
||||
"behaviour": "",
|
||||
"found_another_service": "",
|
||||
"not_listed": ""
|
||||
},
|
||||
"delete_account_feedback_label": "",
|
||||
"delete_account_feedback_placeholder": "",
|
||||
"delete_account_confirm_checkbox_label": "",
|
||||
"delete_account_confirm": "",
|
||||
"delete_account_confirm_message": "",
|
||||
"feedback_required": "",
|
||||
"feedback_required_found_another_service": "",
|
||||
"RECOVER_TWO_FACTOR": "",
|
||||
"at": "",
|
||||
"AUTH_NEXT": "",
|
||||
"AUTH_DOWNLOAD_MOBILE_APP": "",
|
||||
"HIDE": "",
|
||||
"UNHIDE": "",
|
||||
"sort_by": "",
|
||||
"newest_first": "",
|
||||
"oldest_first": "",
|
||||
"CONVERSION_FAILED_NOTIFICATION_MESSAGE": "",
|
||||
"pin_album": "",
|
||||
"unpin_album": "",
|
||||
"DOWNLOAD_COMPLETE": "",
|
||||
"DOWNLOADING_COLLECTION": "",
|
||||
"DOWNLOAD_FAILED": "",
|
||||
"DOWNLOAD_PROGRESS": "",
|
||||
"CHRISTMAS": "",
|
||||
"CHRISTMAS_EVE": "",
|
||||
"NEW_YEAR": "",
|
||||
"NEW_YEAR_EVE": "",
|
||||
"IMAGE": "",
|
||||
"VIDEO": "",
|
||||
"LIVE_PHOTO": "",
|
||||
"editor": {
|
||||
"crop": ""
|
||||
},
|
||||
"CONVERT": "",
|
||||
"CONFIRM_EDITOR_CLOSE_MESSAGE": "",
|
||||
"CONFIRM_EDITOR_CLOSE_DESCRIPTION": "",
|
||||
"BRIGHTNESS": "",
|
||||
"CONTRAST": "",
|
||||
"SATURATION": "",
|
||||
"BLUR": "",
|
||||
"INVERT_COLORS": "",
|
||||
"ASPECT_RATIO": "",
|
||||
"SQUARE": "",
|
||||
"ROTATE_LEFT": "",
|
||||
"ROTATE_RIGHT": "",
|
||||
"FLIP_VERTICALLY": "",
|
||||
"FLIP_HORIZONTALLY": "",
|
||||
"DOWNLOAD_EDITED": "",
|
||||
"SAVE_A_COPY_TO_ENTE": "",
|
||||
"RESTORE_ORIGINAL": "",
|
||||
"TRANSFORM": "",
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
"cast_album_to_tv": "",
|
||||
"enter_cast_pin_code": "",
|
||||
"pair_device_to_tv": "",
|
||||
"tv_not_found": "",
|
||||
"cast_auto_pair": "",
|
||||
"cast_auto_pair_description": "",
|
||||
"choose_device_from_browser": "",
|
||||
"cast_auto_pair_failed": "",
|
||||
"pair_with_pin": "",
|
||||
"pair_with_pin_description": "",
|
||||
"visit_cast_url": "",
|
||||
"FREEHAND": "",
|
||||
"APPLY_CROP": "",
|
||||
"PHOTO_EDIT_REQUIRED_TO_SAVE": "",
|
||||
"passkeys": "",
|
||||
"passkey_fetch_failed": "",
|
||||
"manage_passkey": "",
|
||||
"delete_passkey": "",
|
||||
"delete_passkey_confirmation": "",
|
||||
"rename_passkey": "",
|
||||
"add_passkey": "",
|
||||
"enter_passkey_name": "",
|
||||
"passkeys_description": "",
|
||||
"CREATED_AT": "",
|
||||
"passkey_add_failed": "",
|
||||
"passkey_login_failed": "",
|
||||
"passkey_login_invalid_url": "",
|
||||
"passkey_login_already_claimed_session": "",
|
||||
"passkey_login_generic_error": "",
|
||||
"passkey_login_credential_hint": "",
|
||||
"passkeys_not_supported": "",
|
||||
"try_again": "",
|
||||
"check_status": "",
|
||||
"passkey_login_instructions": "",
|
||||
"passkey_login": "",
|
||||
"passkey": "",
|
||||
"passkey_verify_description": "",
|
||||
"waiting_for_verification": "",
|
||||
"verification_still_pending": "",
|
||||
"passkey_verified": "",
|
||||
"redirecting_back_to_app": "",
|
||||
"redirect_close_instructions": "",
|
||||
"redirect_again": "",
|
||||
"autogenerated_first_album_name": "",
|
||||
"autogenerated_default_album_name": "",
|
||||
"developer_settings": "",
|
||||
"server_endpoint": "",
|
||||
"more_information": "",
|
||||
"save": ""
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Private Sicherungen</div><div>für deine Erinnerungen</div>",
|
||||
"HERO_SLIDE_1": "Standardmäßig Ende-zu-Ende verschlüsselt",
|
||||
"HERO_SLIDE_2_TITLE": "<div>Sicher gespeichert</div><div>in einem Luftschutzbunker</div>",
|
||||
"HERO_SLIDE_2": "Entwickelt, um zu überleben",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Überall</div><div> verfügbar</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, Web, Desktop",
|
||||
"intro_slide_1_title": "<div>Private Sicherungen</div><div>für deine Erinnerungen</div>",
|
||||
"intro_slide_1": "Standardmäßig Ende-zu-Ende verschlüsselt",
|
||||
"intro_slide_2_title": "<div>Sicher gespeichert</div><div>in einem Luftschutzbunker</div>",
|
||||
"intro_slide_2": "Entwickelt, um zu überleben",
|
||||
"intro_slide_3_title": "<div>Überall</div><div> verfügbar</div>",
|
||||
"intro_slide_3": "Android, iOS, Web, Desktop",
|
||||
"login": "Anmelden",
|
||||
"sign_up": "Registrieren",
|
||||
"NEW_USER": "Neu bei Ente",
|
||||
"EXISTING_USER": "Existierender Benutzer",
|
||||
"ENTER_NAME": "Name eingeben",
|
||||
"enter_name": "Name eingeben",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Füge einen Namen hinzu, damit deine Freunde wissen, wem sie für diese tollen Fotos zu danken haben!",
|
||||
"ENTER_EMAIL": "E-Mail-Adresse eingeben",
|
||||
"EMAIL_ERROR": "Geben Sie eine gültige E-Mail-Adresse ein",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "Alben erstellen",
|
||||
"CREATE_COLLECTION": "Neues Album",
|
||||
"enter_album_name": "Albumname",
|
||||
"CLOSE_OPTION": "Schließen (Esc)",
|
||||
"close_key": "Schließen (Esc)",
|
||||
"enter_file_name": "Dateiname",
|
||||
"CLOSE": "Schließen",
|
||||
"NO": "Nein",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Schließen",
|
||||
"no": "Nein",
|
||||
"nothing_here": "",
|
||||
"upload": "Hochladen",
|
||||
"import": "Importieren",
|
||||
"add_photos": "Fotos hinzufügen",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "Passwort anderswo geändert",
|
||||
"password_changed_elsewhere_message": "Bitte melde dich erneut auf diesem Gerät an, um dein neues Passwort zur Authentifizierung zu verwenden.",
|
||||
"GO_BACK": "Zurück",
|
||||
"RECOVERY_KEY": "Wiederherstellungsschlüssel",
|
||||
"SAVE_LATER": "Auf später verschieben",
|
||||
"SAVE": "Schlüssel speichern",
|
||||
"recovery_key": "Wiederherstellungsschlüssel",
|
||||
"do_this_later": "Auf später verschieben",
|
||||
"save_key": "Schlüssel speichern",
|
||||
"RECOVERY_KEY_DESCRIPTION": "Falls du dein Passwort vergisst, kannst du deine Daten nur mit diesem Schlüssel wiederherstellen.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "Wiederherstellungsschlüssel konnte nicht generiert werden, bitte versuche es erneut",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "Wir speichern diesen Schlüssel nicht, also speichere ihn bitte an einem sicheren Ort",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Wiederherstellen",
|
||||
"NO_RECOVERY_KEY": "Kein Wiederherstellungsschlüssel?",
|
||||
"INCORRECT_RECOVERY_KEY": "Falscher Wiederherstellungs-Schlüssel",
|
||||
"SORRY": "Entschuldigung",
|
||||
"sorry": "Entschuldigung",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "Aufgrund unseres Ende-zu-Ende-Verschlüsselungsprotokolls können Ihre Daten nicht ohne Ihr Passwort oder Ihren Wiederherstellungsschlüssel entschlüsselt werden",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Bitte sende eine E-Mail an <a>{{emailID}}</a> von deiner registrierten E-Mail-Adresse",
|
||||
"CONTACT_SUPPORT": "Support kontaktieren",
|
||||
"REQUEST_FEATURE": "Feature anfragen",
|
||||
"SUPPORT": "Support",
|
||||
"CONFIRM": "Bestätigen",
|
||||
"contact_support": "Support kontaktieren",
|
||||
"request_feature": "Feature anfragen",
|
||||
"support": "Support",
|
||||
"cancel": "Abbrechen",
|
||||
"LOGOUT": "Ausloggen",
|
||||
"logout": "Ausloggen",
|
||||
"logout_message": "Sind sie sicher, dass sie sich ausloggen möchten?",
|
||||
"delete_account": "Konto löschen",
|
||||
"delete_account_manually_message": "<p>Bitte sende eine E-Mail an <a>{{emailID}}</a> mit deiner registrierten E-Mail-Adresse.</p><p>Deine Anfrage wird innerhalb von 72 Stunden bearbeitet.</p>",
|
||||
"LOGOUT_MESSAGE": "Sind sie sicher, dass sie sich ausloggen möchten?",
|
||||
"CHANGE_EMAIL": "E-Mail-Adresse ändern",
|
||||
"ok": "OK",
|
||||
"success": "Erfolgreich",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "Album ausblenden",
|
||||
"unhide_collection": "Album wieder einblenden",
|
||||
"MOVE": "Verschieben",
|
||||
"ADD": "Hinzufügen",
|
||||
"add": "Hinzufügen",
|
||||
"REMOVE": "Entfernen",
|
||||
"YES_REMOVE": "Ja, entfernen",
|
||||
"REMOVE_FROM_COLLECTION": "Aus Album entfernen",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Farben",
|
||||
"FLIP": "Spiegeln",
|
||||
"ROTATION": "Drehen",
|
||||
"RESET": "Zurücksetzen",
|
||||
"reset": "Zurücksetzen",
|
||||
"PHOTO_EDITOR": "Foto-Editor",
|
||||
"FASTER_UPLOAD": "Schnelleres Hochladen",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "Uploads über nahegelegene Server leiten",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Ιδιωτικά αντίγραφα ασφαλείας</div><div>για τις αναμνήσεις σας</div>",
|
||||
"HERO_SLIDE_1": "Από προεπιλογή κρυπτογραφημένο από άκρο σε άκρο",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "Σχεδιάστηκε για να επιζήσει",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Διαθέσιμο</div><div> παντού</div>",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "<div>Ιδιωτικά αντίγραφα ασφαλείας</div><div>για τις αναμνήσεις σας</div>",
|
||||
"intro_slide_1": "Από προεπιλογή κρυπτογραφημένο από άκρο σε άκρο",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "Σχεδιάστηκε για να επιζήσει",
|
||||
"intro_slide_3_title": "<div>Διαθέσιμο</div><div> παντού</div>",
|
||||
"intro_slide_3": "",
|
||||
"login": "Σύνδεση",
|
||||
"sign_up": "Εγγραφή",
|
||||
"NEW_USER": "Νέος/α στο Ente",
|
||||
"EXISTING_USER": "Υπάρχων χρήστης",
|
||||
"ENTER_NAME": "Εισάγετε όνομα",
|
||||
"enter_name": "Εισάγετε όνομα",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Προσθέστε ένα όνομα, ώστε οι φίλοι σας να γνωρίζουν ποιον να ευχαριστήσουν για αυτές τις υπέροχες φωτογραφίες!",
|
||||
"ENTER_EMAIL": "Εισάγετε διεύθυνση ηλ. ταχυδρομείου",
|
||||
"EMAIL_ERROR": "Εισάγετε μία έγκυρη διεύθυνση ηλ. ταχυδρομείου",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "Νέο άλμπουμ",
|
||||
"enter_album_name": "Όνομα άλμπουμ",
|
||||
"CLOSE_OPTION": "Κλείσιμο (Esc)",
|
||||
"close_key": "Κλείσιμο (Esc)",
|
||||
"enter_file_name": "Όνομα αρχείου",
|
||||
"CLOSE": "Κλείσιμο",
|
||||
"NO": "Όχι",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Κλείσιμο",
|
||||
"no": "Όχι",
|
||||
"nothing_here": "",
|
||||
"upload": "Μεταφόρτωση",
|
||||
"import": "Εισαγωγή",
|
||||
"add_photos": "Προσθήκη φωτογραφιών",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "Παρακαλώ συνδεθείτε ξανά σε αυτήν τη συσκευή για να χρησιμοποιήσετε το νέο σας κωδικό πρόσβασης για αυθεντικοποίηση.",
|
||||
"GO_BACK": "Επιστροφή",
|
||||
"RECOVERY_KEY": "Κλειδί ανάκτησης",
|
||||
"SAVE_LATER": "Κάντε το αργότερα",
|
||||
"SAVE": "Αποθήκευση Κλειδιού",
|
||||
"recovery_key": "Κλειδί ανάκτησης",
|
||||
"do_this_later": "Κάντε το αργότερα",
|
||||
"save_key": "Αποθήκευση Κλειδιού",
|
||||
"RECOVERY_KEY_DESCRIPTION": "Εάν ξεχάσετε τον κωδικό πρόσβασής σας, ο μόνος τρόπος για να ανακτήσετε τα δεδομένα σας είναι με αυτό το κλειδί.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "Δεν ήταν δυνατή η δημιουργία κωδικού ανάκτησης, παρακαλώ προσπαθήστε ξανά",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "Δεν αποθηκεύουμε αυτό το κλειδί, οπότε παρακαλώ αποθηκεύστε αυτό το κλειδί σε μια ασφαλή τοποθεσία",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Ανάκτηση",
|
||||
"NO_RECOVERY_KEY": "Χωρίς κλειδί ανάκτησης;",
|
||||
"INCORRECT_RECOVERY_KEY": "Εσφαλμένο κλειδί ανάκτησης",
|
||||
"SORRY": "Συγνώμη",
|
||||
"sorry": "Συγνώμη",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Παρακαλώ αφήστε ένα μήνυμα ηλ. ταχυδρομείου στο <a>{{emailID}}</a> από την καταχωρημένη διεύθυνση σας",
|
||||
"CONTACT_SUPPORT": "Επικοινωνήστε με την υποστήριξη",
|
||||
"REQUEST_FEATURE": "Αίτηση Λειτουργίας",
|
||||
"SUPPORT": "Υποστήριξη",
|
||||
"CONFIRM": "Επιβεβαίωση",
|
||||
"contact_support": "Επικοινωνήστε με την υποστήριξη",
|
||||
"request_feature": "Αίτηση Λειτουργίας",
|
||||
"support": "Υποστήριξη",
|
||||
"cancel": "Ακύρωση",
|
||||
"LOGOUT": "Αποσυνδέση",
|
||||
"logout": "Αποσυνδέση",
|
||||
"logout_message": "Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε;",
|
||||
"delete_account": "Διαγραφή λογαριασμού",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε;",
|
||||
"CHANGE_EMAIL": "Αλλαγή διεύθυνσης ηλ. ταχυδρομείου",
|
||||
"ok": "ΟΚ",
|
||||
"success": "Επιτυχία",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "Απόκρυψη άλμπουμ",
|
||||
"unhide_collection": "Επανεμφάνιση άλμπουμ",
|
||||
"MOVE": "Μετακίνηση",
|
||||
"ADD": "Προσθήκη",
|
||||
"add": "Προσθήκη",
|
||||
"REMOVE": "Αφαίρεση",
|
||||
"YES_REMOVE": "Ναι, αφαίρεση",
|
||||
"REMOVE_FROM_COLLECTION": "Αφαίρεση από το άλμπουμ",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Χρώματα",
|
||||
"FLIP": "",
|
||||
"ROTATION": "Περιστροφή",
|
||||
"RESET": "Επαναφορά",
|
||||
"reset": "Επαναφορά",
|
||||
"PHOTO_EDITOR": "Επεξεργαστής φωτογραφιών",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Private backups</div><div>for your memories</div>",
|
||||
"HERO_SLIDE_1": "End-to-end encrypted by default",
|
||||
"HERO_SLIDE_2_TITLE": "<div>Safely stored</div><div>at a fallout shelter</div>",
|
||||
"HERO_SLIDE_2": "Designed to outlive",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Available</div><div> everywhere</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, Web, Desktop",
|
||||
"intro_slide_1_title": "<div>Private backups</div><div>for your memories</div>",
|
||||
"intro_slide_1": "End-to-end encrypted by default",
|
||||
"intro_slide_2_title": "<div>Safely stored</div><div>at a fallout shelter</div>",
|
||||
"intro_slide_2": "Designed to outlive",
|
||||
"intro_slide_3_title": "<div>Available</div><div> everywhere</div>",
|
||||
"intro_slide_3": "Android, iOS, Web, Desktop",
|
||||
"login": "Login",
|
||||
"sign_up": "Signup",
|
||||
"NEW_USER": "New to Ente",
|
||||
"EXISTING_USER": "Existing user",
|
||||
"ENTER_NAME": "Enter name",
|
||||
"enter_name": "Enter name",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Add a name so that your friends know who to thank for these great photos!",
|
||||
"ENTER_EMAIL": "Enter email address",
|
||||
"EMAIL_ERROR": "Enter a valid email",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "Create albums",
|
||||
"CREATE_COLLECTION": "New album",
|
||||
"enter_album_name": "Album name",
|
||||
"CLOSE_OPTION": "Close (Esc)",
|
||||
"close_key": "Close (Esc)",
|
||||
"enter_file_name": "File name",
|
||||
"CLOSE": "Close",
|
||||
"NO": "No",
|
||||
"NOTHING_HERE": "Nothing here yet",
|
||||
"close": "Close",
|
||||
"no": "No",
|
||||
"nothing_here": "Nothing here yet",
|
||||
"upload": "Upload",
|
||||
"import": "Import",
|
||||
"add_photos": "Add photos",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "Password changed elsewhere",
|
||||
"password_changed_elsewhere_message": "Please login again on this device to use your new password to authenticate.",
|
||||
"GO_BACK": "Go back",
|
||||
"RECOVERY_KEY": "Recovery key",
|
||||
"SAVE_LATER": "Do this later",
|
||||
"SAVE": "Save Key",
|
||||
"recovery_key": "Recovery key",
|
||||
"do_this_later": "Do this later",
|
||||
"save_key": "Save Key",
|
||||
"RECOVERY_KEY_DESCRIPTION": "If you forget your password, the only way you can recover your data is with this key.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "Recovery code could not be generated, please try again",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "We don't store this key, so please save this in a safe place",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Recover",
|
||||
"NO_RECOVERY_KEY": "No recovery key?",
|
||||
"INCORRECT_RECOVERY_KEY": "Incorrect recovery key",
|
||||
"SORRY": "Sorry",
|
||||
"sorry": "Sorry",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "Due to the nature of our end-to-end encryption protocol, your data cannot be decrypted without your password or recovery key",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Please drop an email to <a>{{emailID}}</a> from your registered email address",
|
||||
"CONTACT_SUPPORT": "Contact support",
|
||||
"REQUEST_FEATURE": "Request Feature",
|
||||
"SUPPORT": "Support",
|
||||
"CONFIRM": "Confirm",
|
||||
"contact_support": "Contact support",
|
||||
"request_feature": "Request Feature",
|
||||
"support": "Support",
|
||||
"cancel": "Cancel",
|
||||
"LOGOUT": "Logout",
|
||||
"logout": "Logout",
|
||||
"logout_message": "Are you sure you want to logout?",
|
||||
"delete_account": "Delete account",
|
||||
"delete_account_manually_message": "<p>Please send an email to <a>{{emailID}}</a> from your registered email address.</p><p>Your request will be processed within 72 hours.</p>",
|
||||
"LOGOUT_MESSAGE": "Are you sure you want to logout?",
|
||||
"CHANGE_EMAIL": "Change email",
|
||||
"ok": "OK",
|
||||
"success": "Success",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "Hide album",
|
||||
"unhide_collection": "Unhide album",
|
||||
"MOVE": "Move",
|
||||
"ADD": "Add",
|
||||
"add": "Add",
|
||||
"REMOVE": "Remove",
|
||||
"YES_REMOVE": "Yes, remove",
|
||||
"REMOVE_FROM_COLLECTION": "Remove from album",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Colors",
|
||||
"FLIP": "Flip",
|
||||
"ROTATION": "Rotation",
|
||||
"RESET": "Reset",
|
||||
"reset": "Reset",
|
||||
"PHOTO_EDITOR": "Photo Editor",
|
||||
"FASTER_UPLOAD": "Faster uploads",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "Route uploads through nearby servers",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Copias de seguridad privadas</div><div>para su recuerdos</div>",
|
||||
"HERO_SLIDE_1": "Encriptado de extremo a extremo por defecto",
|
||||
"HERO_SLIDE_2_TITLE": "<div>Almacenado de forma segura</div><div>en un refugio de llenos</div>",
|
||||
"HERO_SLIDE_2": "Diseñado para superar",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Disponible</div><div> en todas partes</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, web, computadora",
|
||||
"intro_slide_1_title": "<div>Copias de seguridad privadas</div><div>para su recuerdos</div>",
|
||||
"intro_slide_1": "Encriptado de extremo a extremo por defecto",
|
||||
"intro_slide_2_title": "<div>Almacenado de forma segura</div><div>en un refugio de llenos</div>",
|
||||
"intro_slide_2": "Diseñado para superar",
|
||||
"intro_slide_3_title": "<div>Disponible</div><div> en todas partes</div>",
|
||||
"intro_slide_3": "Android, iOS, web, computadora",
|
||||
"login": "Conectar",
|
||||
"sign_up": "Registro",
|
||||
"NEW_USER": "Nuevo en Ente",
|
||||
"EXISTING_USER": "Usuario existente",
|
||||
"ENTER_NAME": "Introducir nombre",
|
||||
"enter_name": "Introducir nombre",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "¡Añade un nombre para que tus amigos sepan a quién dar las gracias por estas fotos geniales!",
|
||||
"ENTER_EMAIL": "Introducir email",
|
||||
"EMAIL_ERROR": "Introduce un email válido",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "Nuevo álbum",
|
||||
"enter_album_name": "Nombre del álbum",
|
||||
"CLOSE_OPTION": "Cerrar (Esc)",
|
||||
"close_key": "Cerrar (Esc)",
|
||||
"enter_file_name": "Nombre del archivo",
|
||||
"CLOSE": "Cerrar",
|
||||
"NO": "No",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Cerrar",
|
||||
"no": "No",
|
||||
"nothing_here": "",
|
||||
"upload": "Cargar",
|
||||
"import": "Importar",
|
||||
"add_photos": "Añadir fotos",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "Retroceder",
|
||||
"RECOVERY_KEY": "Clave de recuperación",
|
||||
"SAVE_LATER": "Hacer más tarde",
|
||||
"SAVE": "Guardar Clave",
|
||||
"recovery_key": "Clave de recuperación",
|
||||
"do_this_later": "Hacer más tarde",
|
||||
"save_key": "Guardar Clave",
|
||||
"RECOVERY_KEY_DESCRIPTION": "Si olvida su contraseña, la única forma de recuperar sus datos es con esta clave.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "El código de recuperación no pudo ser generado, por favor inténtalo de nuevo",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "No almacenamos esta clave, así que por favor guarde esto en un lugar seguro",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Recuperar",
|
||||
"NO_RECOVERY_KEY": "No hay clave de recuperación?",
|
||||
"INCORRECT_RECOVERY_KEY": "Clave de recuperación incorrecta",
|
||||
"SORRY": "Lo sentimos",
|
||||
"sorry": "Lo sentimos",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "Debido a la naturaleza de nuestro protocolo de cifrado de extremo a extremo, sus datos no pueden ser descifrados sin su contraseña o clave de recuperación",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Por favor, envíe un email a <a>{{emailID}}</a> desde su dirección de correo electrónico registrada",
|
||||
"CONTACT_SUPPORT": "Contacta con soporte",
|
||||
"REQUEST_FEATURE": "Solicitar una función",
|
||||
"SUPPORT": "Soporte",
|
||||
"CONFIRM": "Confirmar",
|
||||
"contact_support": "Contacta con soporte",
|
||||
"request_feature": "Solicitar una función",
|
||||
"support": "Soporte",
|
||||
"cancel": "Cancelar",
|
||||
"LOGOUT": "Cerrar sesión",
|
||||
"logout": "Cerrar sesión",
|
||||
"logout_message": "Seguro que quiere cerrar la sesión?",
|
||||
"delete_account": "Eliminar cuenta",
|
||||
"delete_account_manually_message": "<p>Por favor, envíe un email a <a>{{emailID}}</a> desde su dirección de correo electrónico registrada</p><p>Su solicitud será procesada en 72 horas.</p>",
|
||||
"LOGOUT_MESSAGE": "Seguro que quiere cerrar la sesión?",
|
||||
"CHANGE_EMAIL": "Cambiar email",
|
||||
"ok": "OK",
|
||||
"success": "Completado",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "Mover",
|
||||
"ADD": "Añadir",
|
||||
"add": "Añadir",
|
||||
"REMOVE": "Eliminar",
|
||||
"YES_REMOVE": "Sí, eliminar",
|
||||
"REMOVE_FROM_COLLECTION": "Eliminar del álbum",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Colores",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Yksityiset varmuuskopiot</div><div>muistoillesi</div>",
|
||||
"HERO_SLIDE_1": "Päästä päähän -salaus käytössä oletuksena",
|
||||
"HERO_SLIDE_2_TITLE": "<div>Turvallisesti varastoitu</div><div>väestönsuojan tiloissa</div>",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Saatavilla</div><div> kaikkialla</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, Web, Tietokone",
|
||||
"intro_slide_1_title": "<div>Yksityiset varmuuskopiot</div><div>muistoillesi</div>",
|
||||
"intro_slide_1": "Päästä päähän -salaus käytössä oletuksena",
|
||||
"intro_slide_2_title": "<div>Turvallisesti varastoitu</div><div>väestönsuojan tiloissa</div>",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "<div>Saatavilla</div><div> kaikkialla</div>",
|
||||
"intro_slide_3": "Android, iOS, Web, Tietokone",
|
||||
"login": "Kirjaudu sisään",
|
||||
"sign_up": "Rekisteröidy",
|
||||
"NEW_USER": "Uusi Ente-käyttäjä",
|
||||
"EXISTING_USER": "Jo valmiiksi olemassaoleva käyttäjä",
|
||||
"ENTER_NAME": "Lisää nimi",
|
||||
"enter_name": "Lisää nimi",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Lisää nimi, jotta ystäväsi tietävät, ketä kiittää näistä hienoista kuvista!",
|
||||
"ENTER_EMAIL": "Syötä sähköpostiosoite",
|
||||
"EMAIL_ERROR": "Syötä voimassa oleva sähköpostiosoite",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "Uusi albumi",
|
||||
"enter_album_name": "Albumin nimi",
|
||||
"CLOSE_OPTION": "Sulje (Esc)",
|
||||
"close_key": "Sulje (Esc)",
|
||||
"enter_file_name": "Tiedoston nimi",
|
||||
"CLOSE": "Sulje",
|
||||
"NO": "Ei",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Sulje",
|
||||
"no": "Ei",
|
||||
"nothing_here": "",
|
||||
"upload": "Lataa",
|
||||
"import": "Tuo",
|
||||
"add_photos": "Lisää kuvia",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Sauvegardes privées</div><div>pour vos souvenirs</div>",
|
||||
"HERO_SLIDE_1": "Chiffrement de bout en bout par défaut",
|
||||
"HERO_SLIDE_2_TITLE": "<div>Sécurisé </div><div>dans un abri antiatomique</div>",
|
||||
"HERO_SLIDE_2": "Conçu pour survivre",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Disponible</div><div> en tout lieu</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, Web, Ordinateur",
|
||||
"intro_slide_1_title": "<div>Sauvegardes privées</div><div>pour vos souvenirs</div>",
|
||||
"intro_slide_1": "Chiffrement de bout en bout par défaut",
|
||||
"intro_slide_2_title": "<div>Sécurisé </div><div>dans un abri antiatomique</div>",
|
||||
"intro_slide_2": "Conçu pour survivre",
|
||||
"intro_slide_3_title": "<div>Disponible</div><div> en tout lieu</div>",
|
||||
"intro_slide_3": "Android, iOS, Web, Ordinateur",
|
||||
"login": "Connexion",
|
||||
"sign_up": "Inscription",
|
||||
"NEW_USER": "Nouveau sur Ente",
|
||||
"EXISTING_USER": "Utilisateur existant",
|
||||
"ENTER_NAME": "Saisir un nom",
|
||||
"enter_name": "Saisir un nom",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Ajouter un nom afin que vos amis sachent qui remercier pour ces magnifiques photos!",
|
||||
"ENTER_EMAIL": "Saisir l'adresse e-mail",
|
||||
"EMAIL_ERROR": "Saisir un e-mail valide",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "Créer des albums",
|
||||
"CREATE_COLLECTION": "Nouvel album",
|
||||
"enter_album_name": "Nom de l'album",
|
||||
"CLOSE_OPTION": "Fermer (Échap)",
|
||||
"close_key": "Fermer (Échap)",
|
||||
"enter_file_name": "Nom du fichier",
|
||||
"CLOSE": "Fermer",
|
||||
"NO": "Non",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Fermer",
|
||||
"no": "Non",
|
||||
"nothing_here": "",
|
||||
"upload": "Charger",
|
||||
"import": "Importer",
|
||||
"add_photos": "Ajouter des photos",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "Mot de passe modifié ailleurs",
|
||||
"password_changed_elsewhere_message": "Veuillez vous reconnecter sur cet appareil pour utiliser votre nouveau mot de passe pour vous authentifier.",
|
||||
"GO_BACK": "Retour",
|
||||
"RECOVERY_KEY": "Clé de récupération",
|
||||
"SAVE_LATER": "Plus tard",
|
||||
"SAVE": "Sauvegarder la clé",
|
||||
"recovery_key": "Clé de récupération",
|
||||
"do_this_later": "Plus tard",
|
||||
"save_key": "Sauvegarder la clé",
|
||||
"RECOVERY_KEY_DESCRIPTION": "Si vous oubliez votre mot de passe, la seule façon de récupérer vos données sera grâce à cette clé.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "Le code de récupération ne peut être généré, veuillez réessayer",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "Nous ne stockons pas cette clé, veuillez donc la sauvegarder dans un endroit sûr",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Récupérer",
|
||||
"NO_RECOVERY_KEY": "Pas de clé de récupération?",
|
||||
"INCORRECT_RECOVERY_KEY": "Clé de récupération non valide",
|
||||
"SORRY": "Désolé",
|
||||
"sorry": "Désolé",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "En raison de notre protocole de chiffrement de bout en bout, vos données ne peuvent être décryptées sans votre mot de passe ou clé de récupération",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Veuillez envoyer un e-mail à <a>{{emailID}}</a> depuis votre adresse enregistrée",
|
||||
"CONTACT_SUPPORT": "Contacter le support",
|
||||
"REQUEST_FEATURE": "Soumettre une idée",
|
||||
"SUPPORT": "Support",
|
||||
"CONFIRM": "Confirmer",
|
||||
"contact_support": "Contacter le support",
|
||||
"request_feature": "Soumettre une idée",
|
||||
"support": "Support",
|
||||
"cancel": "Annuler",
|
||||
"LOGOUT": "Déconnexion",
|
||||
"logout": "Déconnexion",
|
||||
"logout_message": "Voulez-vous vraiment vous déconnecter?",
|
||||
"delete_account": "Supprimer le compte",
|
||||
"delete_account_manually_message": "<p>Veuillez envoyer un e-mail à <a>{{emailID}}</a>depuis Votre adresse enregistrée.</p><p> Votre demande sera traitée dans les 72 heures.</p>",
|
||||
"LOGOUT_MESSAGE": "Voulez-vous vraiment vous déconnecter?",
|
||||
"CHANGE_EMAIL": "Modifier l'e-mail",
|
||||
"ok": "Ok",
|
||||
"success": "Parfait",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "Masquer l'album",
|
||||
"unhide_collection": "Dévoiler l'album",
|
||||
"MOVE": "Déplacer",
|
||||
"ADD": "Ajouter",
|
||||
"add": "Ajouter",
|
||||
"REMOVE": "Retirer",
|
||||
"YES_REMOVE": "Oui, retirer",
|
||||
"REMOVE_FROM_COLLECTION": "Retirer de l'album",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Couleurs",
|
||||
"FLIP": "Retourner",
|
||||
"ROTATION": "Rotation",
|
||||
"RESET": "Réinitialiser",
|
||||
"reset": "Réinitialiser",
|
||||
"PHOTO_EDITOR": "Éditeur de photos",
|
||||
"FASTER_UPLOAD": "Chargements plus rapides",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "Router les chargements vers les serveurs à proximité",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Cadangan pribadi</div><div>untuk kenanganmu</div>",
|
||||
"HERO_SLIDE_1": "Dirancang dengan enkripsi ujung ke ujung",
|
||||
"HERO_SLIDE_2_TITLE": "<div>Tersimpan aman</div><div>di tempat pengungsian</div>",
|
||||
"HERO_SLIDE_2": "Dibuat untuk melestarikan",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Tersedia</div><div> di mana saja</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, Web, Desktop",
|
||||
"intro_slide_1_title": "<div>Cadangan pribadi</div><div>untuk kenanganmu</div>",
|
||||
"intro_slide_1": "Dirancang dengan enkripsi ujung ke ujung",
|
||||
"intro_slide_2_title": "<div>Tersimpan aman</div><div>di tempat pengungsian</div>",
|
||||
"intro_slide_2": "Dibuat untuk melestarikan",
|
||||
"intro_slide_3_title": "<div>Tersedia</div><div> di mana saja</div>",
|
||||
"intro_slide_3": "Android, iOS, Web, Desktop",
|
||||
"login": "Masuk",
|
||||
"sign_up": "Daftar",
|
||||
"NEW_USER": "Baru di Ente",
|
||||
"EXISTING_USER": "Pengguna yang sudah ada",
|
||||
"ENTER_NAME": "Masukkan nama",
|
||||
"enter_name": "Masukkan nama",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Tambahkan nama agar teman Anda tahu kepada siapa harus berterima kasih atas foto-foto hebat ini!",
|
||||
"ENTER_EMAIL": "Masukkan alamat email",
|
||||
"EMAIL_ERROR": "Masukkan email yang sah",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "Album baru",
|
||||
"enter_album_name": "Nama album",
|
||||
"CLOSE_OPTION": "Tutup (Esc)",
|
||||
"close_key": "Tutup (Esc)",
|
||||
"enter_file_name": "Nama file",
|
||||
"CLOSE": "Tutup",
|
||||
"NO": "Tidak",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Tutup",
|
||||
"no": "Tidak",
|
||||
"nothing_here": "",
|
||||
"upload": "Unggah",
|
||||
"import": "Impor",
|
||||
"add_photos": "Tambahkan foto",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "Kembali",
|
||||
"RECOVERY_KEY": "Kunci pemulihan",
|
||||
"SAVE_LATER": "Lakukan lain kali",
|
||||
"SAVE": "Simpan Kunci",
|
||||
"recovery_key": "Kunci pemulihan",
|
||||
"do_this_later": "Lakukan lain kali",
|
||||
"save_key": "Simpan Kunci",
|
||||
"RECOVERY_KEY_DESCRIPTION": "Jika Anda lupa kata sandi, satu-satunya cara memulihkan data Anda adalah dengan kunci ini.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "Tidak dapat menghasilkan kode pemulihan, silakan coba lagi",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "Kami tidak menyimpan kunci ini, jadi harap simpan kunci ini dengan aman",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Pulihkan",
|
||||
"NO_RECOVERY_KEY": "Tidak punya kunci pemulihan?",
|
||||
"INCORRECT_RECOVERY_KEY": "Kunci pemulihan salah",
|
||||
"SORRY": "Maaf",
|
||||
"sorry": "Maaf",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "Karena sifat protokol enkripsi ujung ke ujung kami, data kamu tidak dapat didekripsi tanpa sandi atau kunci pemulihan kamu",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Silakan kirimkan email ke <a>{{emailID}}</a> dari alamat email terdaftar kamu",
|
||||
"CONTACT_SUPPORT": "Hubungi dukungan",
|
||||
"REQUEST_FEATURE": "Minta Fitur",
|
||||
"SUPPORT": "Dukungan",
|
||||
"CONFIRM": "Konfirmasi",
|
||||
"contact_support": "Hubungi dukungan",
|
||||
"request_feature": "Minta Fitur",
|
||||
"support": "Dukungan",
|
||||
"cancel": "Batal",
|
||||
"LOGOUT": "Keluar akun",
|
||||
"logout": "Keluar akun",
|
||||
"logout_message": "Apakah kamu yakin ingin keluar akun?",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "Apakah kamu yakin ingin keluar akun?",
|
||||
"CHANGE_EMAIL": "Ubah email",
|
||||
"ok": "OK",
|
||||
"success": "Berhasil",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "Sembunyikan album",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "Pindahkan",
|
||||
"ADD": "Tambah",
|
||||
"add": "Tambah",
|
||||
"REMOVE": "Hapus",
|
||||
"YES_REMOVE": "Ya, hapus",
|
||||
"REMOVE_FROM_COLLECTION": "Hapus dari album",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Warna",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "Editor Foto",
|
||||
"FASTER_UPLOAD": "Pengunggahan lebih cepat",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "Loka",
|
||||
"NO": "Nei",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Loka",
|
||||
"no": "Nei",
|
||||
"nothing_here": "",
|
||||
"upload": "Hlaða upp",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "Fara til baka",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "Gera þetta seinna",
|
||||
"SAVE": "Vista Lykil",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "Gera þetta seinna",
|
||||
"save_key": "Vista Lykil",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Endurheimta",
|
||||
"NO_RECOVERY_KEY": "Enginn endurheimtunarlykill?",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "Fyrirgefðu",
|
||||
"sorry": "Fyrirgefðu",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "Staðfesta",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "Hætta við",
|
||||
"LOGOUT": "Útskrá",
|
||||
"logout": "Útskrá",
|
||||
"logout_message": "Ertu viss um að þú viljir skrá þig út?",
|
||||
"delete_account": "Eyða aðgangi",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "Ertu viss um að þú viljir skrá þig út?",
|
||||
"CHANGE_EMAIL": "Breyta netfangi",
|
||||
"ok": "Í lagi",
|
||||
"success": "Tókst",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Backup privati</div><div>dei tuoi ricordi</div>",
|
||||
"HERO_SLIDE_1": "Crittografia end-to-end",
|
||||
"HERO_SLIDE_2_TITLE": "<div>Salvati in modo sicuro</div><div>in un rifugio antiatomico</div>",
|
||||
"HERO_SLIDE_2": "Progettato per sopravvivere",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Disponibile</div><div> ovunque</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, Web, Desktop",
|
||||
"intro_slide_1_title": "<div>Backup privati</div><div>dei tuoi ricordi</div>",
|
||||
"intro_slide_1": "Crittografia end-to-end",
|
||||
"intro_slide_2_title": "<div>Salvati in modo sicuro</div><div>in un rifugio antiatomico</div>",
|
||||
"intro_slide_2": "Progettato per sopravvivere",
|
||||
"intro_slide_3_title": "<div>Disponibile</div><div> ovunque</div>",
|
||||
"intro_slide_3": "Android, iOS, Web, Desktop",
|
||||
"login": "Accedi",
|
||||
"sign_up": "Registrati",
|
||||
"NEW_USER": "Prima volta con Ente",
|
||||
"EXISTING_USER": "Accedi",
|
||||
"ENTER_NAME": "Inserisci il nome",
|
||||
"enter_name": "Inserisci il nome",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Aggiungi un nome in modo che i tuoi amici sappiano chi ringraziare per queste fantastiche foto!",
|
||||
"ENTER_EMAIL": "Inserisci l'indirizzo email",
|
||||
"EMAIL_ERROR": "Inserisci un indirizzo email valido",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "Crea album",
|
||||
"CREATE_COLLECTION": "Nuovo album",
|
||||
"enter_album_name": "Nome album",
|
||||
"CLOSE_OPTION": "Chiudi (Esc)",
|
||||
"close_key": "Chiudi (Esc)",
|
||||
"enter_file_name": "Nome del file",
|
||||
"CLOSE": "Chiudi",
|
||||
"NO": "No",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Chiudi",
|
||||
"no": "No",
|
||||
"nothing_here": "Non c'e ancora niente qui",
|
||||
"upload": "Carica",
|
||||
"import": "Importa",
|
||||
"add_photos": "Aggiungi foto",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "Password cambiata altrove",
|
||||
"password_changed_elsewhere_message": "Effettua nuovamente il login su questo dispositivo per utilizzare la nuova password per autenticarti.",
|
||||
"GO_BACK": "Torna indietro",
|
||||
"RECOVERY_KEY": "Chiave di recupero",
|
||||
"SAVE_LATER": "Fallo più tardi",
|
||||
"SAVE": "Salva Chiave",
|
||||
"recovery_key": "Chiave di recupero",
|
||||
"do_this_later": "Fallo più tardi",
|
||||
"save_key": "Salva Chiave",
|
||||
"RECOVERY_KEY_DESCRIPTION": "Se dimentichi la tua password, l'unico modo per recuperare i tuoi dati è con questa chiave.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "Impossibile generare il codice di recupero, riprova",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "Non memorizziamo questa chiave, quindi salvala in un luogo sicuro",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Recupera",
|
||||
"NO_RECOVERY_KEY": "Nessuna chiave di recupero?",
|
||||
"INCORRECT_RECOVERY_KEY": "Chiave di recupero errata",
|
||||
"SORRY": "Siamo spiacenti",
|
||||
"sorry": "Siamo spiacenti",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "A causa della natura del nostro protocollo di crittografia end-to-end, i tuoi dati non possono essere decifrati senza la tua password o chiave di ripristino",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Per favore invia un'email a <a>{{emailID}}</a> dal tuo indirizzo email registrato",
|
||||
"CONTACT_SUPPORT": "Contatta il supporto",
|
||||
"REQUEST_FEATURE": "Richiedi una funzionalità",
|
||||
"SUPPORT": "Supporto",
|
||||
"CONFIRM": "Conferma",
|
||||
"contact_support": "Contatta il supporto",
|
||||
"request_feature": "Richiedi una funzionalità",
|
||||
"support": "Supporto",
|
||||
"cancel": "Annulla",
|
||||
"LOGOUT": "Disconnettiti",
|
||||
"logout": "Disconnettiti",
|
||||
"logout_message": "Sei sicuro di volerti disconnettere?",
|
||||
"delete_account": "Elimina account",
|
||||
"delete_account_manually_message": "<p>Per favore invia una email a <a>{{emailID}}</a> dal tuo indirizzo email registrato.</p><p>La tua richiesta verrà elaborata entro 72 ore.</p>",
|
||||
"LOGOUT_MESSAGE": "Sei sicuro di volerti disconnettere?",
|
||||
"CHANGE_EMAIL": "Cambia email",
|
||||
"ok": "OK",
|
||||
"success": "Operazione riuscita",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "Nascondi album",
|
||||
"unhide_collection": "Rimuovi album dai nascosti",
|
||||
"MOVE": "Sposta",
|
||||
"ADD": "Aggiungi",
|
||||
"add": "Aggiungi",
|
||||
"REMOVE": "Rimuovi",
|
||||
"YES_REMOVE": "Sì, rimuovi",
|
||||
"REMOVE_FROM_COLLECTION": "Rimuovi dall'album",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Colori",
|
||||
"FLIP": "Capovolgi",
|
||||
"ROTATION": "Rotazione",
|
||||
"RESET": "Reimposta",
|
||||
"reset": "Reimposta",
|
||||
"PHOTO_EDITOR": "Editor Foto",
|
||||
"FASTER_UPLOAD": "Upload più veloci",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "Effettua l'upload attraverso i server vicini",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>당신의 추억을 위한</div><div>비공개 백업</div>",
|
||||
"HERO_SLIDE_1": "종단간 암호화를 기본적으로 지원합니다",
|
||||
"HERO_SLIDE_2_TITLE": "<div>낙진 대피소에</div><div>안전하게 보관됨</div>",
|
||||
"HERO_SLIDE_2": "장기 보존을 위해 설계되었습니다",
|
||||
"HERO_SLIDE_3_TITLE": "<div>모든 기기에서</div><div>사용 가능</div>",
|
||||
"HERO_SLIDE_3": "안드로이드, iOS, 웹, 데스크탑",
|
||||
"intro_slide_1_title": "<div>당신의 추억을 위한</div><div>비공개 백업</div>",
|
||||
"intro_slide_1": "종단간 암호화를 기본적으로 지원합니다",
|
||||
"intro_slide_2_title": "<div>낙진 대피소에</div><div>안전하게 보관됨</div>",
|
||||
"intro_slide_2": "장기 보존을 위해 설계되었습니다",
|
||||
"intro_slide_3_title": "<div>모든 기기에서</div><div>사용 가능</div>",
|
||||
"intro_slide_3": "안드로이드, iOS, 웹, 데스크탑",
|
||||
"login": "로그인",
|
||||
"sign_up": "회원가입",
|
||||
"NEW_USER": "Ente 의 새소식",
|
||||
"EXISTING_USER": "기존 회원 로그인",
|
||||
"ENTER_NAME": "이름 입력",
|
||||
"enter_name": "이름 입력",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "친구들이 이 멋진 사진에 대해 고마워할 수 있도록 이름을 추가하세요!",
|
||||
"ENTER_EMAIL": "이메일 주소를 입력하세요",
|
||||
"EMAIL_ERROR": "올바른 이메일을 입력하세요",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "새 앨범",
|
||||
"enter_album_name": "앨범 이름",
|
||||
"CLOSE_OPTION": "닫기 (Esc)",
|
||||
"close_key": "닫기 (Esc)",
|
||||
"enter_file_name": "파일 이름",
|
||||
"CLOSE": "닫기",
|
||||
"NO": "아니오",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "닫기",
|
||||
"no": "아니오",
|
||||
"nothing_here": "",
|
||||
"upload": "업로드",
|
||||
"import": "가져오기",
|
||||
"add_photos": "사진 추가",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "뒤로 가기",
|
||||
"RECOVERY_KEY": "키 복구하기",
|
||||
"SAVE_LATER": "나중에 저장하기",
|
||||
"SAVE": "키 저장하기",
|
||||
"recovery_key": "키 복구하기",
|
||||
"do_this_later": "나중에 저장하기",
|
||||
"save_key": "키 저장하기",
|
||||
"RECOVERY_KEY_DESCRIPTION": "암호 분실시, 오직 이 키를 이용해야만 데이터를 복구할 수 있습니다.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "확인",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "취소",
|
||||
"LOGOUT": "로그아웃",
|
||||
"logout": "로그아웃",
|
||||
"logout_message": "정말로 로그아웃 하시겠습니까?",
|
||||
"delete_account": "계정 삭제",
|
||||
"delete_account_manually_message": "<p>회원가입에 사용한 이메일을 통해 <a>{{emailID}}</a> (으)로 메일을 보내주세요.</p><p>귀하의 요청은 72시간 내로 처리됩니다.</p>",
|
||||
"LOGOUT_MESSAGE": "정말로 로그아웃 하시겠습니까?",
|
||||
"CHANGE_EMAIL": "이메일 주소 변경",
|
||||
"ok": "확인",
|
||||
"success": "성공",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Privé back-ups</div><div>voor uw herinneringen</div>",
|
||||
"HERO_SLIDE_1": "Standaard end-to-end versleuteld",
|
||||
"HERO_SLIDE_2_TITLE": "<div>Veilig opgeslagen</div><div>in een kernbunker</div>",
|
||||
"HERO_SLIDE_2": "Ontworpen om levenslang mee te gaan",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Overal</div><div> beschikbaar</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, Web, Desktop",
|
||||
"intro_slide_1_title": "<div>Privé back-ups</div><div>voor uw herinneringen</div>",
|
||||
"intro_slide_1": "Standaard end-to-end versleuteld",
|
||||
"intro_slide_2_title": "<div>Veilig opgeslagen</div><div>in een kernbunker</div>",
|
||||
"intro_slide_2": "Ontworpen om levenslang mee te gaan",
|
||||
"intro_slide_3_title": "<div>Overal</div><div> beschikbaar</div>",
|
||||
"intro_slide_3": "Android, iOS, Web, Desktop",
|
||||
"login": "Inloggen",
|
||||
"sign_up": "Registreren",
|
||||
"NEW_USER": "Nieuw bij Ente",
|
||||
"EXISTING_USER": "Bestaande gebruiker",
|
||||
"ENTER_NAME": "Naam invoeren",
|
||||
"enter_name": "Naam invoeren",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Voeg een naam toe zodat je vrienden weten wie ze moeten bedanken voor deze geweldige foto's!",
|
||||
"ENTER_EMAIL": "Vul e-mailadres in",
|
||||
"EMAIL_ERROR": "Vul een geldig e-mailadres in",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "Albums aanmaken",
|
||||
"CREATE_COLLECTION": "Nieuw album",
|
||||
"enter_album_name": "Albumnaam",
|
||||
"CLOSE_OPTION": "Sluiten (Esc)",
|
||||
"close_key": "Sluiten (Esc)",
|
||||
"enter_file_name": "Bestandsnaam",
|
||||
"CLOSE": "Sluiten",
|
||||
"NO": "Nee",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Sluiten",
|
||||
"no": "Nee",
|
||||
"nothing_here": "",
|
||||
"upload": "Uploaden",
|
||||
"import": "Importeren",
|
||||
"add_photos": "Foto's toevoegen",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "Wachtwoord elders gewijzigd",
|
||||
"password_changed_elsewhere_message": "Log opnieuw in op dit apparaat om uw nieuwe wachtwoord te gebruiken om te verifiëren.",
|
||||
"GO_BACK": "Ga terug",
|
||||
"RECOVERY_KEY": "Herstelsleutel",
|
||||
"SAVE_LATER": "Doe dit later",
|
||||
"SAVE": "Sleutel opslaan",
|
||||
"recovery_key": "Herstelsleutel",
|
||||
"do_this_later": "Doe dit later",
|
||||
"save_key": "Sleutel opslaan",
|
||||
"RECOVERY_KEY_DESCRIPTION": "Als je je wachtwoord vergeet, kun je alleen met deze sleutel je gegevens herstellen.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "Herstelcode kon niet worden gegenereerd, probeer het opnieuw",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "We slaan deze sleutel niet op, bewaar dit op een veilige plaats",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Herstellen",
|
||||
"NO_RECOVERY_KEY": "Geen herstelsleutel?",
|
||||
"INCORRECT_RECOVERY_KEY": "Onjuiste herstelsleutel",
|
||||
"SORRY": "Sorry",
|
||||
"sorry": "Sorry",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "Door de aard van ons end-to-end encryptieprotocol kunnen je gegevens niet worden ontsleuteld zonder je wachtwoord of herstelsleutel",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Stuur een e-mail naar <a>{{emailID}}</a> vanaf het door jou geregistreerde e-mailadres",
|
||||
"CONTACT_SUPPORT": "Klantenservice",
|
||||
"REQUEST_FEATURE": "Vraag nieuwe functie aan",
|
||||
"SUPPORT": "Ondersteuning",
|
||||
"CONFIRM": "Bevestigen",
|
||||
"contact_support": "Klantenservice",
|
||||
"request_feature": "Vraag nieuwe functie aan",
|
||||
"support": "Ondersteuning",
|
||||
"cancel": "Annuleren",
|
||||
"LOGOUT": "Uitloggen",
|
||||
"logout": "Uitloggen",
|
||||
"logout_message": "Weet u zeker dat u wilt uitloggen?",
|
||||
"delete_account": "Account verwijderen",
|
||||
"delete_account_manually_message": "<p>Stuur een e-mail naar <a>{{emailID}}</a> vanaf uw geregistreerde e-mailadres.</p><p>Uw aanvraag wordt binnen 72 uur verwerkt.</p>",
|
||||
"LOGOUT_MESSAGE": "Weet u zeker dat u wilt uitloggen?",
|
||||
"CHANGE_EMAIL": "E-mail wijzigen",
|
||||
"ok": "Oké",
|
||||
"success": "Succes",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "Verberg album",
|
||||
"unhide_collection": "Album zichtbaar maken",
|
||||
"MOVE": "Verplaatsen",
|
||||
"ADD": "Toevoegen",
|
||||
"add": "Toevoegen",
|
||||
"REMOVE": "Verwijderen",
|
||||
"YES_REMOVE": "Ja, verwijderen",
|
||||
"REMOVE_FROM_COLLECTION": "Verwijderen uit album",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Kleuren",
|
||||
"FLIP": "Omdraaien",
|
||||
"ROTATION": "Draaiing",
|
||||
"RESET": "Herstellen",
|
||||
"reset": "Herstellen",
|
||||
"PHOTO_EDITOR": "Fotobewerker",
|
||||
"FASTER_UPLOAD": "Snellere uploads",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "Uploaden door nabije servers",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Prywatne kopie zapasowe</div><div>dla Twoich wspomnień</div>",
|
||||
"HERO_SLIDE_1": "Domyślnie zaszyfrowane metodą end-to-end",
|
||||
"HERO_SLIDE_2_TITLE": "<div>Bezpiecznie przechowywane</div><div>w awaryjnym schronieniu</div>",
|
||||
"HERO_SLIDE_2": "Zaprojektowane do przetrwania",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Dostępne</div><div> wszędzie</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, Strona Internetowa, Aplikacja Komputerowa",
|
||||
"intro_slide_1_title": "<div>Prywatne kopie zapasowe</div><div>dla Twoich wspomnień</div>",
|
||||
"intro_slide_1": "Domyślnie zaszyfrowane metodą end-to-end",
|
||||
"intro_slide_2_title": "<div>Bezpiecznie przechowywane</div><div>w awaryjnym schronieniu</div>",
|
||||
"intro_slide_2": "Zaprojektowane do przetrwania",
|
||||
"intro_slide_3_title": "<div>Dostępne</div><div> wszędzie</div>",
|
||||
"intro_slide_3": "Android, iOS, Strona Internetowa, Aplikacja Komputerowa",
|
||||
"login": "Zaloguj się",
|
||||
"sign_up": "Zarejestruj się",
|
||||
"NEW_USER": "Nowy/a do Ente",
|
||||
"EXISTING_USER": "Istniejący użytkownik",
|
||||
"ENTER_NAME": "Wprowadź nazwę",
|
||||
"enter_name": "Wprowadź nazwę",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Dodaj imię, aby Twoi znajomi wiedzieli, kto będzie mógł podziękować za te wspaniałe zdjęcia!",
|
||||
"ENTER_EMAIL": "Wprowadź adres e-mail",
|
||||
"EMAIL_ERROR": "Wprowadź prawidłowy adres e-mail",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "Utwórz albumy",
|
||||
"CREATE_COLLECTION": "Nowy album",
|
||||
"enter_album_name": "Nazwa albumu",
|
||||
"CLOSE_OPTION": "Zamknij (Esc)",
|
||||
"close_key": "Zamknij (Esc)",
|
||||
"enter_file_name": "Nazwa pliku",
|
||||
"CLOSE": "Zamknij",
|
||||
"NO": "Nie",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Zamknij",
|
||||
"no": "Nie",
|
||||
"nothing_here": "Nic tu jeszcze nie ma",
|
||||
"upload": "Prześlij",
|
||||
"import": "Importuj",
|
||||
"add_photos": "Dodaj zdjęcia",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "Hasło zostało zmienione gdzie indziej",
|
||||
"password_changed_elsewhere_message": "Zaloguj się ponownie na tym urządzeniu, aby użyć nowego hasła, aby się uwierzytelnić.",
|
||||
"GO_BACK": "Cofnij się",
|
||||
"RECOVERY_KEY": "Klucz odzyskiwania",
|
||||
"SAVE_LATER": "Zrób to później",
|
||||
"SAVE": "Zapisz Klucz",
|
||||
"recovery_key": "Klucz odzyskiwania",
|
||||
"do_this_later": "Zrób to później",
|
||||
"save_key": "Zapisz Klucz",
|
||||
"RECOVERY_KEY_DESCRIPTION": "Jeśli zapomnisz swojego hasła, jedynym sposobem na odzyskanie Twoich danych jest ten klucz.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "Nie można wygenerować kodu odzyskiwania, spróbuj ponownie",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "Nie przechowujemy tego klucza, prosimy zapisać to w bezpiecznym miejscu",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Odzyskaj",
|
||||
"NO_RECOVERY_KEY": "Brak klucza odzyskiwania?",
|
||||
"INCORRECT_RECOVERY_KEY": "Nieprawidłowy klucz odzyskiwania",
|
||||
"SORRY": "Przepraszamy",
|
||||
"sorry": "Przepraszamy",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "Ze względu na charakter naszego protokołu szyfrowania end-to-end, Twoje dane nie mogą być odszyfrowane bez hasła lub klucza odzyskiwania",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Wyślij wiadomość e-mail na <a>{{emailID}}</a> z zarejestrowanego adresu e-mail",
|
||||
"CONTACT_SUPPORT": "Skontaktuj się z pomocą techniczną",
|
||||
"REQUEST_FEATURE": "Zaproponuj Funkcję",
|
||||
"SUPPORT": "Wsparcie Techniczne",
|
||||
"CONFIRM": "Potwierdź",
|
||||
"contact_support": "Skontaktuj się z pomocą techniczną",
|
||||
"request_feature": "Zaproponuj Funkcję",
|
||||
"support": "Wsparcie Techniczne",
|
||||
"cancel": "Anuluj",
|
||||
"LOGOUT": "Wyloguj się",
|
||||
"logout": "Wyloguj się",
|
||||
"logout_message": "Czy na pewno chcesz się wylogować?",
|
||||
"delete_account": "Usuń konto",
|
||||
"delete_account_manually_message": "<p>Prosimy wysłać wiadomość e-mail na <a>{{emailID}}</a> z Twojego zarejestrowanego adresu e-mail. </p><p>Twoja prośba zostanie przetworzona w ciągu 72 godzin.</p>",
|
||||
"LOGOUT_MESSAGE": "Czy na pewno chcesz się wylogować?",
|
||||
"CHANGE_EMAIL": "Zmień adres e-mail",
|
||||
"ok": "OK",
|
||||
"success": "Sukces",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "Ukryj album",
|
||||
"unhide_collection": "Odkryj album",
|
||||
"MOVE": "Przenieś",
|
||||
"ADD": "Dodaj",
|
||||
"add": "Dodaj",
|
||||
"REMOVE": "Usuń",
|
||||
"YES_REMOVE": "Tak, usuń",
|
||||
"REMOVE_FROM_COLLECTION": "Usuń z albumu",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Kolory",
|
||||
"FLIP": "Obróć",
|
||||
"ROTATION": "Rotacja",
|
||||
"RESET": "Zresetuj",
|
||||
"reset": "Zresetuj",
|
||||
"PHOTO_EDITOR": "Edytor Zdjęć",
|
||||
"FASTER_UPLOAD": "Szybsze przesłania",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "Kieruj przesłania przez pobliskie serwery",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Backups privados</div><div>para as suas memórias</div>",
|
||||
"HERO_SLIDE_1": "Criptografia de ponta a ponta por padrão",
|
||||
"HERO_SLIDE_2_TITLE": "<div>Armazenado com segurança</div><div>em um abrigo avançado</div>",
|
||||
"HERO_SLIDE_2": "Feito para ter longevidade",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Disponível</div><div> em qualquer lugar</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, Web, Desktop",
|
||||
"intro_slide_1_title": "<div>Backups privados</div><div>para as suas memórias</div>",
|
||||
"intro_slide_1": "Criptografia de ponta a ponta por padrão",
|
||||
"intro_slide_2_title": "<div>Armazenado com segurança</div><div>em um abrigo avançado</div>",
|
||||
"intro_slide_2": "Feito para ter longevidade",
|
||||
"intro_slide_3_title": "<div>Disponível</div><div> em qualquer lugar</div>",
|
||||
"intro_slide_3": "Android, iOS, Web, Desktop",
|
||||
"login": "Entrar",
|
||||
"sign_up": "Registrar",
|
||||
"NEW_USER": "Novo no Ente",
|
||||
"EXISTING_USER": "Usuário existente",
|
||||
"ENTER_NAME": "Insira o nome",
|
||||
"enter_name": "Insira o nome",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Adicione um nome para que os seus amigos saibam a quem agradecer por estas ótimas fotos!",
|
||||
"ENTER_EMAIL": "Insira o endereço de e-mail",
|
||||
"EMAIL_ERROR": "Inserir um endereço de e-mail válido",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "Criar álbuns",
|
||||
"CREATE_COLLECTION": "Novo álbum",
|
||||
"enter_album_name": "Nome do álbum",
|
||||
"CLOSE_OPTION": "Fechar (Esc)",
|
||||
"close_key": "Fechar (Esc)",
|
||||
"enter_file_name": "Nome do arquivo",
|
||||
"CLOSE": "Fechar",
|
||||
"NO": "Não",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Fechar",
|
||||
"no": "Não",
|
||||
"nothing_here": "Nada aqui ainda",
|
||||
"upload": "Enviar",
|
||||
"import": "Importar",
|
||||
"add_photos": "Adicionar fotos",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "Senha alterada em outro lugar",
|
||||
"password_changed_elsewhere_message": "Por favor, inicie sessão novamente neste dispositivo para usar sua nova senha para autenticar.",
|
||||
"GO_BACK": "Voltar",
|
||||
"RECOVERY_KEY": "Chave de recuperação",
|
||||
"SAVE_LATER": "Fazer isso mais tarde",
|
||||
"SAVE": "Salvar Chave",
|
||||
"recovery_key": "Chave de recuperação",
|
||||
"do_this_later": "Fazer isso mais tarde",
|
||||
"save_key": "Salvar Chave",
|
||||
"RECOVERY_KEY_DESCRIPTION": "Caso você esqueça sua senha, a única maneira de recuperar seus dados é com essa chave.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "Não foi possível gerar o código de recuperação, tente novamente",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "Não armazenamos essa chave, por favor, salve essa chave de palavras em um lugar seguro",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Recuperar",
|
||||
"NO_RECOVERY_KEY": "Não possui a chave de recuperação?",
|
||||
"INCORRECT_RECOVERY_KEY": "Chave de recuperação incorreta",
|
||||
"SORRY": "Desculpe",
|
||||
"sorry": "Desculpe",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "Devido à natureza do nosso protocolo de criptografia de ponta a ponta, seus dados não podem ser descriptografados sem sua senha ou chave de recuperação",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Por favor, envie um e-mail para <a>{{emailID}}<a> a partir do seu endereço de e-mail registrado",
|
||||
"CONTACT_SUPPORT": "Falar com o suporte",
|
||||
"REQUEST_FEATURE": "Solicitar recurso",
|
||||
"SUPPORT": "Suporte",
|
||||
"CONFIRM": "Confirmar",
|
||||
"contact_support": "Falar com o suporte",
|
||||
"request_feature": "Solicitar recurso",
|
||||
"support": "Suporte",
|
||||
"cancel": "Cancelar",
|
||||
"LOGOUT": "Encerrar sessão",
|
||||
"logout": "Encerrar sessão",
|
||||
"logout_message": "Você tem certeza que deseja encerrar a sessão?",
|
||||
"delete_account": "Excluir conta",
|
||||
"delete_account_manually_message": "<p>Por favor, envie um e-mail para <a>{{emailID}}</a> a partir do seu endereço de e-mail registrado.</p><p>Seu pedido será processado dentro de 72 horas.</p>",
|
||||
"LOGOUT_MESSAGE": "Você tem certeza que deseja encerrar a sessão?",
|
||||
"CHANGE_EMAIL": "Mudar e-mail",
|
||||
"ok": "Aceitar",
|
||||
"success": "Bem-sucedido",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "Ocultar álbum",
|
||||
"unhide_collection": "Reexibir álbum",
|
||||
"MOVE": "Mover",
|
||||
"ADD": "Adicionar",
|
||||
"add": "Adicionar",
|
||||
"REMOVE": "Remover",
|
||||
"YES_REMOVE": "Sim, remover",
|
||||
"REMOVE_FROM_COLLECTION": "Remover do álbum",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Cores",
|
||||
"FLIP": "Inverter",
|
||||
"ROTATION": "Rotação",
|
||||
"RESET": "Redefinir",
|
||||
"reset": "Redefinir",
|
||||
"PHOTO_EDITOR": "Editor de Fotos",
|
||||
"FASTER_UPLOAD": "Envios mais rápidos",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "Rotas enviam em servidores próximos",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Backups privados</div><div>para as suas memórias</div>",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Disponível</div><div> em qualquer lugar</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, Web, Desktop",
|
||||
"intro_slide_1_title": "<div>Backups privados</div><div>para as suas memórias</div>",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "<div>Disponível</div><div> em qualquer lugar</div>",
|
||||
"intro_slide_3": "Android, iOS, Web, Desktop",
|
||||
"login": "Entrar",
|
||||
"sign_up": "Registar",
|
||||
"NEW_USER": "Novo no Ente",
|
||||
"EXISTING_USER": "Utilizador existente",
|
||||
"ENTER_NAME": "Insira o nome",
|
||||
"enter_name": "Insira o nome",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Adicione um nome para que os seus amigos saibam a quem agradecer por estas ótimas fotos!",
|
||||
"ENTER_EMAIL": "Insira o endereço de email",
|
||||
"EMAIL_ERROR": "Inserir um endereço de email válido",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "Novo álbum",
|
||||
"enter_album_name": "Nome do álbum",
|
||||
"CLOSE_OPTION": "Fechar (Esc)",
|
||||
"close_key": "Fechar (Esc)",
|
||||
"enter_file_name": "Nome do ficheiro",
|
||||
"CLOSE": "Fechar",
|
||||
"NO": "Não",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Fechar",
|
||||
"no": "Não",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "Importar",
|
||||
"add_photos": "Adicionar fotos",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Приватные резервные копии</div><div>для ваших воспоминаний</div>",
|
||||
"HERO_SLIDE_1": "Сквозное шифрование по умолчанию",
|
||||
"HERO_SLIDE_2_TITLE": "<div>Надежно хранится</div><div>в убежище от радиоактивных осадков</div>",
|
||||
"HERO_SLIDE_2": "Созданный для того, чтобы пережить",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Доступно</div><div> везде</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, Веб, ПК",
|
||||
"intro_slide_1_title": "<div>Приватные резервные копии</div><div>для ваших воспоминаний</div>",
|
||||
"intro_slide_1": "Сквозное шифрование по умолчанию",
|
||||
"intro_slide_2_title": "<div>Надежно хранится</div><div>в убежище от радиоактивных осадков</div>",
|
||||
"intro_slide_2": "Созданный для того, чтобы пережить",
|
||||
"intro_slide_3_title": "<div>Доступно</div><div> везде</div>",
|
||||
"intro_slide_3": "Android, iOS, Веб, ПК",
|
||||
"login": "Войти",
|
||||
"sign_up": "Регистрация",
|
||||
"NEW_USER": "Новенький в Ente",
|
||||
"EXISTING_USER": "Существующий пользователь",
|
||||
"ENTER_NAME": "Введите имя",
|
||||
"enter_name": "Введите имя",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Добавьте имя, чтобы ваши друзья знали, кого благодарить за эти замечательные фотографии!",
|
||||
"ENTER_EMAIL": "Введите адрес электронной почты",
|
||||
"EMAIL_ERROR": "Введите действительный адрес электронной почты",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "Создать альбомы",
|
||||
"CREATE_COLLECTION": "Новый альбом",
|
||||
"enter_album_name": "Название альбома",
|
||||
"CLOSE_OPTION": "Закрыть (Esc)",
|
||||
"close_key": "Закрыть (Esc)",
|
||||
"enter_file_name": "Имя файла",
|
||||
"CLOSE": "Закрыть",
|
||||
"NO": "Нет",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Закрыть",
|
||||
"no": "Нет",
|
||||
"nothing_here": "",
|
||||
"upload": "Загрузить",
|
||||
"import": "Импорт",
|
||||
"add_photos": "Добавить фотографии",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "Пароль изменен в другом месте",
|
||||
"password_changed_elsewhere_message": "Пожалуйста, войдите снова на этом устройстве, чтобы использовать новый пароль для аутентификации.",
|
||||
"GO_BACK": "Вернуться назад",
|
||||
"RECOVERY_KEY": "Ключ восстановления",
|
||||
"SAVE_LATER": "Сделать позже",
|
||||
"SAVE": "Сохранить ключ",
|
||||
"recovery_key": "Ключ восстановления",
|
||||
"do_this_later": "Сделать позже",
|
||||
"save_key": "Сохранить ключ",
|
||||
"RECOVERY_KEY_DESCRIPTION": "Если вы забыли свой пароль, то восстановить данные можно только с помощью этого ключа.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "Не удалось сгенерировать код восстановления, пожалуйста, повторите попытку",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "Мы не храним этот ключ, поэтому, пожалуйста, сохраните его в надежном месте",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Восстановить",
|
||||
"NO_RECOVERY_KEY": "Нет ключа восстановления?",
|
||||
"INCORRECT_RECOVERY_KEY": "Неправильный ключ восстановления",
|
||||
"SORRY": "Извините",
|
||||
"sorry": "Извините",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "Из-за природы нашего сквозного протокола шифрования ваши данные не могут быть расшифрованы без вашего пароля или ключа восстановления",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Пожалуйста, отправьте электронное письмо на адрес <a>{{emailID}}</a> с вашего зарегистрированного адреса электронной почты",
|
||||
"CONTACT_SUPPORT": "Связаться с поддержкой",
|
||||
"REQUEST_FEATURE": "Запросить функцию",
|
||||
"SUPPORT": "Поддержка",
|
||||
"CONFIRM": "Подтвердить",
|
||||
"contact_support": "Связаться с поддержкой",
|
||||
"request_feature": "Запросить функцию",
|
||||
"support": "Поддержка",
|
||||
"cancel": "Отменить",
|
||||
"LOGOUT": "Выйти",
|
||||
"logout": "Выйти",
|
||||
"logout_message": "Вы уверены, что хотите выйти?",
|
||||
"delete_account": "Удалить аккаунт",
|
||||
"delete_account_manually_message": "<p>Пожалуйста, отправьте письмо по адресу <a>{{emailID}}</a> с вашего зарегистрированного адреса электронной почты.</p><p> Ваш запрос будет обработан в течение 72 часов</p>",
|
||||
"LOGOUT_MESSAGE": "Вы уверены, что хотите выйти?",
|
||||
"CHANGE_EMAIL": "Изменить адрес электронной почты",
|
||||
"ok": "ОК",
|
||||
"success": "Успешно",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "Скрыть альбом",
|
||||
"unhide_collection": "Показать альбом",
|
||||
"MOVE": "Подвиньте",
|
||||
"ADD": "Добавь",
|
||||
"add": "Добавь",
|
||||
"REMOVE": "Удалять",
|
||||
"YES_REMOVE": "Да, удалить",
|
||||
"REMOVE_FROM_COLLECTION": "Удалить из альбома",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Цвета",
|
||||
"FLIP": "Перевернуть",
|
||||
"ROTATION": "Вращение",
|
||||
"RESET": "Сбросить",
|
||||
"reset": "Сбросить",
|
||||
"PHOTO_EDITOR": "Редактор фото",
|
||||
"FASTER_UPLOAD": "Более быстрая загрузка данных",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "Загрузка маршрута через близлежащие серверы",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>Privata säkerhetskopior</div><div>för dina minnen</div>",
|
||||
"HERO_SLIDE_1": "Totalsträckskryptering som standard",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "Utformad för att överleva",
|
||||
"HERO_SLIDE_3_TITLE": "<div>Tillgänglig</div><div> överallt</div>",
|
||||
"HERO_SLIDE_3": "Android, iOS, webb, skrivbord",
|
||||
"intro_slide_1_title": "<div>Privata säkerhetskopior</div><div>för dina minnen</div>",
|
||||
"intro_slide_1": "Totalsträckskryptering som standard",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "Utformad för att överleva",
|
||||
"intro_slide_3_title": "<div>Tillgänglig</div><div> överallt</div>",
|
||||
"intro_slide_3": "Android, iOS, webb, skrivbord",
|
||||
"login": "Logga in",
|
||||
"sign_up": "Registrera dig",
|
||||
"NEW_USER": "Ny hos Ente",
|
||||
"EXISTING_USER": "Befintlig användare",
|
||||
"ENTER_NAME": "Ange namn",
|
||||
"enter_name": "Ange namn",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Lägg till ett namn så att dina vänner vet vem de ska tacka för dessa fantastiska bilder!",
|
||||
"ENTER_EMAIL": "Ange e-postadress",
|
||||
"EMAIL_ERROR": "Ange en giltig e-postadress",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "Skapa album",
|
||||
"CREATE_COLLECTION": "Nytt album",
|
||||
"enter_album_name": "Albumnamn",
|
||||
"CLOSE_OPTION": "Stäng (Esc)",
|
||||
"close_key": "Stäng (Esc)",
|
||||
"enter_file_name": "Filnamn",
|
||||
"CLOSE": "Stäng",
|
||||
"NO": "Nej",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Stäng",
|
||||
"no": "Nej",
|
||||
"nothing_here": "",
|
||||
"upload": "Ladda upp",
|
||||
"import": "Importera",
|
||||
"add_photos": "Lägg till foton",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "Återställningsnyckel",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "Spara nyckel",
|
||||
"recovery_key": "Återställningsnyckel",
|
||||
"do_this_later": "",
|
||||
"save_key": "Spara nyckel",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Återställ",
|
||||
"NO_RECOVERY_KEY": "Ingen återställningsnyckel?",
|
||||
"INCORRECT_RECOVERY_KEY": "Felaktig återställningsnyckel",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "Support",
|
||||
"CONFIRM": "Bekräfta",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "Avbryt",
|
||||
"LOGOUT": "Logga ut",
|
||||
"logout": "Logga ut",
|
||||
"logout_message": "Är du säker på att du vill logga ut?",
|
||||
"delete_account": "Radera konto",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "Är du säker på att du vill logga ut?",
|
||||
"CHANGE_EMAIL": "Ändra e-postadress",
|
||||
"ok": "OK",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "Dölj album",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "Flytta",
|
||||
"ADD": "Lägg till",
|
||||
"add": "Lägg till",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "Färger",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "Återställ",
|
||||
"reset": "Återställ",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "",
|
||||
"sign_up": "",
|
||||
"NEW_USER": "",
|
||||
"EXISTING_USER": "",
|
||||
"ENTER_NAME": "",
|
||||
"enter_name": "",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
|
||||
"ENTER_EMAIL": "",
|
||||
"EMAIL_ERROR": "",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "",
|
||||
"CREATE_COLLECTION": "",
|
||||
"enter_album_name": "",
|
||||
"CLOSE_OPTION": "",
|
||||
"close_key": "",
|
||||
"enter_file_name": "",
|
||||
"CLOSE": "",
|
||||
"NO": "",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "",
|
||||
"no": "",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "",
|
||||
"password_changed_elsewhere_message": "",
|
||||
"GO_BACK": "",
|
||||
"RECOVERY_KEY": "",
|
||||
"SAVE_LATER": "",
|
||||
"SAVE": "",
|
||||
"recovery_key": "",
|
||||
"do_this_later": "",
|
||||
"save_key": "",
|
||||
"RECOVERY_KEY_DESCRIPTION": "",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "",
|
||||
"NO_RECOVERY_KEY": "",
|
||||
"INCORRECT_RECOVERY_KEY": "",
|
||||
"SORRY": "",
|
||||
"sorry": "",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "",
|
||||
"CONTACT_SUPPORT": "",
|
||||
"REQUEST_FEATURE": "",
|
||||
"SUPPORT": "",
|
||||
"CONFIRM": "",
|
||||
"contact_support": "",
|
||||
"request_feature": "",
|
||||
"support": "",
|
||||
"cancel": "",
|
||||
"LOGOUT": "",
|
||||
"logout": "",
|
||||
"logout_message": "",
|
||||
"delete_account": "",
|
||||
"delete_account_manually_message": "",
|
||||
"LOGOUT_MESSAGE": "",
|
||||
"CHANGE_EMAIL": "",
|
||||
"ok": "",
|
||||
"success": "",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "",
|
||||
"HERO_SLIDE_1": "",
|
||||
"HERO_SLIDE_2_TITLE": "",
|
||||
"HERO_SLIDE_2": "",
|
||||
"HERO_SLIDE_3_TITLE": "",
|
||||
"HERO_SLIDE_3": "",
|
||||
"intro_slide_1_title": "",
|
||||
"intro_slide_1": "",
|
||||
"intro_slide_2_title": "",
|
||||
"intro_slide_2": "",
|
||||
"intro_slide_3_title": "",
|
||||
"intro_slide_3": "",
|
||||
"login": "Giriş yap",
|
||||
"sign_up": "Hesap aç",
|
||||
"NEW_USER": "Yeni ente kullanıcısı",
|
||||
"EXISTING_USER": "Mevcut kullanıcı",
|
||||
"ENTER_NAME": "İsim gir",
|
||||
"enter_name": "İsim gir",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "Arkadaşlarının bu harika fotoğraflar için kime teşekkür etmeleri gerektiğini bilmeleri için bir isim ekle!",
|
||||
"ENTER_EMAIL": "E-posta adresini girin",
|
||||
"EMAIL_ERROR": "Geçerli bir e-posta gir",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "Albüm oluştur",
|
||||
"CREATE_COLLECTION": "Yeni albüm",
|
||||
"enter_album_name": "Albüm adı",
|
||||
"CLOSE_OPTION": "Kapat (Esc)",
|
||||
"close_key": "Kapat (Esc)",
|
||||
"enter_file_name": "Dosya adı",
|
||||
"CLOSE": "Kapat",
|
||||
"NO": "Hayır",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "Kapat",
|
||||
"no": "Hayır",
|
||||
"nothing_here": "",
|
||||
"upload": "",
|
||||
"import": "",
|
||||
"add_photos": "",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "Parola başka bir yerde değiştirildi",
|
||||
"password_changed_elsewhere_message": "Lütfen yeni parolanı kullanarak kimlik doğrulaması yapmak için bu cihazda tekrar oturum aç.",
|
||||
"GO_BACK": "Geri dön",
|
||||
"RECOVERY_KEY": "Kurtarma anahtarı",
|
||||
"SAVE_LATER": "Sonra yap",
|
||||
"SAVE": "Anahtarı kaydet",
|
||||
"recovery_key": "Kurtarma anahtarı",
|
||||
"do_this_later": "Sonra yap",
|
||||
"save_key": "Anahtarı kaydet",
|
||||
"RECOVERY_KEY_DESCRIPTION": "Eğer parolanı unutursan, verilerini kurtarabileceğin tek yol bu anahtardır.",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "Kurtarma kodu oluşturulamadı, lütfen tekrar dene",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "Bu anahtarı saklamıyoruz, bu nedenle lütfen güvenli bir yerde sakla",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "Kurtar",
|
||||
"NO_RECOVERY_KEY": "Kurtarma anahtarı yok mu?",
|
||||
"INCORRECT_RECOVERY_KEY": "Kurtarma anahtarı yanlış",
|
||||
"SORRY": "Üzgünüz",
|
||||
"sorry": "Üzgünüz",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "Uçtan uca şifreleme protokolümüzün doğası gereği, verilerin parolan veya kurtarma anahtarın olmadan çözülemez",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "Lütfen kaydolduğun e-posta adresinden <a>{{emailID}}</a> adresine bir e-posta bırak",
|
||||
"CONTACT_SUPPORT": "Destek ile iletişime geç",
|
||||
"REQUEST_FEATURE": "Özellik İste",
|
||||
"SUPPORT": "Destek",
|
||||
"CONFIRM": "Onayla",
|
||||
"contact_support": "Destek ile iletişime geç",
|
||||
"request_feature": "Özellik İste",
|
||||
"support": "Destek",
|
||||
"cancel": "İptal",
|
||||
"LOGOUT": "Çıkış yap",
|
||||
"logout": "Çıkış yap",
|
||||
"logout_message": "Çıkış yapmak istediğine emin misin?",
|
||||
"delete_account": "Hesabı sil",
|
||||
"delete_account_manually_message": "<p>Lütfen kaydolduğun e-posta adresinden <a>{{emailID}}</a> adresine bir e-posta gönder.</p><p>İsteğin 72 saat içinde işleme alınacaktır.</p>",
|
||||
"LOGOUT_MESSAGE": "Çıkış yapmak istediğine emin misin?",
|
||||
"CHANGE_EMAIL": "E-posta adresini değiştir",
|
||||
"ok": "Tamam",
|
||||
"success": "Başarılı",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "",
|
||||
"unhide_collection": "",
|
||||
"MOVE": "",
|
||||
"ADD": "",
|
||||
"add": "",
|
||||
"REMOVE": "",
|
||||
"YES_REMOVE": "",
|
||||
"REMOVE_FROM_COLLECTION": "",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "",
|
||||
"FLIP": "",
|
||||
"ROTATION": "",
|
||||
"RESET": "",
|
||||
"reset": "",
|
||||
"PHOTO_EDITOR": "",
|
||||
"FASTER_UPLOAD": "",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"HERO_SLIDE_1_TITLE": "<div>私人备份</div><div>为您的回忆</div>",
|
||||
"HERO_SLIDE_1": "默认端到端加密",
|
||||
"HERO_SLIDE_2_TITLE": "<div>安全地存放</div><div>在一个掩护所中</div>",
|
||||
"HERO_SLIDE_2": "经久耐用",
|
||||
"HERO_SLIDE_3_TITLE": "<div>可用于</div><div> 各处</div>",
|
||||
"HERO_SLIDE_3": "安卓, iOS, 网页端, 桌面端",
|
||||
"intro_slide_1_title": "<div>私人备份</div><div>为您的回忆</div>",
|
||||
"intro_slide_1": "默认端到端加密",
|
||||
"intro_slide_2_title": "<div>安全地存放</div><div>在一个掩护所中</div>",
|
||||
"intro_slide_2": "经久耐用",
|
||||
"intro_slide_3_title": "<div>可用于</div><div> 各处</div>",
|
||||
"intro_slide_3": "安卓, iOS, 网页端, 桌面端",
|
||||
"login": "登录",
|
||||
"sign_up": "注册",
|
||||
"NEW_USER": "初来 Ente",
|
||||
"EXISTING_USER": "现有用户",
|
||||
"ENTER_NAME": "输入名字",
|
||||
"enter_name": "输入名字",
|
||||
"PUBLIC_UPLOADER_NAME_MESSAGE": "请添加一个名字,以便您的朋友知晓该感谢谁拍摄了这些精美的照片!",
|
||||
"ENTER_EMAIL": "请输入电子邮件地址",
|
||||
"EMAIL_ERROR": "请输入有效的电子邮件",
|
||||
@@ -44,11 +44,11 @@
|
||||
"create_albums": "创建相册",
|
||||
"CREATE_COLLECTION": "新建相册",
|
||||
"enter_album_name": "相册名称",
|
||||
"CLOSE_OPTION": "关闭 (或按Esc键)",
|
||||
"close_key": "关闭 (或按Esc键)",
|
||||
"enter_file_name": "文件名",
|
||||
"CLOSE": "关闭",
|
||||
"NO": "否",
|
||||
"NOTHING_HERE": "",
|
||||
"close": "关闭",
|
||||
"no": "否",
|
||||
"nothing_here": "这里什么也没有",
|
||||
"upload": "上传",
|
||||
"import": "导入",
|
||||
"add_photos": "添加照片",
|
||||
@@ -109,9 +109,9 @@
|
||||
"password_changed_elsewhere": "密码已在别处更改",
|
||||
"password_changed_elsewhere_message": "请在此设备上再次登录以使用您的新密码进行身份验证。",
|
||||
"GO_BACK": "返回",
|
||||
"RECOVERY_KEY": "恢复密钥",
|
||||
"SAVE_LATER": "稍后再做",
|
||||
"SAVE": "保存密钥",
|
||||
"recovery_key": "恢复密钥",
|
||||
"do_this_later": "稍后再做",
|
||||
"save_key": "保存密钥",
|
||||
"RECOVERY_KEY_DESCRIPTION": "如果您忘记了密码,恢复数据的唯一方法就是使用此密钥。",
|
||||
"RECOVER_KEY_GENERATION_FAILED": "无法生成恢复代码,请重试",
|
||||
"KEY_NOT_STORED_DISCLAIMER": "我们不存储此密钥,因此请将其保存在安全的地方",
|
||||
@@ -121,18 +121,17 @@
|
||||
"RECOVER": "恢复",
|
||||
"NO_RECOVERY_KEY": "没有恢复密钥?",
|
||||
"INCORRECT_RECOVERY_KEY": "不正确的恢复密钥",
|
||||
"SORRY": "抱歉",
|
||||
"sorry": "抱歉",
|
||||
"NO_RECOVERY_KEY_MESSAGE": "由于我们端到端加密协议的性质,如果没有您的密码或恢复密钥,您的数据将无法解密",
|
||||
"NO_TWO_FACTOR_RECOVERY_KEY_MESSAGE": "请用您注册Ente账户的电子邮箱发一封邮件给 <a>{{emailID}}</a>",
|
||||
"CONTACT_SUPPORT": "联系支持",
|
||||
"REQUEST_FEATURE": "功能建议",
|
||||
"SUPPORT": "支持",
|
||||
"CONFIRM": "确认",
|
||||
"contact_support": "联系支持",
|
||||
"request_feature": "功能建议",
|
||||
"support": "支持",
|
||||
"cancel": "取消",
|
||||
"LOGOUT": "退出登录",
|
||||
"logout": "退出登录",
|
||||
"logout_message": "你确定要退出登录吗?",
|
||||
"delete_account": "删除账户",
|
||||
"delete_account_manually_message": "<p>请从您注册的电子邮件地址发送一封电子邮件到 <a>{{emailID}}</a></p><p>。您的请求将在72小时内处理。</p>",
|
||||
"LOGOUT_MESSAGE": "你确定要退出登录吗?",
|
||||
"CHANGE_EMAIL": "更换邮箱",
|
||||
"ok": "确定",
|
||||
"success": "成功",
|
||||
@@ -324,7 +323,7 @@
|
||||
"hide_collection": "隐藏相册",
|
||||
"unhide_collection": "取消隐藏相册",
|
||||
"MOVE": "移动",
|
||||
"ADD": "添加",
|
||||
"add": "添加",
|
||||
"REMOVE": "移除",
|
||||
"YES_REMOVE": "是,移除",
|
||||
"REMOVE_FROM_COLLECTION": "从相册中移除",
|
||||
@@ -596,7 +595,7 @@
|
||||
"COLORS": "颜色",
|
||||
"FLIP": "上下翻转",
|
||||
"ROTATION": "回转",
|
||||
"RESET": "重设",
|
||||
"reset": "重设",
|
||||
"PHOTO_EDITOR": "照片编辑器",
|
||||
"FASTER_UPLOAD": "更快上传",
|
||||
"FASTER_UPLOAD_DESCRIPTION": "通过附近的服务器路由上传",
|
||||
|
||||
@@ -52,6 +52,10 @@ import {
|
||||
export type GalleryBarMode = "albums" | "hidden-albums" | "people";
|
||||
|
||||
export interface GalleryBarImplProps {
|
||||
/**
|
||||
* When `true`, the bar shows a button to switch to the people section.
|
||||
*/
|
||||
showPeopleSectionButton: boolean;
|
||||
/**
|
||||
* What are we displaying currently.
|
||||
*/
|
||||
@@ -94,11 +98,9 @@ export interface GalleryBarImplProps {
|
||||
*/
|
||||
people: Person[];
|
||||
/**
|
||||
* The ID of the currently selected person.
|
||||
*
|
||||
* Required if mode is "people".
|
||||
* The currently selected person, if any.
|
||||
*/
|
||||
activePersonID: string | undefined;
|
||||
activePerson: Person | undefined;
|
||||
/**
|
||||
* Called when the selection should be moved to a new person in the bar, or
|
||||
* reset to the default state (when {@link person} is `undefined`).
|
||||
@@ -107,6 +109,7 @@ export interface GalleryBarImplProps {
|
||||
}
|
||||
|
||||
export const GalleryBarImpl: React.FC<GalleryBarImplProps> = ({
|
||||
showPeopleSectionButton,
|
||||
mode,
|
||||
onChangeMode,
|
||||
collectionSummaries,
|
||||
@@ -116,7 +119,7 @@ export const GalleryBarImpl: React.FC<GalleryBarImplProps> = ({
|
||||
collectionsSortBy,
|
||||
onChangeCollectionsSortBy,
|
||||
people,
|
||||
activePersonID,
|
||||
activePerson,
|
||||
onSelectPerson,
|
||||
}) => {
|
||||
const isMobile = useIsMobileWidth();
|
||||
@@ -194,11 +197,11 @@ export const GalleryBarImpl: React.FC<GalleryBarImplProps> = ({
|
||||
);
|
||||
break;
|
||||
case "people":
|
||||
i = people.findIndex(({ id }) => id == activePersonID);
|
||||
i = people.findIndex(({ id }) => id == activePerson?.id);
|
||||
break;
|
||||
}
|
||||
if (i != -1) listRef.current.scrollToItem(i, "smart");
|
||||
}, [mode, collectionSummaries, activeCollectionID, people, activePersonID]);
|
||||
}, [mode, collectionSummaries, activeCollectionID, people, activePerson]);
|
||||
|
||||
const itemData = useMemo<ItemData>(
|
||||
() =>
|
||||
@@ -210,12 +213,9 @@ export const GalleryBarImpl: React.FC<GalleryBarImplProps> = ({
|
||||
onSelectCollectionID,
|
||||
}
|
||||
: {
|
||||
type: "people",
|
||||
type: "people" as const,
|
||||
people,
|
||||
activePerson: ensure(
|
||||
people.find((p) => p.id == activePersonID) ??
|
||||
people[0],
|
||||
),
|
||||
activePerson,
|
||||
onSelectPerson,
|
||||
},
|
||||
[
|
||||
@@ -224,12 +224,12 @@ export const GalleryBarImpl: React.FC<GalleryBarImplProps> = ({
|
||||
activeCollectionID,
|
||||
onSelectCollectionID,
|
||||
people,
|
||||
activePersonID,
|
||||
activePerson,
|
||||
onSelectPerson,
|
||||
],
|
||||
);
|
||||
|
||||
const controls1 = isMobile && (
|
||||
const controls1 = isMobile && mode != "people" && (
|
||||
<Box display="flex" alignItems={"center"} gap={1}>
|
||||
<CollectionsSortOptions
|
||||
activeSortBy={collectionsSortBy}
|
||||
@@ -242,7 +242,7 @@ export const GalleryBarImpl: React.FC<GalleryBarImplProps> = ({
|
||||
</Box>
|
||||
);
|
||||
|
||||
const controls2 = !isMobile && (
|
||||
const controls2 = !isMobile && mode != "people" && (
|
||||
<Box display="flex" alignItems={"center"} gap={1} height={"64px"}>
|
||||
<CollectionsSortOptions
|
||||
activeSortBy={collectionsSortBy}
|
||||
@@ -255,9 +255,14 @@ export const GalleryBarImpl: React.FC<GalleryBarImplProps> = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<BarWrapper>
|
||||
// Hide the bottom border if we're showing the empty state for people.
|
||||
<BarWrapper
|
||||
sx={people.length ? {} : { borderBlockEndColor: "transparent" }}
|
||||
>
|
||||
<Row1>
|
||||
<ModeIndicator {...{ mode, onChangeMode }} />
|
||||
<ModeIndicator
|
||||
{...{ showPeopleSectionButton, mode, onChangeMode }}
|
||||
/>
|
||||
{controls1}
|
||||
</Row1>
|
||||
<Row2>
|
||||
@@ -314,33 +319,46 @@ export const Row2 = styled(Box)`
|
||||
`;
|
||||
|
||||
const ModeIndicator: React.FC<
|
||||
Pick<GalleryBarImplProps, "mode" | "onChangeMode">
|
||||
> = ({ mode, onChangeMode }) => {
|
||||
Pick<
|
||||
GalleryBarImplProps,
|
||||
"showPeopleSectionButton" | "mode" | "onChangeMode"
|
||||
>
|
||||
> = ({ showPeopleSectionButton, mode, onChangeMode }) => {
|
||||
// Mode switcher is not shown in the hidden albums section.
|
||||
if (mode == "hidden-albums") {
|
||||
return <Typography>{t("hidden_albums")}</Typography>;
|
||||
}
|
||||
|
||||
// Show the static mode indicator with only the "Albums" title unless we
|
||||
// come here with the people mode already set. This is because we don't
|
||||
// currently have an empty state for the People mode when ML is not enabled.
|
||||
if (mode == "albums") {
|
||||
// Show the static mode indicator with only the "Albums" title if we have
|
||||
// not been asked to show the people button (there are no other sections to
|
||||
// switch to in such a case).
|
||||
if (!showPeopleSectionButton) {
|
||||
return <Typography>{t("albums")}</Typography>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack direction="row" sx={{ gap: "10px" }}>
|
||||
<AlbumModeButton onClick={() => onChangeMode("albums")}>
|
||||
<ModeButton
|
||||
active={mode == "albums"}
|
||||
onClick={() => onChangeMode("albums")}
|
||||
>
|
||||
<Typography>{t("albums")}</Typography>
|
||||
</AlbumModeButton>
|
||||
<Typography>{t("people")}</Typography>
|
||||
</ModeButton>
|
||||
<ModeButton
|
||||
active={mode == "people"}
|
||||
onClick={() => onChangeMode("people")}
|
||||
>
|
||||
<Typography>{t("people")}</Typography>
|
||||
</ModeButton>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const AlbumModeButton = styled(UnstyledButton)(
|
||||
({ theme }) => `
|
||||
p { color: ${theme.colors.text.muted} }
|
||||
const ModeButton = styled(UnstyledButton, {
|
||||
shouldForwardProp: (propName) => propName != "active",
|
||||
})<{ active: boolean }>(
|
||||
({ active, theme }) => `
|
||||
p { color: ${active ? theme.colors.text.base : theme.colors.text.muted} }
|
||||
p:hover { color: ${theme.colors.text.base} }
|
||||
`,
|
||||
);
|
||||
@@ -407,7 +425,7 @@ type ItemData =
|
||||
| {
|
||||
type: "people";
|
||||
people: Person[];
|
||||
activePerson: Person;
|
||||
activePerson: Person | undefined;
|
||||
onSelectPerson: (person: Person) => void;
|
||||
};
|
||||
|
||||
@@ -573,7 +591,7 @@ const ActiveIndicator = styled("div")`
|
||||
|
||||
interface PersonCardProps {
|
||||
person: Person;
|
||||
activePerson: Person;
|
||||
activePerson: Person | undefined;
|
||||
onSelectPerson: (person: Person) => void;
|
||||
}
|
||||
|
||||
@@ -586,10 +604,11 @@ const PersonCard: React.FC<PersonCardProps> = ({
|
||||
<ItemCard
|
||||
TileComponent={BarItemTile}
|
||||
coverFile={person.displayFaceFile}
|
||||
coverFaceID={person.displayFaceID}
|
||||
onClick={() => onSelectPerson(person)}
|
||||
>
|
||||
{person.name && <CardText text={person.name} />}
|
||||
</ItemCard>
|
||||
{activePerson.id === person.id && <ActiveIndicator />}
|
||||
{activePerson?.id === person.id && <ActiveIndicator />}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -31,6 +31,27 @@ import { NameInputDialog } from "../NameInputDialog";
|
||||
import type { GalleryBarImplProps } from "./BarImpl";
|
||||
import { GalleryItemsHeaderAdapter, GalleryItemsSummary } from "./ListHeader";
|
||||
|
||||
/**
|
||||
* Derived UI state backing the gallery when it is in "people" mode.
|
||||
*
|
||||
* This may be different from the actual underlying state since there might be
|
||||
* unsynced data (hidden or deleted that have not yet been synced with remote)
|
||||
* that should be taken into account for the UI state.
|
||||
*/
|
||||
export interface GalleryPeopleState {
|
||||
/**
|
||||
* The currently selected person, if any.
|
||||
*
|
||||
* Whenever this is present, it is guaranteed to be one of the items from
|
||||
* within {@link people}.
|
||||
*/
|
||||
activePerson: Person | undefined;
|
||||
/**
|
||||
* The list of people to show.
|
||||
*/
|
||||
people: Person[];
|
||||
}
|
||||
|
||||
type PeopleHeaderProps = Pick<GalleryBarImplProps, "onSelectPerson"> & {
|
||||
person: Person;
|
||||
appContext: NewAppContextPhotos;
|
||||
@@ -107,7 +128,7 @@ const CGroupPersonOptions: React.FC<CGroupPersonOptionsProps> = ({
|
||||
),
|
||||
close: { text: t("cancel") },
|
||||
proceed: {
|
||||
text: t("RESET"),
|
||||
text: t("reset"),
|
||||
action: doDeletePerson,
|
||||
},
|
||||
buttonDirection: "row",
|
||||
@@ -151,8 +172,8 @@ const CGroupPersonOptions: React.FC<CGroupPersonOptionsProps> = ({
|
||||
<NameInputDialog
|
||||
open={openAddNameInput}
|
||||
onClose={() => setOpenAddNameInput(false)}
|
||||
title={pt("Rename person")}
|
||||
placeholder={t("ENTER_NAME") /* TODO-Cluster */}
|
||||
title={pt("Rename person") /* TODO-Cluster pt()'s */}
|
||||
placeholder={t("enter_name")}
|
||||
initialValue={cgroup.data.name ?? ""}
|
||||
submitButtonTitle={t("rename")}
|
||||
onSubmit={renamePersonUsingName}
|
||||
@@ -210,10 +231,10 @@ const ClusterPersonOptions: React.FC<ClusterPersonOptionsProps> = ({
|
||||
<NameInputDialog
|
||||
open={openNameInput}
|
||||
onClose={() => setOpenNameInput(false)}
|
||||
title={pt("Add person")}
|
||||
placeholder={t("ENTER_NAME") /* TODO-Cluster */}
|
||||
title={pt("Add person") /* TODO-Cluster */}
|
||||
placeholder={t("enter_name")}
|
||||
initialValue={""}
|
||||
submitButtonTitle={t("ADD")}
|
||||
submitButtonTitle={t("add")}
|
||||
onSubmit={addPersonWithName}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
* there.
|
||||
*/
|
||||
|
||||
import { pt } from "@/base/i18n";
|
||||
import type { SearchOption } from "@/new/photos/services/search/types";
|
||||
import { VerticallyCentered } from "@ente/shared/components/Container";
|
||||
import { Typography } from "@mui/material";
|
||||
import { t } from "i18next";
|
||||
import React from "react";
|
||||
@@ -40,3 +42,17 @@ export const SearchResultsHeader: React.FC<SearchResultsHeaderProps> = ({
|
||||
/>
|
||||
</GalleryItemsHeaderAdapter>
|
||||
);
|
||||
|
||||
export const PeopleEmptyState: React.FC = () => (
|
||||
<VerticallyCentered>
|
||||
<Typography
|
||||
color="text.muted"
|
||||
sx={{
|
||||
// Approximately compensate for the hidden section bar
|
||||
paddingBlockEnd: "86px",
|
||||
}}
|
||||
>
|
||||
{pt("People will appear here once indexing completes")}
|
||||
</Typography>
|
||||
</VerticallyCentered>
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import downloadManager from "@/new/photos/services/download";
|
||||
import { type EnteFile } from "@/new/photos/types/file";
|
||||
import { styled } from "@mui/material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { faceCrop } from "../services/ml";
|
||||
|
||||
interface ItemCardProps {
|
||||
/**
|
||||
@@ -16,11 +17,17 @@ interface ItemCardProps {
|
||||
* Optional file whose thumbnail (if any) should be should be shown.
|
||||
*/
|
||||
coverFile?: EnteFile | undefined;
|
||||
/**
|
||||
* Optional ID of a specific face within {@link coverFile} to show.
|
||||
*
|
||||
* Precondition: {@link faceID} must be an ID of a face that belongs to the
|
||||
* given {@link coverFile}.
|
||||
*/
|
||||
coverFaceID?: string | undefined;
|
||||
/**
|
||||
* Optional boolean indicating if the user is currently scrolling.
|
||||
*
|
||||
* This is used as a hint by the cover file downloader to prioritize
|
||||
* downloads.
|
||||
* This is used as a hint by the file downloader to prioritize downloads.
|
||||
*/
|
||||
isScrolling?: boolean;
|
||||
/**
|
||||
@@ -28,25 +35,50 @@ interface ItemCardProps {
|
||||
*/
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic card that can be be used to represent collections, files, people -
|
||||
* anything that (usually) has an associated "cover photo".
|
||||
*
|
||||
* Usually, we provide it a {@link coverFile} prop to set the file whose
|
||||
* thumbnail should be shown in the card. However, an additional
|
||||
* {@link coverFaceID} prop can be used to show the face crop for that specific
|
||||
* face within the cover file.
|
||||
*
|
||||
* Note that while the common use case is to use this with a cover photo (and an
|
||||
* additional cover faceID), both of these are optional and the item card can
|
||||
* also be used as a static component without an associated cover image by
|
||||
* covering it with an opaque overlay.
|
||||
*/
|
||||
export const ItemCard: React.FC<React.PropsWithChildren<ItemCardProps>> = ({
|
||||
TileComponent,
|
||||
coverFile,
|
||||
coverFaceID,
|
||||
isScrolling,
|
||||
onClick,
|
||||
children,
|
||||
}) => {
|
||||
const [coverImageURL, setCoverImageURL] = useState("");
|
||||
const [coverImageURL, setCoverImageURL] = useState<string | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!coverFile) return;
|
||||
void downloadManager
|
||||
.getThumbnailForPreview(coverFile, isScrolling)
|
||||
.then((url) => url && setCoverImageURL(url));
|
||||
}, [coverFile, isScrolling]);
|
||||
|
||||
let didCancel = false;
|
||||
|
||||
if (coverFaceID) {
|
||||
void faceCrop(coverFaceID, coverFile).then(
|
||||
(url) => !didCancel && setCoverImageURL(url),
|
||||
);
|
||||
} else {
|
||||
void downloadManager
|
||||
.getThumbnailForPreview(coverFile, isScrolling)
|
||||
.then((url) => !didCancel && setCoverImageURL(url));
|
||||
}
|
||||
|
||||
return () => {
|
||||
didCancel = true;
|
||||
};
|
||||
}, [coverFile, coverFaceID, isScrolling]);
|
||||
|
||||
return (
|
||||
<TileComponent {...{ onClick }}>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useIsMobileWidth } from "@/base/hooks";
|
||||
import { faceCrop, unidentifiedFaceIDs } from "@/new/photos/services/ml";
|
||||
import { pt } from "@/base/i18n";
|
||||
import { faceCrop, type AnnotatedFaceID } from "@/new/photos/services/ml";
|
||||
import type { Person } from "@/new/photos/services/ml/people";
|
||||
import type { EnteFile } from "@/new/photos/types/file";
|
||||
import { Skeleton, Typography, styled } from "@mui/material";
|
||||
@@ -25,7 +26,7 @@ export const SearchPeopleList: React.FC<SearchPeopleListProps> = ({
|
||||
sx={{ justifyContent: people.length > 3 ? "center" : "start" }}
|
||||
>
|
||||
{people.slice(0, isMobileWidth ? 6 : 7).map((person) => (
|
||||
<SearchPeopleButton
|
||||
<SearchPersonButton
|
||||
key={person.id}
|
||||
onClick={() => onSelectPerson(person)}
|
||||
>
|
||||
@@ -34,7 +35,7 @@ export const SearchPeopleList: React.FC<SearchPeopleListProps> = ({
|
||||
enteFile={person.displayFaceFile}
|
||||
placeholderDimension={87}
|
||||
/>
|
||||
</SearchPeopleButton>
|
||||
</SearchPersonButton>
|
||||
))}
|
||||
</SearchPeopleContainer>
|
||||
);
|
||||
@@ -49,7 +50,7 @@ const SearchPeopleContainer = styled("div")`
|
||||
margin-block-end: 15px;
|
||||
`;
|
||||
|
||||
const SearchPeopleButton = styled(UnstyledButton)(
|
||||
const SearchPersonButton = styled(UnstyledButton)(
|
||||
({ theme }) => `
|
||||
width: 87px;
|
||||
height: 87px;
|
||||
@@ -66,88 +67,132 @@ const SearchPeopleButton = styled(UnstyledButton)(
|
||||
`,
|
||||
);
|
||||
|
||||
const FaceChipContainer = styled("div")`
|
||||
export interface AnnotatedFacePeopleListProps {
|
||||
/**
|
||||
* The {@link EnteFile} whose information we are showing.
|
||||
*/
|
||||
enteFile: EnteFile;
|
||||
/**
|
||||
* The list of faces in the file that are associated with a person.
|
||||
*/
|
||||
annotatedFaceIDs: AnnotatedFaceID[];
|
||||
/**
|
||||
* Called when the user selects a face in the list.
|
||||
*/
|
||||
onSelectFace: (annotatedFaceID: AnnotatedFaceID) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the list of faces in the given file that are associated with a specific
|
||||
* person.
|
||||
*/
|
||||
export const AnnotatedFacePeopleList: React.FC<
|
||||
AnnotatedFacePeopleListProps
|
||||
> = ({ enteFile, annotatedFaceIDs, onSelectFace }) => {
|
||||
if (annotatedFaceIDs.length == 0) return <></>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography variant="large" p={1}>
|
||||
{t("people")}
|
||||
</Typography>
|
||||
<FileFaceList>
|
||||
{annotatedFaceIDs.map((annotatedFaceID) => (
|
||||
<AnnotatedFaceButton
|
||||
key={annotatedFaceID.faceID}
|
||||
onClick={() => onSelectFace(annotatedFaceID)}
|
||||
>
|
||||
<FaceCropImageView
|
||||
faceID={annotatedFaceID.faceID}
|
||||
enteFile={enteFile}
|
||||
placeholderDimension={112}
|
||||
/>
|
||||
</AnnotatedFaceButton>
|
||||
))}
|
||||
</FileFaceList>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const FileFaceList = styled("div")`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
overflow: auto;
|
||||
gap: 5px;
|
||||
margin: 5px;
|
||||
`;
|
||||
|
||||
const FaceChip = styled("div")<{ clickable?: boolean }>`
|
||||
const AnnotatedFaceButton = styled(UnstyledButton)(
|
||||
({ theme }) => `
|
||||
width: 112px;
|
||||
height: 112px;
|
||||
margin: 5px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
cursor: ${({ clickable }) => (clickable ? "pointer" : "normal")};
|
||||
& > img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
:hover {
|
||||
outline: 1px solid ${theme.colors.stroke.faint};
|
||||
outline-offset: 2px;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
export interface PhotoPeopleListProps {
|
||||
file: EnteFile;
|
||||
onSelect?: (person: Person, index: number) => void;
|
||||
}
|
||||
|
||||
export function PhotoPeopleList() {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
interface UnidentifiedFacesProps {
|
||||
export interface UnclusteredFaceListProps {
|
||||
/**
|
||||
* The {@link EnteFile} whose information we are showing.
|
||||
*/
|
||||
enteFile: EnteFile;
|
||||
/**
|
||||
* The list of faces in the file that are not associated with a person.
|
||||
*/
|
||||
faceIDs: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the list of faces in the given file that are not linked to a specific
|
||||
* person ("face cluster").
|
||||
* Show the list of faces in the given file that are not associated with a
|
||||
* specific person.
|
||||
*/
|
||||
export const UnidentifiedFaces: React.FC<UnidentifiedFacesProps> = ({
|
||||
export const UnclusteredFaceList: React.FC<UnclusteredFaceListProps> = ({
|
||||
enteFile,
|
||||
faceIDs,
|
||||
}) => {
|
||||
const [faceIDs, setFaceIDs] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
let didCancel = false;
|
||||
|
||||
const go = async () => {
|
||||
const faceIDs = await unidentifiedFaceIDs(enteFile);
|
||||
!didCancel && setFaceIDs(faceIDs);
|
||||
};
|
||||
|
||||
void go();
|
||||
|
||||
return () => {
|
||||
didCancel = true;
|
||||
};
|
||||
}, [enteFile]);
|
||||
|
||||
if (faceIDs.length == 0) return <></>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography variant="large" p={1}>
|
||||
{t("UNIDENTIFIED_FACES")}
|
||||
{pt("Other faces")}
|
||||
{/*t("UNIDENTIFIED_FACES") TODO-Cluster */}
|
||||
</Typography>
|
||||
<FaceChipContainer>
|
||||
<FileFaceList>
|
||||
{faceIDs.map((faceID) => (
|
||||
<FaceChip key={faceID}>
|
||||
<UnclusteredFace key={faceID}>
|
||||
<FaceCropImageView
|
||||
placeholderDimension={112}
|
||||
{...{ enteFile, faceID }}
|
||||
/>
|
||||
</FaceChip>
|
||||
</UnclusteredFace>
|
||||
))}
|
||||
</FaceChipContainer>
|
||||
</FileFaceList>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const UnclusteredFace = styled("div")`
|
||||
width: 112px;
|
||||
height: 112px;
|
||||
margin: 5px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
& > img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
interface FaceCropImageViewProps {
|
||||
/** The ID of the face to display. */
|
||||
faceID: string;
|
||||
@@ -171,25 +216,22 @@ const FaceCropImageView: React.FC<FaceCropImageViewProps> = ({
|
||||
enteFile,
|
||||
placeholderDimension,
|
||||
}) => {
|
||||
const [objectURL, setObjectURL] = useState<string | undefined>();
|
||||
const [url, setURL] = useState<string | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
let didCancel = false;
|
||||
let thisObjectURL: string | undefined;
|
||||
|
||||
void faceCrop(faceID, enteFile).then((blob) => {
|
||||
if (blob && !didCancel)
|
||||
setObjectURL((thisObjectURL = URL.createObjectURL(blob)));
|
||||
});
|
||||
void faceCrop(faceID, enteFile).then(
|
||||
(url) => !didCancel && setURL(url),
|
||||
);
|
||||
|
||||
return () => {
|
||||
didCancel = true;
|
||||
if (thisObjectURL) URL.revokeObjectURL(thisObjectURL);
|
||||
};
|
||||
}, [faceID, enteFile]);
|
||||
|
||||
return objectURL ? (
|
||||
<img style={{ objectFit: "cover" }} src={objectURL} />
|
||||
return url ? (
|
||||
<img style={{ objectFit: "cover" }} src={url} />
|
||||
) : (
|
||||
<Skeleton
|
||||
variant="circular"
|
||||
|
||||
@@ -144,6 +144,21 @@ class DownloadManagerImpl {
|
||||
return thumb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves with an URL that points to the file's thumbnail.
|
||||
*
|
||||
* The thumbnail will be downloaded (unless {@link localOnly} is true) and
|
||||
* cached.
|
||||
*
|
||||
* The optional {@link localOnly} parameter can be set to indicate that this
|
||||
* is being called as part of a scroll, so the downloader should not attempt
|
||||
* to download the file but should instead fulfill the request from the
|
||||
* cache. This avoids an unbounded flurry of requests on scroll, only
|
||||
* downloading when the position has quiescized.
|
||||
*
|
||||
* The returned URL is actually an object URL, but it should not be revoked
|
||||
* since the download manager caches it for future use.
|
||||
*/
|
||||
async getThumbnailForPreview(
|
||||
file: EnteFile,
|
||||
localOnly = false,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { assertionFailed } from "@/base/assert";
|
||||
import { newNonSecureID } from "@/base/id-worker";
|
||||
import log from "@/base/log";
|
||||
import { ensure } from "@/utils/ensure";
|
||||
@@ -204,18 +205,24 @@ const sortFacesNewestOnesFirst = (
|
||||
const fileForFaceID = new Map(
|
||||
faces.map(({ faceID }) => [
|
||||
faceID,
|
||||
ensure(localFileByID.get(ensure(fileIDFromFaceID(faceID)))),
|
||||
localFileByID.get(ensure(fileIDFromFaceID(faceID))),
|
||||
]),
|
||||
);
|
||||
|
||||
const fileForFace = ({ faceID }: { faceID: string }) =>
|
||||
ensure(fileForFaceID.get(faceID));
|
||||
// In unexpected scenarios, we might run clustering without having the
|
||||
// corresponding EnteFile available locally. This shouldn't happen, so log
|
||||
// an warning, but meanwhile let the clustering proceed by assigning such
|
||||
// files an arbitrary creationTime.
|
||||
const sortTimeForFace = ({ faceID }: { faceID: string }) => {
|
||||
const file = fileForFaceID.get(faceID);
|
||||
if (!file) {
|
||||
assertionFailed(`Did not find a local file for faceID ${faceID}`);
|
||||
return 0;
|
||||
}
|
||||
return file.metadata.creationTime;
|
||||
};
|
||||
|
||||
return faces.sort(
|
||||
(a, b) =>
|
||||
fileForFace(b).metadata.creationTime -
|
||||
fileForFace(a).metadata.creationTime,
|
||||
);
|
||||
return faces.sort((a, b) => sortTimeForFace(b) - sortTimeForFace(a));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -294,10 +301,6 @@ const clusterBatchLinear = async (
|
||||
if (csim > nnCosineSimilarity && csim >= threshold) {
|
||||
nnIndex = j;
|
||||
nnCosineSimilarity = csim;
|
||||
|
||||
// If we've find something above our early exit threshold, stop
|
||||
// looking for a better match (A way to speed up clustering).
|
||||
if (csim >= 0.9) break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,22 +343,22 @@ export const reconcileClusters = async (
|
||||
const clusterByID = new Map(clusters.map((c) => [c.id, c]));
|
||||
|
||||
// Get the existing remote cluster groups.
|
||||
const cgroupEntities = await savedCGroups();
|
||||
const cgroups = await savedCGroups();
|
||||
|
||||
// Find the cgroups that have changed since we started.
|
||||
const changedCGroupEntities = cgroupEntities
|
||||
.map((cgroupEntity) => {
|
||||
for (const oldCluster of cgroupEntity.data.assigned) {
|
||||
const changedCGroups = cgroups
|
||||
.map((cgroup) => {
|
||||
for (const oldCluster of cgroup.data.assigned) {
|
||||
// The clustering algorithm does not remove any existing faces, it
|
||||
// can only add new ones to the cluster. So we can use the count as
|
||||
// an indication if something changed.
|
||||
const newCluster = ensure(clusterByID.get(oldCluster.id));
|
||||
if (oldCluster.faces.length != newCluster.faces.length) {
|
||||
return {
|
||||
...cgroupEntity,
|
||||
...cgroup,
|
||||
data: {
|
||||
...cgroupEntity.data,
|
||||
assigned: cgroupEntity.data.assigned.map(({ id }) =>
|
||||
...cgroup.data,
|
||||
assigned: cgroup.data.assigned.map(({ id }) =>
|
||||
ensure(clusterByID.get(id)),
|
||||
),
|
||||
},
|
||||
@@ -367,19 +370,15 @@ export const reconcileClusters = async (
|
||||
.filter((g) => !!g);
|
||||
|
||||
// Update remote if needed.
|
||||
if (changedCGroupEntities.length) {
|
||||
await updateOrCreateUserEntities(
|
||||
"cgroup",
|
||||
changedCGroupEntities,
|
||||
masterKey,
|
||||
);
|
||||
log.info(`Updated ${changedCGroupEntities.length} remote cgroups`);
|
||||
if (changedCGroups.length) {
|
||||
await updateOrCreateUserEntities("cgroup", changedCGroups, masterKey);
|
||||
log.info(`Updated ${changedCGroups.length} remote cgroups`);
|
||||
}
|
||||
|
||||
// Find which clusters are part of remote cgroups.
|
||||
const isRemoteClusterID = new Set<string>();
|
||||
for (const cgroupEntity of cgroupEntities) {
|
||||
for (const cluster of cgroupEntity.data.assigned)
|
||||
for (const cgroup of cgroups) {
|
||||
for (const cluster of cgroup.data.assigned)
|
||||
isRemoteClusterID.add(cluster.id);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import { isDesktop } from "@/base/app";
|
||||
import { blobCache } from "@/base/blob-cache";
|
||||
import { ensureElectron } from "@/base/electron";
|
||||
import { isDevBuild } from "@/base/env";
|
||||
import log from "@/base/log";
|
||||
import { masterKeyFromSession } from "@/base/session-store";
|
||||
import type { Electron } from "@/base/types/ipc";
|
||||
@@ -15,7 +14,6 @@ import type { EnteFile } from "@/new/photos/types/file";
|
||||
import { ensure } from "@/utils/ensure";
|
||||
import { throttled } from "@/utils/promise";
|
||||
import { proxy, transfer } from "comlink";
|
||||
import { isInternalUser } from "../feature-flags";
|
||||
import { getRemoteFlag, updateRemoteFlag } from "../remote-store";
|
||||
import { setSearchPeople } from "../search";
|
||||
import type { UploadItem } from "../upload/types";
|
||||
@@ -88,6 +86,9 @@ class MLState {
|
||||
/**
|
||||
* Snapshot of the {@link Person}s returned by the {@link peopleSnapshot}
|
||||
* function.
|
||||
*
|
||||
* It will be `undefined` only if ML is disabled. Otherwise, it will be an
|
||||
* empty array even if the snapshot is pending its first sync.
|
||||
*/
|
||||
peopleSnapshot: Person[] | undefined;
|
||||
|
||||
@@ -96,6 +97,13 @@ class MLState {
|
||||
* whose faces we are regenerating.
|
||||
*/
|
||||
inFlightFaceCropRegens = new Map<number, Promise<void>>();
|
||||
|
||||
/**
|
||||
* Cached object URLs to face crops that we have previously vended out.
|
||||
*
|
||||
* The cache is only cleared on logout.
|
||||
*/
|
||||
faceCropObjectURLCache = new Map<string, string>();
|
||||
}
|
||||
|
||||
/** State shared by the functions in this module. See {@link MLState}. */
|
||||
@@ -107,7 +115,7 @@ const worker = () =>
|
||||
|
||||
const createComlinkWorker = async () => {
|
||||
const electron = ensureElectron();
|
||||
const delegate = { workerDidUpdateStatus };
|
||||
const delegate = { workerDidUpdateStatus, workerDidUnawaitedIndex };
|
||||
|
||||
// Obtain a message port from the Electron layer.
|
||||
const messagePort = await createMLWorker(electron);
|
||||
@@ -180,6 +188,7 @@ export const isMLSupported = isDesktop;
|
||||
*/
|
||||
export const initML = () => {
|
||||
_state.isMLEnabled = isMLEnabledLocal();
|
||||
resetPeopleSnapshot();
|
||||
};
|
||||
|
||||
export const logoutML = async () => {
|
||||
@@ -188,6 +197,9 @@ export const logoutML = async () => {
|
||||
// execution contexts], it gets called first in the logout sequence, and
|
||||
// then this function (`logoutML`) gets called at a later point in time.
|
||||
|
||||
[..._state.faceCropObjectURLCache.values()].forEach((url) =>
|
||||
URL.revokeObjectURL(url),
|
||||
);
|
||||
_state = new MLState();
|
||||
await clearMLDB();
|
||||
};
|
||||
@@ -213,6 +225,7 @@ export const enableML = async () => {
|
||||
setIsMLEnabledLocal(true);
|
||||
_state.isMLEnabled = true;
|
||||
setInterimScheduledStatus();
|
||||
resetPeopleSnapshot();
|
||||
// Trigger updates, but don't wait for them to finish.
|
||||
void updateMLStatusSnapshot().then(mlSync);
|
||||
};
|
||||
@@ -230,6 +243,7 @@ export const disableML = async () => {
|
||||
_state.isSyncing = false;
|
||||
await terminateMLWorker();
|
||||
triggerStatusUpdate();
|
||||
resetPeopleSnapshot();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -313,30 +327,32 @@ export const mlSync = async () => {
|
||||
|
||||
// Dependency order for the sync
|
||||
//
|
||||
// files -> faces -> cgroups -> clusters
|
||||
// files -> faces -> cgroups -> clusters -> people
|
||||
//
|
||||
|
||||
const w = await worker();
|
||||
|
||||
// Fetch indexes, or index locally if needed.
|
||||
await w.index();
|
||||
await (await worker()).index();
|
||||
|
||||
// TODO-Cluster
|
||||
if (await wipClusterEnable()) {
|
||||
const masterKey = await masterKeyFromSession();
|
||||
|
||||
// Fetch existing cgroups from remote.
|
||||
await pullUserEntities("cgroup", masterKey);
|
||||
|
||||
// Generate or update local clusters.
|
||||
await w.clusterFaces(masterKey);
|
||||
}
|
||||
|
||||
await updatePeople();
|
||||
await updateClustersAndPeople();
|
||||
|
||||
_state.isSyncing = false;
|
||||
};
|
||||
|
||||
const workerDidUnawaitedIndex = () => void updateClustersAndPeople();
|
||||
|
||||
const updateClustersAndPeople = async () => {
|
||||
const masterKey = await masterKeyFromSession();
|
||||
|
||||
// Fetch existing cgroups from remote.
|
||||
await pullUserEntities("cgroup", masterKey);
|
||||
|
||||
// Generate or update local clusters.
|
||||
await (await worker()).clusterFaces(masterKey);
|
||||
|
||||
// Update the people shown in the UI.
|
||||
await updatePeople();
|
||||
};
|
||||
|
||||
/**
|
||||
* Run indexing on a file which was uploaded from this client.
|
||||
*
|
||||
@@ -361,14 +377,6 @@ export const indexNewUpload = (enteFile: EnteFile, uploadItem: UploadItem) => {
|
||||
void worker().then((w) => w.onUpload(enteFile, uploadItem));
|
||||
};
|
||||
|
||||
/**
|
||||
* WIP! Don't enable, dragon eggs are hatching here.
|
||||
* TODO-Cluster
|
||||
*/
|
||||
export const wipClusterEnable = async (): Promise<boolean> =>
|
||||
(!!process.env.NEXT_PUBLIC_ENTE_WIP_CL && isDevBuild) ||
|
||||
(await isInternalUser());
|
||||
|
||||
export type MLStatus =
|
||||
| { phase: "disabled" /* The ML remote flag is off */ }
|
||||
| {
|
||||
@@ -539,16 +547,25 @@ export const peopleSubscribe = (onChange: () => void): (() => void) => {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* If ML is enabled, set the people snapshot to an empty array to indicate that
|
||||
* ML is enabled, but we're still reading in the set of people.
|
||||
*
|
||||
* Otherwise, if ML is disabled, set the people snapshot to `undefined`.
|
||||
*/
|
||||
const resetPeopleSnapshot = () =>
|
||||
setPeopleSnapshot(_state.isMLEnabled ? [] : undefined);
|
||||
|
||||
/**
|
||||
* Return the last known, cached {@link people}.
|
||||
*
|
||||
* This, along with {@link peopleSnapshot}, is meant to be used as arguments to
|
||||
* React's {@link useSyncExternalStore}.
|
||||
*
|
||||
* A return value of `undefined` indicates that we're either still loading the
|
||||
* initial list of people, or that the user has ML disabled and thus doesn't
|
||||
* have any people (this is distinct from the case where the user has ML enabled
|
||||
* but doesn't have any named "person" clusters so far).
|
||||
* A return value of `undefined` indicates that ML is disabled. In all other
|
||||
* cases, the list will be either empty (if we're either still loading the
|
||||
* initial list of people, or if the user doesn't have any people), or, well,
|
||||
* non-empty.
|
||||
*/
|
||||
export const peopleSnapshot = () => _state.peopleSnapshot;
|
||||
|
||||
@@ -589,19 +606,72 @@ export const clipMatches = (
|
||||
): Promise<CLIPMatches | undefined> =>
|
||||
worker().then((w) => w.clipMatches(searchPhrase));
|
||||
|
||||
/** A face ID annotated with the ID of the person to which it is associated. */
|
||||
export interface AnnotatedFaceID {
|
||||
faceID: string;
|
||||
personID: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the IDs of all the faces in the given {@link enteFile} that are not
|
||||
* associated with a person cluster.
|
||||
* List of faces found in a file
|
||||
*
|
||||
* It is actually a pair of lists, one annotated by the person ids, and one with
|
||||
* just the face ids.
|
||||
*/
|
||||
export const unidentifiedFaceIDs = async (
|
||||
export interface AnnotatedFacesForFile {
|
||||
/**
|
||||
* A list of {@link AnnotatedFaceID}s for all faces in the file that are
|
||||
* also associated with a {@link Person}.
|
||||
*/
|
||||
annotatedFaceIDs: AnnotatedFaceID[];
|
||||
/* A list of the remaining face (ids). */
|
||||
otherFaceIDs: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of faces found in the given {@link enteFile}.
|
||||
*/
|
||||
export const getAnnotatedFacesForFile = async (
|
||||
enteFile: EnteFile,
|
||||
): Promise<string[]> => {
|
||||
): Promise<AnnotatedFacesForFile> => {
|
||||
const annotatedFaceIDs: AnnotatedFaceID[] = [];
|
||||
const otherFaceIDs: string[] = [];
|
||||
|
||||
const index = await getFaceIndex(enteFile.id);
|
||||
return index?.faces.map((f) => f.faceID) ?? [];
|
||||
if (!index) return { annotatedFaceIDs, otherFaceIDs };
|
||||
|
||||
const people = _state.peopleSnapshot ?? [];
|
||||
|
||||
const faceIDToPersonID = new Map<string, string>();
|
||||
for (const person of people) {
|
||||
let faceIDs: string[];
|
||||
if (person.type == "cgroup") {
|
||||
faceIDs = person.cgroup.data.assigned.map((c) => c.faces).flat();
|
||||
} else {
|
||||
faceIDs = person.cluster.faces;
|
||||
}
|
||||
for (const faceID of faceIDs) {
|
||||
faceIDToPersonID.set(faceID, person.id);
|
||||
}
|
||||
}
|
||||
|
||||
for (const { faceID } of index.faces) {
|
||||
const personID = faceIDToPersonID.get(faceID);
|
||||
if (personID) {
|
||||
annotatedFaceIDs.push({ faceID, personID });
|
||||
} else {
|
||||
otherFaceIDs.push(faceID);
|
||||
}
|
||||
}
|
||||
|
||||
return { annotatedFaceIDs, otherFaceIDs };
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the cached face crop for the given face, regenerating it if needed.
|
||||
* Return a URL to the face crop for the given face, regenerating it if needed.
|
||||
*
|
||||
* The resultant URL is cached (both the object URL itself, and the underlying
|
||||
* file crop blob used to generete it).
|
||||
*
|
||||
* @param faceID The id of the face whose face crop we want.
|
||||
*
|
||||
@@ -617,8 +687,17 @@ export const faceCrop = async (faceID: string, enteFile: EnteFile) => {
|
||||
|
||||
await inFlight;
|
||||
|
||||
const cache = await blobCache("face-crops");
|
||||
return cache.get(faceID);
|
||||
let url = _state.faceCropObjectURLCache.get(faceID);
|
||||
if (!url) {
|
||||
const cache = await blobCache("face-crops");
|
||||
const blob = await cache.get(faceID);
|
||||
if (blob) {
|
||||
url = URL.createObjectURL(blob);
|
||||
if (url) _state.faceCropObjectURLCache.set(faceID, url);
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { wipClusterEnable } from ".";
|
||||
import type { EnteFile } from "../../types/file";
|
||||
import { getLocalFiles } from "../files";
|
||||
import { savedCGroups, type CGroup } from "../user-entity";
|
||||
@@ -138,8 +137,6 @@ export type Person = (
|
||||
* reference.
|
||||
*/
|
||||
export const reconstructPeople = async (): Promise<Person[]> => {
|
||||
if (!(await wipClusterEnable())) return [];
|
||||
|
||||
const files = await getLocalFiles("normal");
|
||||
const fileByID = new Map(files.map((f) => [f.id, f]));
|
||||
|
||||
@@ -164,6 +161,20 @@ export const reconstructPeople = async (): Promise<Person[]> => {
|
||||
}
|
||||
}
|
||||
|
||||
// Return annotated "person faces" corresponding to the given face ids,
|
||||
// sorting them by the creation time of the file they belong to.
|
||||
//
|
||||
// Within the same file, sort by the face score.
|
||||
const personFacesSortedNewestFirst = (faceIDs: string[]) =>
|
||||
faceIDs
|
||||
.map((faceID) => personFaceByID.get(faceID))
|
||||
.filter((pf) => !!pf)
|
||||
.sort((a, b) => {
|
||||
const at = a.file.metadata.creationTime;
|
||||
const bt = b.file.metadata.creationTime;
|
||||
return bt == at ? b.score - a.score : bt - at;
|
||||
});
|
||||
|
||||
// Help out tsc.
|
||||
type Interim = (Person | undefined)[];
|
||||
|
||||
@@ -177,18 +188,19 @@ export const reconstructPeople = async (): Promise<Person[]> => {
|
||||
// in the UI.
|
||||
if (isHidden) return undefined;
|
||||
|
||||
// Older versions of the mobile app marked hidden cgroups by setting
|
||||
// their name to an empty string.
|
||||
if (!name) return undefined;
|
||||
|
||||
// Person faces from all the clusters assigned to this cgroup, sorted by
|
||||
// their score.
|
||||
const faces = assigned
|
||||
.map(({ faces }) =>
|
||||
faces.map((id) => personFaceByID.get(id)).filter((f) => !!f),
|
||||
)
|
||||
.flat()
|
||||
.sort((a, b) => b.score - a.score);
|
||||
// recency (then score).
|
||||
const faces = personFacesSortedNewestFirst(
|
||||
assigned.map(({ faces }) => faces).flat(),
|
||||
);
|
||||
|
||||
// Ignore this cgroup if we don't have visible faces left in it.
|
||||
const highestScoringFace = faces[0];
|
||||
if (!highestScoringFace) return undefined;
|
||||
const mostRecentFace = faces[0];
|
||||
if (!mostRecentFace) return undefined;
|
||||
|
||||
// IDs of the files containing this face.
|
||||
const fileIDs = [...new Set(faces.map((f) => f.file.id))];
|
||||
@@ -207,8 +219,8 @@ export const reconstructPeople = async (): Promise<Person[]> => {
|
||||
displayFaceID = avatarFaceID;
|
||||
displayFaceFile = avatarFile;
|
||||
} else {
|
||||
displayFaceID = highestScoringFace.faceID;
|
||||
displayFaceFile = highestScoringFace.file;
|
||||
displayFaceID = mostRecentFace.faceID;
|
||||
displayFaceFile = mostRecentFace.file;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -225,32 +237,32 @@ export const reconstructPeople = async (): Promise<Person[]> => {
|
||||
// Convert local-only clusters to people.
|
||||
const localClusters = await savedFaceClusters();
|
||||
const clusterPeople: Interim = localClusters.map((cluster) => {
|
||||
const faces = cluster.faces
|
||||
.map((id) => personFaceByID.get(id))
|
||||
.filter((f) => !!f);
|
||||
const faces = personFacesSortedNewestFirst(cluster.faces);
|
||||
|
||||
// Ignore this cluster if we don't have visible faces left in it.
|
||||
const mostRecentFace = faces[0];
|
||||
if (!mostRecentFace) return undefined;
|
||||
|
||||
// Ignore clusters with too few visible faces.
|
||||
if (faces.length < 10) return undefined;
|
||||
|
||||
const topFace = faces.reduce((top, face) =>
|
||||
top.score > face.score ? top : face,
|
||||
);
|
||||
|
||||
return {
|
||||
type: "cluster",
|
||||
cluster,
|
||||
id: cluster.id,
|
||||
name: undefined,
|
||||
fileIDs: [...new Set(faces.map((f) => f.file.id))],
|
||||
displayFaceID: topFace.faceID,
|
||||
displayFaceFile: topFace.file,
|
||||
displayFaceID: mostRecentFace.faceID,
|
||||
displayFaceFile: mostRecentFace.file,
|
||||
};
|
||||
});
|
||||
|
||||
return cgroupPeople
|
||||
.concat(clusterPeople)
|
||||
.filter((c) => !!c)
|
||||
.sort((a, b) => b.fileIDs.length - a.fileIDs.length);
|
||||
const sorted = (ps: Interim) =>
|
||||
ps
|
||||
.filter((c) => !!c)
|
||||
.sort((a, b) => b.fileIDs.length - a.fileIDs.length);
|
||||
|
||||
return sorted(cgroupPeople).concat(sorted(clusterPeople));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,19 @@ export interface MLWorkerDelegate {
|
||||
* indicating the indexing or clustering status to be updated.
|
||||
*/
|
||||
workerDidUpdateStatus: () => void;
|
||||
/**
|
||||
* Called when the worker indexes some files, but then notices that the main
|
||||
* thread was not awaiting the indexing (e.g. it was not initiated by the
|
||||
* main thread during a sync, but happened because of a live upload).
|
||||
*
|
||||
* In such cases, it uses this method to inform the main thread that some
|
||||
* files were indexed, so that it can update any dependent state (e.g.
|
||||
* clusters).
|
||||
*
|
||||
* It doesn't always call this because otherwise the main thread would need
|
||||
* some extra code to avoid updating the dependent state twice.
|
||||
*/
|
||||
workerDidUnawaitedIndex: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -109,7 +109,13 @@ export class MLWorker {
|
||||
private liveQ: IndexableItem[] = [];
|
||||
private idleTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
private idleDuration = idleDurationStart; /* unit: seconds */
|
||||
private onNextIdles: (() => void)[] = [];
|
||||
/** Resolvers for pending promises returned from calls to {@link index}. */
|
||||
private onNextIdles: ((count: number) => void)[] = [];
|
||||
/**
|
||||
* Number of items processed since the last time {@link onNextIdles} was
|
||||
* drained.
|
||||
*/
|
||||
private countSinceLastIdle = 0;
|
||||
|
||||
/**
|
||||
* Initialize a new {@link MLWorker}.
|
||||
@@ -140,9 +146,12 @@ export class MLWorker {
|
||||
* During a backfill, we first attempt to fetch ML data for files which
|
||||
* don't have that data locally. If on fetching we find what we need, we
|
||||
* save it locally. Otherwise we index them.
|
||||
*
|
||||
* @return The count of items processed since the last last time we were
|
||||
* idle.
|
||||
*/
|
||||
index() {
|
||||
const nextIdle = new Promise<void>((resolve) =>
|
||||
const nextIdle = new Promise<number>((resolve) =>
|
||||
this.onNextIdles.push(resolve),
|
||||
);
|
||||
this.wakeUp();
|
||||
@@ -225,17 +234,23 @@ export class MLWorker {
|
||||
// Use the liveQ if present, otherwise get the next batch to backfill.
|
||||
const items = liveQ.length ? liveQ : await this.backfillQ();
|
||||
|
||||
const allSuccess = await indexNextBatch(
|
||||
items,
|
||||
ensure(this.electron),
|
||||
this.delegate,
|
||||
);
|
||||
if (allSuccess) {
|
||||
// Everything is running smoothly. Reset the idle duration.
|
||||
this.idleDuration = idleDurationStart;
|
||||
// And tick again.
|
||||
scheduleTick();
|
||||
return;
|
||||
this.countSinceLastIdle += items.length;
|
||||
|
||||
// If there is items remaining,
|
||||
if (items.length > 0) {
|
||||
// Index them.
|
||||
const allSuccess = await indexNextBatch(
|
||||
items,
|
||||
ensure(this.electron),
|
||||
this.delegate,
|
||||
);
|
||||
if (allSuccess) {
|
||||
// Everything is running smoothly. Reset the idle duration.
|
||||
this.idleDuration = idleDurationStart;
|
||||
// And tick again.
|
||||
scheduleTick();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We come here in three scenarios - either there is nothing left to do,
|
||||
@@ -255,8 +270,16 @@ export class MLWorker {
|
||||
|
||||
// Resolve any awaiting promises returned from `index`.
|
||||
const onNextIdles = this.onNextIdles;
|
||||
const countSinceLastIdle = this.countSinceLastIdle;
|
||||
this.onNextIdles = [];
|
||||
onNextIdles.forEach((f) => f());
|
||||
this.countSinceLastIdle = 0;
|
||||
onNextIdles.forEach((f) => f(countSinceLastIdle));
|
||||
|
||||
// If no one was waiting, then let the main thread know via a different
|
||||
// channel so that it can update the clusters and people.
|
||||
if (onNextIdles.length == 0 && countSinceLastIdle > 0) {
|
||||
this.delegate?.workerDidUnawaitedIndex();
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the next batch of items to backfill (if any). */
|
||||
@@ -321,13 +344,13 @@ export class MLWorker {
|
||||
expose(MLWorker);
|
||||
|
||||
/**
|
||||
* Find out files which need to be indexed. Then index the next batch of them.
|
||||
* Index the given batch of items.
|
||||
*
|
||||
* Returns `false` to indicate that either an error occurred, or there are no
|
||||
* more files to process, or that we cannot currently process files.
|
||||
* Returns `false` to indicate that either an error occurred, or that we cannot
|
||||
* currently process files since we don't have network connectivity.
|
||||
*
|
||||
* Which means that when it returns true, all is well and there are more
|
||||
* things pending to process, so we should chug along at full speed.
|
||||
* Which means that when it returns true, all is well and if there are more
|
||||
* things pending to process, we should chug along at full speed.
|
||||
*/
|
||||
const indexNextBatch = async (
|
||||
items: IndexableItem[],
|
||||
@@ -342,9 +365,6 @@ const indexNextBatch = async (
|
||||
return false;
|
||||
}
|
||||
|
||||
// Nothing to do.
|
||||
if (items.length == 0) return false;
|
||||
|
||||
// Keep track if any of the items failed.
|
||||
let allSuccess = true;
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ function RecoveryKey({ somethingWentWrong, ...props }: Props) {
|
||||
fullWidth
|
||||
>
|
||||
<DialogTitleWithCloseButton onClose={props.onHide}>
|
||||
{t("RECOVERY_KEY")}
|
||||
{t("recovery_key")}
|
||||
</DialogTitleWithCloseButton>
|
||||
<DialogContent>
|
||||
<Typography mb={3}>{t("RECOVERY_KEY_DESCRIPTION")}</Typography>
|
||||
@@ -81,10 +81,10 @@ function RecoveryKey({ somethingWentWrong, ...props }: Props) {
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button color="secondary" size="large" onClick={props.onHide}>
|
||||
{t("SAVE_LATER")}
|
||||
{t("do_this_later")}
|
||||
</Button>
|
||||
<Button color="accent" size="large" onClick={onSaveClick}>
|
||||
{t("SAVE")}
|
||||
{t("save_key")}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
Reference in New Issue
Block a user