Compare commits
197 Commits
auth-v4.0.
...
photos-v0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa361904f6 | ||
|
|
00e75c0fb2 | ||
|
|
026ab8dcc6 | ||
|
|
7d42ed37e1 | ||
|
|
a5d01a9ffe | ||
|
|
675b7f6cea | ||
|
|
772373580a | ||
|
|
f0a19e38aa | ||
|
|
d1d6590547 | ||
|
|
03da960c33 | ||
|
|
2bdc010849 | ||
|
|
c160afc6de | ||
|
|
f971b968af | ||
|
|
c8468efd20 | ||
|
|
9515cf70f5 | ||
|
|
963650db4b | ||
|
|
a2841a8af2 | ||
|
|
6a6db0813f | ||
|
|
66fad15743 | ||
|
|
eb4d77bd24 | ||
|
|
e2c0aed2e4 | ||
|
|
5a5e046192 | ||
|
|
42a6e3ac25 | ||
|
|
7fdb8a6dae | ||
|
|
ef63d4c7a0 | ||
|
|
60b9d1e43f | ||
|
|
c453c7dc81 | ||
|
|
5c41e8ad3c | ||
|
|
53c706fba7 | ||
|
|
27a34a08f4 | ||
|
|
6bea1fa0f5 | ||
|
|
a97ca411d7 | ||
|
|
636cd1395c | ||
|
|
6e23e5e453 | ||
|
|
77f5d21dad | ||
|
|
5d210ab740 | ||
|
|
8d14997f36 | ||
|
|
b6059273fb | ||
|
|
1dde716d26 | ||
|
|
8629212584 | ||
|
|
08cf14a72b | ||
|
|
fe5da2ee8b | ||
|
|
f255ded0b6 | ||
|
|
59bf51346c | ||
|
|
3288f3250b | ||
|
|
1eb5eaece9 | ||
|
|
08f84c9cf8 | ||
|
|
6d969ab72a | ||
|
|
da8326229c | ||
|
|
05763a5d83 | ||
|
|
d7e2330f20 | ||
|
|
99ba5a31d3 | ||
|
|
2aaa23312b | ||
|
|
cc262aad0c | ||
|
|
72c93a1703 | ||
|
|
2f27ae7b19 | ||
|
|
039256cb05 | ||
|
|
167c4efc40 | ||
|
|
5b73eee14c | ||
|
|
eafc8fc4cb | ||
|
|
370d4af008 | ||
|
|
4bb6aa2b39 | ||
|
|
01f31c352b | ||
|
|
50c60dff1c | ||
|
|
7bdbaec443 | ||
|
|
57d245d9e0 | ||
|
|
8a953cab88 | ||
|
|
2827a166dc | ||
|
|
4e04739d54 | ||
|
|
7a60b1e15e | ||
|
|
e2e374fbf4 | ||
|
|
71adb1e366 | ||
|
|
393878a52e | ||
|
|
7644900bd8 | ||
|
|
e70f9b5ccd | ||
|
|
a37ff3cf57 | ||
|
|
9235e41855 | ||
|
|
71369bf5c9 | ||
|
|
924f5ce19b | ||
|
|
4d4b3f8bef | ||
|
|
27a0d7707e | ||
|
|
57ea097a5d | ||
|
|
2c0f2d43e7 | ||
|
|
968067d6aa | ||
|
|
de6a494da7 | ||
|
|
e13f2a379a | ||
|
|
4c0c05a54a | ||
|
|
ac04ceadce | ||
|
|
91127b6ce5 | ||
|
|
f95cc1f135 | ||
|
|
64d7959c95 | ||
|
|
9a444b4881 | ||
|
|
795187177d | ||
|
|
54c5c2ce7e | ||
|
|
1a966fdedd | ||
|
|
6f9cd84b6d | ||
|
|
4506e5b6d9 | ||
|
|
1c74eae132 | ||
|
|
262ff01999 | ||
|
|
1daa0f3e07 | ||
|
|
c4931de42e | ||
|
|
77cdf673a9 | ||
|
|
7ff5f74fa6 | ||
|
|
57a425e14c | ||
|
|
642c85fe59 | ||
|
|
0e046b9c8a | ||
|
|
be222f53bf | ||
|
|
0498c70bad | ||
|
|
08fcc02282 | ||
|
|
aef32027a1 | ||
|
|
f9cbce66c0 | ||
|
|
39d39eb195 | ||
|
|
b052026526 | ||
|
|
8a79ae9b96 | ||
|
|
dbd160c135 | ||
|
|
7634ba0dea | ||
|
|
840ba2803e | ||
|
|
3093894b12 | ||
|
|
41d5960d1f | ||
|
|
fb63b1d832 | ||
|
|
68c93537d3 | ||
|
|
0f73a68681 | ||
|
|
306bc56c21 | ||
|
|
feec9a475b | ||
|
|
8314cda12a | ||
|
|
4cc8be748c | ||
|
|
5a33820877 | ||
|
|
830f1b9c18 | ||
|
|
c81cf5a432 | ||
|
|
f71c3f4171 | ||
|
|
5b99902d68 | ||
|
|
2569337be8 | ||
|
|
87f7d3a484 | ||
|
|
8fa6adb16a | ||
|
|
00c9572b43 | ||
|
|
9424b7a65e | ||
|
|
af680b6da3 | ||
|
|
3826c1f957 | ||
|
|
3b6bee6042 | ||
|
|
2163a5fbea | ||
|
|
8e485bfe39 | ||
|
|
c361fcbff4 | ||
|
|
ba063bf4a7 | ||
|
|
82c0bbb879 | ||
|
|
711f31991d | ||
|
|
9c0d9ac538 | ||
|
|
2521cd6d31 | ||
|
|
b5db5e2b83 | ||
|
|
7d52b60cd9 | ||
|
|
08765ccd39 | ||
|
|
3dcf5fa860 | ||
|
|
cda925fc80 | ||
|
|
7b2206161e | ||
|
|
bbd7be4423 | ||
|
|
c8ab6be9f8 | ||
|
|
5609309660 | ||
|
|
fa19254bbc | ||
|
|
70476b2011 | ||
|
|
7dd9d8aef3 | ||
|
|
13727b9a96 | ||
|
|
6decb15be7 | ||
|
|
a93c43d341 | ||
|
|
40b1cdcabb | ||
|
|
f5ee46189c | ||
|
|
48cab57d53 | ||
|
|
7c867f94bf | ||
|
|
3465253fcc | ||
|
|
61b324ca05 | ||
|
|
cb57351951 | ||
|
|
3f4250dab3 | ||
|
|
1efbbf2b2f | ||
|
|
1ff21b3d8e | ||
|
|
31ffc5bee5 | ||
|
|
6cd43707a8 | ||
|
|
3324019b38 | ||
|
|
cc02236ca9 | ||
|
|
ece4980d94 | ||
|
|
47b6e758d2 | ||
|
|
53c19bc64e | ||
|
|
bd232c151a | ||
|
|
8970074f80 | ||
|
|
27d901bc60 | ||
|
|
e87a6a5106 | ||
|
|
7f132b1827 | ||
|
|
b763cab1ba | ||
|
|
15a7e0b805 | ||
|
|
f702a93031 | ||
|
|
432ef74101 | ||
|
|
dd5dae2833 | ||
|
|
2c0739e1d1 | ||
|
|
3449021272 | ||
|
|
c01a439b81 | ||
|
|
1d5cca6ee1 | ||
|
|
821787f81d | ||
|
|
bc7704916a | ||
|
|
7ed620d817 | ||
|
|
1bfa7b1998 |
@@ -618,7 +618,8 @@
|
||||
{
|
||||
"title": "T-Mobile",
|
||||
"altNames": [
|
||||
"T Mobile"
|
||||
"T Mobile",
|
||||
"T-Mobile ID"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -5,8 +5,10 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
// Add more language to the list only when at least 90% of the strings are
|
||||
// translated in the corresponding language.
|
||||
const List<Locale> appSupportedLocales = <Locale>[
|
||||
Locale('ar'),
|
||||
Locale('bg'),
|
||||
Locale('de'),
|
||||
Locale('el'),
|
||||
Locale('en'),
|
||||
Locale('es', 'ES'),
|
||||
Locale('fa'),
|
||||
@@ -17,8 +19,10 @@ const List<Locale> appSupportedLocales = <Locale>[
|
||||
Locale('pl'),
|
||||
Locale('pt', 'BR'),
|
||||
Locale('ru'),
|
||||
Locale('sk'),
|
||||
Locale('tr'),
|
||||
Locale('uk'),
|
||||
Locale('vi'),
|
||||
Locale("zh", "CN"),
|
||||
];
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:ente_auth/ente_theme_data.dart';
|
||||
import 'package:ente_auth/events/trigger_logout_event.dart';
|
||||
import "package:ente_auth/l10n/l10n.dart";
|
||||
import 'package:ente_auth/locale.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/theme/text_style.dart';
|
||||
import 'package:ente_auth/ui/account/email_entry_page.dart';
|
||||
import 'package:ente_auth/ui/account/login_page.dart';
|
||||
@@ -50,7 +49,6 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
await autoLogoutAlert(context);
|
||||
});
|
||||
super.initState();
|
||||
PreferenceService.instance.configureDefaults().ignore();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -57,12 +57,6 @@ class PreferenceService {
|
||||
await _prefs.setBool(kCompactMode, value);
|
||||
}
|
||||
|
||||
Future<void> configureDefaults() async {
|
||||
if (!_prefs.containsKey(kCompactMode)) {
|
||||
await _prefs.setBool(kCompactMode, true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setHideCodes(bool value) async {
|
||||
await _prefs.setBool(kShouldHideCodesKey, value);
|
||||
Bus.instance.fire(IconsChangedEvent());
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:ente_auth/store/code_store.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/code_timer_progress.dart';
|
||||
import 'package:ente_auth/ui/components/bottom_action_bar_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/ui/share/code_share.dart';
|
||||
import 'package:ente_auth/ui/utils/icon_utils.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
@@ -263,6 +264,12 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
icon: Icons.qr_code_2_outlined,
|
||||
onSelected: () => _onShowQrPressed(null),
|
||||
),
|
||||
if (widget.code.note.isNotEmpty)
|
||||
MenuItem(
|
||||
label: context.l10n.notes,
|
||||
icon: Icons.notes_outlined,
|
||||
onSelected: () => _onShowNotesPressed(null),
|
||||
),
|
||||
if (!widget.code.isTrashed)
|
||||
MenuItem(
|
||||
label:
|
||||
@@ -487,6 +494,20 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onShowNotesPressed([bool? pop]) async {
|
||||
if (mounted && pop == true) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
await showChoiceDialog(
|
||||
context,
|
||||
title: context.l10n.notes,
|
||||
body: widget.code.note,
|
||||
firstButtonLabel: context.l10n.close,
|
||||
firstButtonType: ButtonType.secondary,
|
||||
secondButtonLabel: null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onEditPressed([bool? pop]) async {
|
||||
if (mounted && pop == true) {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@@ -77,17 +77,19 @@ class DialogWidget extends StatelessWidget {
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ContentContainer(
|
||||
title: title,
|
||||
body: body,
|
||||
icon: icon,
|
||||
),
|
||||
const SizedBox(height: 36),
|
||||
Actions(buttons),
|
||||
],
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ContentContainer(
|
||||
title: title,
|
||||
body: body,
|
||||
icon: icon,
|
||||
),
|
||||
const SizedBox(height: 36),
|
||||
Actions(buttons),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'package:ente_auth/events/codes_updated_event.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CoachMarkWidget extends StatelessWidget {
|
||||
|
||||
@@ -124,10 +124,14 @@ class _ItemsWidgetState extends State<ItemsWidget> {
|
||||
|
||||
String _getLanguageName(Locale locale) {
|
||||
switch (locale.languageCode) {
|
||||
case 'ar':
|
||||
return 'العربية';
|
||||
case 'en':
|
||||
return 'English';
|
||||
case 'bg':
|
||||
return 'Български';
|
||||
case 'el':
|
||||
return 'Ελληνικά';
|
||||
case 'es':
|
||||
switch (locale.countryCode) {
|
||||
case 'ES':
|
||||
@@ -154,10 +158,14 @@ class _ItemsWidgetState extends State<ItemsWidget> {
|
||||
}
|
||||
case 'ru':
|
||||
return 'Русский';
|
||||
case 'sk':
|
||||
return 'Slovenčina';
|
||||
case 'tr':
|
||||
return 'Türkçe';
|
||||
case 'uk':
|
||||
return 'Українська';
|
||||
case 'vi':
|
||||
return 'Tiếng Việt';
|
||||
case 'fi':
|
||||
return 'Suomi';
|
||||
case 'zh':
|
||||
|
||||
@@ -102,16 +102,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
final bool hasAuthenticated = await LocalAuthenticationService
|
||||
.instance
|
||||
.requestLocalAuthentication(
|
||||
context,
|
||||
context.l10n.authToViewPasskey,
|
||||
);
|
||||
await PlatformUtil.refocusWindows();
|
||||
if (hasAuthenticated) {
|
||||
await onPasskeyClick(context);
|
||||
}
|
||||
await onPasskeyClick(context);
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
|
||||
@@ -142,7 +142,10 @@ class SettingsPage extends StatelessWidget {
|
||||
sectionSpacing,
|
||||
]);
|
||||
|
||||
if (Platform.isAndroid || kDebugMode) {
|
||||
if (Platform.isAndroid ||
|
||||
Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
kDebugMode) {
|
||||
contents.addAll([
|
||||
const ThemeSwitchWidget(),
|
||||
sectionSpacing,
|
||||
|
||||
@@ -239,7 +239,7 @@ Future<ButtonResult?> showChoiceDialog(
|
||||
required String title,
|
||||
String? body,
|
||||
required String firstButtonLabel,
|
||||
String secondButtonLabel = "Cancel",
|
||||
String? secondButtonLabel = "Cancel",
|
||||
ButtonType firstButtonType = ButtonType.neutral,
|
||||
ButtonType secondButtonType = ButtonType.secondary,
|
||||
ButtonAction firstButtonAction = ButtonAction.first,
|
||||
@@ -258,13 +258,14 @@ Future<ButtonResult?> showChoiceDialog(
|
||||
onTap: firstButtonOnTap,
|
||||
buttonAction: firstButtonAction,
|
||||
),
|
||||
ButtonWidget(
|
||||
buttonType: secondButtonType,
|
||||
labelText: secondButtonLabel,
|
||||
isInAlert: true,
|
||||
onTap: secondButtonOnTap,
|
||||
buttonAction: secondButtonAction,
|
||||
),
|
||||
if (secondButtonLabel != null)
|
||||
ButtonWidget(
|
||||
buttonType: secondButtonType,
|
||||
labelText: secondButtonLabel,
|
||||
isInAlert: true,
|
||||
onTap: secondButtonOnTap,
|
||||
buttonAction: secondButtonAction,
|
||||
),
|
||||
];
|
||||
return showDialogWidget(
|
||||
context: context,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -202,8 +202,8 @@ const createInferenceSession = async (modelPath: string) => {
|
||||
};
|
||||
|
||||
const cachedCLIPImageSession = makeCachedInferenceSession(
|
||||
"mobileclip_s2_image.onnx",
|
||||
143061211 /* 143 MB */,
|
||||
"mobileclip_s2_image_opset18_rgba_sim.onnx",
|
||||
143093992 /* 143 MB */,
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -211,10 +211,14 @@ const cachedCLIPImageSession = makeCachedInferenceSession(
|
||||
*
|
||||
* The embeddings are computed using ONNX runtime, with MobileCLIP as the model.
|
||||
*/
|
||||
export const computeCLIPImageEmbedding = async (input: Float32Array) => {
|
||||
export const computeCLIPImageEmbedding = async (
|
||||
input: Uint8ClampedArray,
|
||||
inputShape: number[],
|
||||
) => {
|
||||
const session = await cachedCLIPImageSession();
|
||||
const inputArray = new Uint8Array(input.buffer);
|
||||
const feeds = {
|
||||
input: new ort.Tensor("float32", input, [1, 3, 256, 256]),
|
||||
input: new ort.Tensor("uint8", inputArray, inputShape),
|
||||
};
|
||||
const t = Date.now();
|
||||
const results = await session.run(feeds);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:photos/core/network/network.dart';
|
||||
import 'package:photos/db/files_db.dart';
|
||||
import 'package:photos/models/duplicate_files.dart';
|
||||
import 'package:photos/models/file/file.dart';
|
||||
import "package:photos/models/file/file_type.dart";
|
||||
import "package:photos/services/collections_service.dart";
|
||||
import "package:photos/services/files_service.dart";
|
||||
|
||||
@@ -27,7 +28,7 @@ class DeduplicationService {
|
||||
}
|
||||
|
||||
// Returns a list of DuplicateFiles, where each DuplicateFiles object contains
|
||||
// a list of files that have the same size and hash
|
||||
// a list of files that have the same hash
|
||||
Future<List<DuplicateFiles>> _getDuplicateFiles() async {
|
||||
Map<int, int> uploadIDToSize = {};
|
||||
final bool hasFileSizes = await FilesService.instance.hasMigratedSizes();
|
||||
@@ -62,29 +63,55 @@ class DeduplicationService {
|
||||
|
||||
final Map<String, List<EnteFile>> sizeHashToFilesMap = {};
|
||||
final Map<String, Set<int>> sizeHashToCollectionsSet = {};
|
||||
final Map<String, List<EnteFile>> livePhotoHashToFilesMap = {};
|
||||
final Map<String, Set<int>> livePhotoHashToCollectionsSet = {};
|
||||
final Set<int> processedFileIds = <int>{};
|
||||
for (final file in filteredFiles) {
|
||||
final key = '${file.fileSize}-${file.hash}';
|
||||
if (!sizeHashToFilesMap.containsKey(key)) {
|
||||
sizeHashToFilesMap[key] = <EnteFile>[];
|
||||
sizeHashToCollectionsSet[key] = <int>{};
|
||||
}
|
||||
sizeHashToCollectionsSet[key]!.add(file.collectionID!);
|
||||
if (!processedFileIds.contains(file.uploadedFileID)) {
|
||||
sizeHashToFilesMap[key]!.add(file);
|
||||
processedFileIds.add(file.uploadedFileID!);
|
||||
// Note: For live photos, the zipped file size could be different if
|
||||
// the files were uploaded from different devices. So, we dedupe live
|
||||
// photos based on hash only.
|
||||
if (file.fileType == FileType.livePhoto) {
|
||||
final key = '${file.hash}';
|
||||
if (!livePhotoHashToFilesMap.containsKey(key)) {
|
||||
livePhotoHashToFilesMap[key] = <EnteFile>[];
|
||||
livePhotoHashToCollectionsSet[key] = <int>{};
|
||||
}
|
||||
livePhotoHashToCollectionsSet[key]!.add(file.collectionID!);
|
||||
if (!processedFileIds.contains(file.uploadedFileID)) {
|
||||
livePhotoHashToFilesMap[key]!.add(file);
|
||||
processedFileIds.add(file.uploadedFileID!);
|
||||
}
|
||||
} else {
|
||||
final key = '${file.fileSize}-${file.hash}';
|
||||
if (!sizeHashToFilesMap.containsKey(key)) {
|
||||
sizeHashToFilesMap[key] = <EnteFile>[];
|
||||
sizeHashToCollectionsSet[key] = <int>{};
|
||||
}
|
||||
sizeHashToCollectionsSet[key]!.add(file.collectionID!);
|
||||
if (!processedFileIds.contains(file.uploadedFileID)) {
|
||||
sizeHashToFilesMap[key]!.add(file);
|
||||
processedFileIds.add(file.uploadedFileID!);
|
||||
}
|
||||
}
|
||||
}
|
||||
final List<DuplicateFiles> dupesBySizeHash = [];
|
||||
final List<DuplicateFiles> dupesByHash = [];
|
||||
for (final key in sizeHashToFilesMap.keys) {
|
||||
final List<EnteFile> files = sizeHashToFilesMap[key]!;
|
||||
final Set<int> collectionIds = sizeHashToCollectionsSet[key]!;
|
||||
if (files.length > 1) {
|
||||
final size = files[0].fileSize!;
|
||||
dupesBySizeHash.add(DuplicateFiles(files, size, collectionIds));
|
||||
dupesByHash.add(DuplicateFiles(files, size, collectionIds));
|
||||
}
|
||||
}
|
||||
return dupesBySizeHash;
|
||||
for (final key in livePhotoHashToFilesMap.keys) {
|
||||
final List<EnteFile> files = livePhotoHashToFilesMap[key]!;
|
||||
final Set<int> collectionIds = livePhotoHashToCollectionsSet[key]!;
|
||||
if (files.length > 1 && (files.first.fileSize ?? 0) > 0) {
|
||||
dupesByHash
|
||||
.add(DuplicateFiles(files, files.first.fileSize!, collectionIds));
|
||||
}
|
||||
}
|
||||
return dupesByHash;
|
||||
}
|
||||
|
||||
Future<DuplicateFilesResponse> _fetchDuplicateFileIDs() async {
|
||||
|
||||
@@ -107,6 +107,14 @@ class EntityService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> syncEntity(EntityType type) async {
|
||||
try {
|
||||
await _remoteToLocalSync(type);
|
||||
} catch (e) {
|
||||
_logger.severe("Failed to sync entities", e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _remoteToLocalSync(EntityType type) async {
|
||||
final int lastSyncTime =
|
||||
_prefs.getInt(_getEntityLastSyncTimePrefix(type)) ?? 0;
|
||||
@@ -116,7 +124,6 @@ class EntityService {
|
||||
limit: fetchLimit,
|
||||
);
|
||||
if (result.isEmpty) {
|
||||
debugPrint("No $type entries to sync");
|
||||
return;
|
||||
}
|
||||
final bool hasMoreItems = result.length == fetchLimit;
|
||||
|
||||
@@ -6,6 +6,8 @@ import "package:logging/logging.dart";
|
||||
import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/events/diff_sync_complete_event.dart";
|
||||
import "package:photos/events/people_changed_event.dart";
|
||||
import "package:photos/models/api/entity/type.dart";
|
||||
import "package:photos/services/entity_service.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_detection/detection.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_detection/face_detection_service.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_embedding/face_embedding_service.dart";
|
||||
@@ -59,6 +61,7 @@ class FaceRecognitionService {
|
||||
return;
|
||||
}
|
||||
_isSyncing = true;
|
||||
await EntityService.instance.syncEntity(EntityType.cgroup);
|
||||
if (_shouldSyncPeople) {
|
||||
await PersonService.instance.reconcileClusters();
|
||||
Bus.instance.fire(PeopleChangedEvent(type: PeopleEventType.syncDone));
|
||||
|
||||
@@ -335,7 +335,7 @@ class ClusterFeedbackService {
|
||||
}
|
||||
|
||||
Future<void> ignoreCluster(String clusterID) async {
|
||||
await PersonService.instance.addPerson('', clusterID);
|
||||
await PersonService.instance.addPerson('', clusterID, isHidden: true);
|
||||
Bus.instance.fire(PeopleChangedEvent());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import "package:photos/db/ml/db.dart";
|
||||
import "package:photos/events/people_changed_event.dart";
|
||||
import "package:photos/extensions/stop_watch.dart";
|
||||
import "package:photos/models/api/entity/type.dart";
|
||||
import "package:photos/models/file/file.dart";
|
||||
import 'package:photos/models/ml/face/face.dart';
|
||||
import "package:photos/models/ml/face/person.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/entity_service.dart";
|
||||
@@ -292,6 +294,24 @@ class PersonService {
|
||||
await faceMLDataDB.bulkAssignClusterToPersonID(clusterToPersonID);
|
||||
}
|
||||
|
||||
Future<void> updateAvatar(PersonEntity p, EnteFile file) async {
|
||||
final Face? face = await MLDataDB.instance.getCoverFaceForPerson(
|
||||
recentFileID: file.uploadedFileID!,
|
||||
personID: p.remoteID,
|
||||
);
|
||||
if (face == null) {
|
||||
throw Exception(
|
||||
"No face found for person ${p.remoteID} in file ${file.uploadedFileID}",
|
||||
);
|
||||
}
|
||||
|
||||
final person = (await getPerson(p.remoteID))!;
|
||||
final updatedPerson = person.copyWith(
|
||||
data: person.data.copyWith(avatarFaceId: face.faceID),
|
||||
);
|
||||
await _updatePerson(updatedPerson);
|
||||
}
|
||||
|
||||
Future<void> updateAttributes(
|
||||
String id, {
|
||||
String? name,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import "dart:typed_data" show Uint8List, Float32List;
|
||||
import "dart:typed_data" show Int32List, Uint8List;
|
||||
import "dart:ui" show Image;
|
||||
|
||||
import "package:logging/logging.dart";
|
||||
import "package:onnx_dart/onnx_dart.dart";
|
||||
import "package:onnxruntime/onnxruntime.dart";
|
||||
import "package:photos/services/machine_learning/ml_model.dart";
|
||||
import "package:photos/utils/image_ml_util.dart";
|
||||
import "package:photos/utils/ml_util.dart";
|
||||
|
||||
class ClipImageEncoder extends MlModel {
|
||||
static const kRemoteBucketModelPath = "mobileclip_s2_image.onnx";
|
||||
static const kRemoteBucketModelPath =
|
||||
"mobileclip_s2_image_opset18_rgba_sim.onnx";
|
||||
static const _modelName = "ClipImageEncoder";
|
||||
|
||||
@override
|
||||
@@ -34,16 +34,13 @@ class ClipImageEncoder extends MlModel {
|
||||
int? enteFileID,
|
||||
]) async {
|
||||
final startTime = DateTime.now();
|
||||
final inputList = await preprocessImageClip(image, rawRgbaBytes);
|
||||
final preprocessingTime = DateTime.now();
|
||||
final preprocessingMs =
|
||||
preprocessingTime.difference(startTime).inMilliseconds;
|
||||
final inputShape = <int>[image.height, image.width, 4]; // [H, W, C]
|
||||
late List<double> result;
|
||||
try {
|
||||
if (MlModel.usePlatformPlugin) {
|
||||
result = await _runPlatformPluginPredict(inputList);
|
||||
result = await _runPlatformPluginPredict(rawRgbaBytes, inputShape);
|
||||
} else {
|
||||
result = _runFFIBasedPredict(inputList, sessionAddress);
|
||||
result = _runFFIBasedPredict(rawRgbaBytes, inputShape, sessionAddress);
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
_logger.severe(
|
||||
@@ -53,21 +50,22 @@ class ClipImageEncoder extends MlModel {
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
final inferTime = DateTime.now();
|
||||
final inferenceMs = inferTime.difference(preprocessingTime).inMilliseconds;
|
||||
final totalMs = inferTime.difference(startTime).inMilliseconds;
|
||||
final totalMs = DateTime.now().difference(startTime).inMilliseconds;
|
||||
_logger.info(
|
||||
"Clip image predict took $totalMs ms${enteFileID != null ? " with fileID $enteFileID" : ""} (inference: $inferenceMs ms, preprocessing: $preprocessingMs ms)",
|
||||
"Clip image predict took $totalMs ms${enteFileID != null ? " with fileID $enteFileID" : ""}",
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<double> _runFFIBasedPredict(
|
||||
Float32List inputList,
|
||||
Uint8List inputImageList,
|
||||
List<int> inputImageShape,
|
||||
int sessionAddress,
|
||||
) {
|
||||
final inputOrt =
|
||||
OrtValueTensor.createTensorWithDataList(inputList, [1, 3, 256, 256]);
|
||||
final inputOrt = OrtValueTensor.createTensorWithDataList(
|
||||
inputImageList,
|
||||
inputImageShape,
|
||||
);
|
||||
final inputs = {'input': inputOrt};
|
||||
final session = OrtSession.fromAddress(sessionAddress);
|
||||
final runOptions = OrtRunOptions();
|
||||
@@ -83,11 +81,13 @@ class ClipImageEncoder extends MlModel {
|
||||
}
|
||||
|
||||
static Future<List<double>> _runPlatformPluginPredict(
|
||||
Float32List inputImageList,
|
||||
Uint8List inputImageList,
|
||||
List<int> inputImageShape,
|
||||
) async {
|
||||
final OnnxDart plugin = OnnxDart();
|
||||
final result = await plugin.predict(
|
||||
final result = await plugin.predictRgba(
|
||||
inputImageList,
|
||||
Int32List.fromList(inputImageShape),
|
||||
_modelName,
|
||||
);
|
||||
final List<double> embedding = result!.sublist(0, 512);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -121,13 +121,7 @@ class RemoteSyncService {
|
||||
await syncDeviceCollectionFilesForUpload();
|
||||
}
|
||||
await _pullDiff();
|
||||
// sync trash but consume error during initial launch.
|
||||
// this is to ensure that we don't pause upload due to any error during
|
||||
// the trash sync. Impact: We may end up re-uploading a file which was
|
||||
// recently trashed.
|
||||
await TrashSyncService.instance
|
||||
.syncTrash()
|
||||
.onError((e, s) => _logger.severe('trash sync failed', e, s));
|
||||
await TrashSyncService.instance.syncTrash();
|
||||
if (!hasSyncedBefore) {
|
||||
await _prefs.setBool(_isFirstRemoteSyncDone, true);
|
||||
await syncDeviceCollectionFilesForUpload();
|
||||
|
||||
@@ -696,10 +696,7 @@ class _FileSelectionActionsWidgetState
|
||||
|
||||
Future<void> _setPersonCover() async {
|
||||
final EnteFile file = widget.selectedFiles.files.first;
|
||||
await PersonService.instance.updateAttributes(
|
||||
widget.person!.remoteID,
|
||||
avatarFaceId: file.uploadedFileID.toString(),
|
||||
);
|
||||
await PersonService.instance.updateAvatar(widget.person!, file);
|
||||
widget.selectedFiles.clearAll();
|
||||
if (mounted) {
|
||||
setState(() => {});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "dart:async";
|
||||
import "dart:io" show File, Platform;
|
||||
import "dart:math" show exp, max, min, pi;
|
||||
import "dart:math" show exp, min, pi;
|
||||
import "dart:typed_data" show Float32List, Uint8List;
|
||||
import "dart:ui";
|
||||
|
||||
@@ -231,44 +231,6 @@ Future<(Float32List, Dimensions)> preprocessImageToFloat32ChannelsFirst(
|
||||
return (processedBytes, Dimensions(width: scaledWidth, height: scaledHeight));
|
||||
}
|
||||
|
||||
Future<Float32List> preprocessImageClip(
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
) async {
|
||||
const int requiredWidth = 256;
|
||||
const int requiredHeight = 256;
|
||||
const int requiredSize = 3 * requiredWidth * requiredHeight;
|
||||
final scale = max(requiredWidth / image.width, requiredHeight / image.height);
|
||||
final bool useAntiAlias = scale < 0.8;
|
||||
final scaledWidth = (image.width * scale).round();
|
||||
final scaledHeight = (image.height * scale).round();
|
||||
final widthOffset = max(0, scaledWidth - requiredWidth) / 2;
|
||||
final heightOffset = max(0, scaledHeight - requiredHeight) / 2;
|
||||
|
||||
final processedBytes = Float32List(requiredSize);
|
||||
final buffer = Float32List.view(processedBytes.buffer);
|
||||
int pixelIndex = 0;
|
||||
const int greenOff = requiredHeight * requiredWidth;
|
||||
const int blueOff = 2 * requiredHeight * requiredWidth;
|
||||
for (var h = 0 + heightOffset; h < scaledHeight - heightOffset; h++) {
|
||||
for (var w = 0 + widthOffset; w < scaledWidth - widthOffset; w++) {
|
||||
final RGB pixel = _getPixelBilinear(
|
||||
w / scale,
|
||||
h / scale,
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
antiAlias: useAntiAlias,
|
||||
);
|
||||
buffer[pixelIndex] = pixel.$1 / 255;
|
||||
buffer[pixelIndex + greenOff] = pixel.$2 / 255;
|
||||
buffer[pixelIndex + blueOff] = pixel.$3 / 255;
|
||||
pixelIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return processedBytes;
|
||||
}
|
||||
|
||||
Future<(Float32List, List<AlignmentResult>, List<bool>, List<double>, Size)>
|
||||
preprocessToMobileFaceNetFloat32List(
|
||||
Image image,
|
||||
|
||||
@@ -93,13 +93,15 @@ class OnnxDartPlugin: FlutterPlugin, MethodCallHandler {
|
||||
val modelType = call.argument<String>("modelType")
|
||||
val inputDataArray = call.argument<FloatArray>("inputData")
|
||||
val inputIntDataArray = call.argument<IntArray>("inputDataInt")
|
||||
val inputUint8DataArray = call.argument<ByteArray>("inputDataUint8")
|
||||
val inputShapeArray = call.argument<IntArray>("inputShapeList")
|
||||
|
||||
if (sessionAddress == null || modelType == null || (inputDataArray == null && inputIntDataArray == null)) {
|
||||
if (sessionAddress == null || modelType == null || (inputDataArray == null && inputIntDataArray == null && inputUint8DataArray == null)) {
|
||||
result.error("INVALID_ARGUMENT", "Session address, model type, or input data is missing", null)
|
||||
return
|
||||
}
|
||||
|
||||
predict(ModelType.valueOf(modelType), sessionAddress, inputDataArray, inputIntDataArray, result)
|
||||
predict(ModelType.valueOf(modelType), sessionAddress, inputDataArray, inputIntDataArray, inputUint8DataArray, inputShapeArray, result)
|
||||
}
|
||||
else -> {
|
||||
result.notImplemented()
|
||||
@@ -155,9 +157,8 @@ class OnnxDartPlugin: FlutterPlugin, MethodCallHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private fun predict(modelType: ModelType, sessionAddress: Int, inputDataFloat: FloatArray? = null, inputDataInt: IntArray? = null, result: Result) {
|
||||
// Assert that exactly one of inputDataFloat or inputDataInt is provided
|
||||
assert((inputDataFloat != null).xor(inputDataInt != null)) { "Exactly one of inputDataFloat or inputDataInt must be provided" }
|
||||
private fun predict(modelType: ModelType, sessionAddress: Int, inputDataFloat: FloatArray? = null, inputDataInt: IntArray? = null, inputUint8DataArray: ByteArray? = null, inputShapeArray: IntArray? = null, result: Result) {
|
||||
assert((inputDataFloat != null).xor((inputDataInt != null).xor(inputUint8DataArray != null))) { "Exactly one of inputDataFloat, inputDataInt or inputUint8DataArray must be provided" }
|
||||
|
||||
scope.launch {
|
||||
val modelState = sessionMap[modelType]
|
||||
@@ -178,8 +179,11 @@ class OnnxDartPlugin: FlutterPlugin, MethodCallHandler {
|
||||
inputTensorShape = longArrayOf(totalSize, 112, 112, 3)
|
||||
}
|
||||
ModelType.ClipImageEncoder -> {
|
||||
inputTensorShape = longArrayOf(1, 3, 256, 256)
|
||||
// inputTensorShape = longArrayOf(1, 3, 256, 256)
|
||||
if (inputShapeArray != null) {
|
||||
inputTensorShape = inputShapeArray.map { it.toLong() }.toLongArray()
|
||||
} else {
|
||||
result.error("INVALID_ARGUMENT", "Input shape is missing for clip image input", null)
|
||||
}
|
||||
}
|
||||
ModelType.ClipTextEncoder -> {
|
||||
inputTensorShape = longArrayOf(1, 77)
|
||||
@@ -192,6 +196,7 @@ class OnnxDartPlugin: FlutterPlugin, MethodCallHandler {
|
||||
val inputTensor = when {
|
||||
inputDataFloat != null -> OnnxTensor.createTensor(env, FloatBuffer.wrap(inputDataFloat), inputTensorShape)
|
||||
inputDataInt != null -> OnnxTensor.createTensor(env, IntBuffer.wrap(inputDataInt), inputTensorShape)
|
||||
inputUint8DataArray != null -> OnnxTensor.createTensor(env, ByteBuffer.wrap(inputUint8DataArray), inputTensorShape, OnnxJavaType.UINT8)
|
||||
else -> throw IllegalArgumentException("No input data provided")
|
||||
}
|
||||
val inputs = mutableMapOf<String, OnnxTensor>()
|
||||
@@ -219,7 +224,7 @@ class OnnxDartPlugin: FlutterPlugin, MethodCallHandler {
|
||||
inputTensor.close()
|
||||
} catch (e: OrtException) {
|
||||
withContext(Dispatchers.Main) {
|
||||
result.error("PREDICTION_ERROR", "Error during prediction: ${e.message}", null)
|
||||
result.error("PREDICTION_ERROR", "Error during prediction: ${e.message} ${e.stackTraceToString()}", null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error during prediction: ${e.message}", e)
|
||||
|
||||
@@ -25,8 +25,13 @@ class OnnxDart {
|
||||
String modelType, {
|
||||
int sessionAddress = 0,
|
||||
}) async {
|
||||
final result = await OnnxDartPlatform.instance
|
||||
.predict(inputData, null, modelType, sessionAddress: sessionAddress);
|
||||
final result = await OnnxDartPlatform.instance.predict(
|
||||
inputData,
|
||||
null,
|
||||
null,
|
||||
modelType,
|
||||
sessionAddress: sessionAddress,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -35,8 +40,30 @@ class OnnxDart {
|
||||
String modelType, {
|
||||
int sessionAddress = 0,
|
||||
}) async {
|
||||
final result = await OnnxDartPlatform.instance
|
||||
.predict(null, inputDataInt, modelType, sessionAddress: sessionAddress);
|
||||
final result = await OnnxDartPlatform.instance.predict(
|
||||
null,
|
||||
inputDataInt,
|
||||
null,
|
||||
modelType,
|
||||
sessionAddress: sessionAddress,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<Float32List?> predictRgba(
|
||||
Uint8List inputBytes,
|
||||
Int32List inputShape,
|
||||
String modelType, {
|
||||
int sessionAddress = 0,
|
||||
}) async {
|
||||
final result = await OnnxDartPlatform.instance.predict(
|
||||
null,
|
||||
null,
|
||||
inputBytes,
|
||||
modelType,
|
||||
sessionAddress: sessionAddress,
|
||||
inputShapeList: inputShape,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,10 @@ class MethodChannelOnnxDart extends OnnxDartPlatform {
|
||||
Future<Float32List?> predict(
|
||||
Float32List? inputData,
|
||||
Int32List? inputDataInt,
|
||||
Uint8List? inputDataUint8,
|
||||
String modelType, {
|
||||
int sessionAddress = 0,
|
||||
Int32List? inputShapeList,
|
||||
}) {
|
||||
return methodChannel.invokeMethod<Float32List?>(
|
||||
'predict',
|
||||
@@ -49,7 +51,9 @@ class MethodChannelOnnxDart extends OnnxDartPlatform {
|
||||
'sessionAddress': sessionAddress,
|
||||
'inputData': inputData,
|
||||
'inputDataInt': inputDataInt,
|
||||
'inputDataUint8': inputDataUint8,
|
||||
'modelType': modelType,
|
||||
"inputShapeList": inputShapeList,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,12 +42,12 @@ abstract class OnnxDartPlatform extends PlatformInterface {
|
||||
|
||||
Future<Float32List?> predict(
|
||||
Float32List? inputData,
|
||||
Int32List? inputDataInt,
|
||||
Int32List? inputDataInt,
|
||||
Uint8List? inputDataRgba,
|
||||
String modelType, {
|
||||
int sessionAddress = 0,
|
||||
Int32List? inputShapeList,
|
||||
}) {
|
||||
throw UnimplementedError('predict() has not been implemented.');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ description: ente photos application
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
|
||||
version: 0.9.44+944
|
||||
version: 0.9.46+946
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
|
||||
@@ -158,7 +158,7 @@ const AuthNavbar: React.FC = () => {
|
||||
startIcon={<LogoutOutlined />}
|
||||
onClick={logout}
|
||||
>
|
||||
{t("LOGOUT")}
|
||||
{t("logout")}
|
||||
</OverflowMenuOption>
|
||||
</OverflowMenu>
|
||||
</HorizontalFlex>
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import type { Collection } from "@/media/collection";
|
||||
import { PersonListHeader } from "@/new/photos/components/Gallery";
|
||||
import {
|
||||
GalleryBarImpl,
|
||||
type GalleryBarImplProps,
|
||||
} from "@/new/photos/components/Gallery/BarImpl";
|
||||
import { PeopleHeader } from "@/new/photos/components/Gallery/PeopleHeader";
|
||||
import {
|
||||
collectionsSortBy,
|
||||
type CollectionsSortBy,
|
||||
type CollectionSummaries,
|
||||
} from "@/new/photos/types/collection";
|
||||
import { ensure } from "@/utils/ensure";
|
||||
import { includes } from "@/utils/type-guards";
|
||||
import {
|
||||
getData,
|
||||
@@ -20,7 +19,14 @@ import AllCollections from "components/Collections/AllCollections";
|
||||
import { SetCollectionNamerAttributes } from "components/Collections/CollectionNamer";
|
||||
import CollectionShare from "components/Collections/CollectionShare";
|
||||
import { ITEM_TYPE, TimeStampListItem } from "components/PhotoList";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { AppContext } from "pages/_app";
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { sortCollectionSummaries } from "services/collectionService";
|
||||
import { SetFilesDownloadProgressAttributesCreator } from "types/gallery";
|
||||
import {
|
||||
@@ -80,6 +86,7 @@ type CollectionsProps = Omit<
|
||||
*/
|
||||
export const GalleryBarAndListHeader: React.FC<CollectionsProps> = ({
|
||||
shouldHide,
|
||||
showPeopleSectionButton,
|
||||
mode,
|
||||
onChangeMode,
|
||||
collectionSummaries,
|
||||
@@ -95,6 +102,8 @@ export const GalleryBarAndListHeader: React.FC<CollectionsProps> = ({
|
||||
filesDownloadProgressAttributesList,
|
||||
setFilesDownloadProgressAttributesCreator,
|
||||
}) => {
|
||||
const appContext = useContext(AppContext);
|
||||
|
||||
const [openAllCollectionDialog, setOpenAllCollectionDialog] =
|
||||
useState(false);
|
||||
const [openCollectionShareView, setOpenCollectionShareView] =
|
||||
@@ -162,8 +171,13 @@ export const GalleryBarAndListHeader: React.FC<CollectionsProps> = ({
|
||||
}
|
||||
onCollectionCast={() => setOpenAlbumCastDialog(true)}
|
||||
/>
|
||||
) : activePerson ? (
|
||||
<PeopleHeader
|
||||
person={activePerson}
|
||||
{...{ onSelectPerson, appContext }}
|
||||
/>
|
||||
) : (
|
||||
<PersonListHeader person={ensure(activePerson)} />
|
||||
<></>
|
||||
),
|
||||
itemType: ITEM_TYPE.HEADER,
|
||||
height: 68,
|
||||
@@ -174,7 +188,6 @@ export const GalleryBarAndListHeader: React.FC<CollectionsProps> = ({
|
||||
toShowCollectionSummaries,
|
||||
activeCollectionID,
|
||||
isActiveCollectionDownloadInProgress,
|
||||
people,
|
||||
activePerson,
|
||||
]);
|
||||
|
||||
@@ -186,6 +199,7 @@ export const GalleryBarAndListHeader: React.FC<CollectionsProps> = ({
|
||||
<>
|
||||
<GalleryBarImpl
|
||||
{...{
|
||||
showPeopleSectionButton,
|
||||
mode,
|
||||
onChangeMode,
|
||||
activeCollectionID,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -47,7 +47,14 @@ import isElectron from "is-electron";
|
||||
import type { AppProps } from "next/app";
|
||||
import { useRouter } from "next/router";
|
||||
import "photoswipe/dist/photoswipe.css";
|
||||
import { createContext, useContext, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import LoadingBar from "react-top-loading-bar";
|
||||
import { resumeExportsIfNeeded } from "services/export";
|
||||
import { photosLogout } from "services/logout";
|
||||
@@ -83,6 +90,7 @@ type AppContextT = AccountsContextT & {
|
||||
themeColor: THEME_COLOR;
|
||||
setThemeColor: (themeColor: THEME_COLOR) => void;
|
||||
somethingWentWrong: () => void;
|
||||
onGenericError: (error: unknown) => void;
|
||||
isCFProxyDisabled: boolean;
|
||||
setIsCFProxyDisabled: (disabled: boolean) => void;
|
||||
};
|
||||
@@ -269,12 +277,28 @@ export default function App({ Component, pageProps }: AppProps) {
|
||||
const closeMessageDialog = () => setMessageDialogView(false);
|
||||
const closeDialogBoxV2 = () => setDialogBoxV2View(false);
|
||||
|
||||
const somethingWentWrong = () =>
|
||||
setDialogMessage({
|
||||
title: t("error"),
|
||||
close: { variant: "critical" },
|
||||
content: t("UNKNOWN_ERROR"),
|
||||
});
|
||||
// Use `onGenericError` instead.
|
||||
const somethingWentWrong = useCallback(
|
||||
() =>
|
||||
setDialogMessage({
|
||||
title: t("error"),
|
||||
close: { variant: "critical" },
|
||||
content: t("UNKNOWN_ERROR"),
|
||||
}),
|
||||
[setDialogMessage],
|
||||
);
|
||||
|
||||
const onGenericError = useCallback(
|
||||
(e: unknown) => (
|
||||
log.error("Error", e),
|
||||
setDialogBoxAttributesV2({
|
||||
title: t("error"),
|
||||
content: t("UNKNOWN_ERROR"),
|
||||
close: { variant: "critical" },
|
||||
})
|
||||
),
|
||||
[setDialogBoxAttributesV2],
|
||||
);
|
||||
|
||||
const logout = () => {
|
||||
void photosLogout().then(() => router.push("/"));
|
||||
@@ -294,6 +318,7 @@ export default function App({ Component, pageProps }: AppProps) {
|
||||
themeColor,
|
||||
setThemeColor,
|
||||
somethingWentWrong,
|
||||
onGenericError,
|
||||
setDialogBoxAttributesV2,
|
||||
mapEnabled,
|
||||
updateMapEnabled,
|
||||
|
||||
@@ -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,
|
||||
@@ -251,7 +255,7 @@ export default function Gallery() {
|
||||
accept: ".zip",
|
||||
});
|
||||
|
||||
const syncInProgress = useRef(true);
|
||||
const syncInProgress = useRef(false);
|
||||
const syncInterval = useRef<NodeJS.Timeout>();
|
||||
const resync = useRef<{ force: boolean; silent: boolean }>();
|
||||
|
||||
@@ -317,8 +321,8 @@ export default function Gallery() {
|
||||
// If visible, what should the (sticky) gallery bar show.
|
||||
const [barMode, setBarMode] = useState<GalleryBarMode>("albums");
|
||||
|
||||
// The currently selected person in the gallery bar (if any).
|
||||
const [activePerson, setActivePerson] = useState<Person | undefined>();
|
||||
// The ID of the currently selected person in the gallery bar (if any).
|
||||
const [activePersonID, setActivePersonID] = useState<string | undefined>();
|
||||
|
||||
const people = useSyncExternalStore(peopleSubscribe, peopleSnapshot);
|
||||
|
||||
@@ -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,32 +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 pfSet = new Set(ensure(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(
|
||||
@@ -627,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,
|
||||
@@ -642,9 +677,15 @@ export default function Gallery() {
|
||||
selectedSearchOption,
|
||||
activeCollectionID,
|
||||
archivedCollections,
|
||||
activePerson,
|
||||
people,
|
||||
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 (
|
||||
@@ -675,10 +716,13 @@ export default function Gallery() {
|
||||
count: 0,
|
||||
collectionID: activeCollectionID,
|
||||
context:
|
||||
barMode == "people" && activePerson
|
||||
? { mode: "people" as const, personID: activePerson.id }
|
||||
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),
|
||||
},
|
||||
};
|
||||
@@ -741,6 +785,7 @@ export default function Gallery() {
|
||||
resync.current = { force, silent };
|
||||
return;
|
||||
}
|
||||
const isForced = syncInProgress.current && force;
|
||||
syncInProgress.current = true;
|
||||
try {
|
||||
const token = getToken();
|
||||
@@ -761,7 +806,14 @@ export default function Gallery() {
|
||||
await syncFiles("normal", normalCollections, setFiles);
|
||||
await syncFiles("hidden", hiddenCollections, setHiddenFiles);
|
||||
await syncTrash(collections, setTrashedFiles);
|
||||
await sync();
|
||||
// syncWithRemote is called with the force flag set to true before
|
||||
// doing an upload. So it is possible, say when resuming a pending
|
||||
// upload, that we get two syncWithRemotes happening in parallel.
|
||||
//
|
||||
// Do the non-file-related sync only for one of these parallel ones.
|
||||
if (!isForced) {
|
||||
await sync();
|
||||
}
|
||||
} catch (e) {
|
||||
switch (e.message) {
|
||||
case CustomError.SESSION_EXPIRED:
|
||||
@@ -996,7 +1048,7 @@ export default function Gallery() {
|
||||
setActiveCollectionID(searchOption.suggestion.collectionID);
|
||||
} else {
|
||||
setBarMode("people");
|
||||
setActivePerson(searchOption.suggestion.person);
|
||||
setActivePersonID(searchOption.suggestion.person.id);
|
||||
}
|
||||
} else {
|
||||
setIsInSearchMode(!!searchOption);
|
||||
@@ -1049,23 +1101,23 @@ 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).
|
||||
setActivePerson(person ?? ensure(people[0]));
|
||||
setActivePersonID(person?.id);
|
||||
setBarMode("people");
|
||||
};
|
||||
|
||||
if (activePerson) {
|
||||
log.debug(() => ["person", activePerson]);
|
||||
}
|
||||
const handleSelectFileInfoPerson = (personID: string) => {
|
||||
setActivePersonID(personID);
|
||||
setBarMode("people");
|
||||
};
|
||||
|
||||
if (!collectionSummaries || !filteredData) {
|
||||
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={{
|
||||
@@ -1135,7 +1187,12 @@ export default function Gallery() {
|
||||
attributes={fixCreationTimeAttributes}
|
||||
/>
|
||||
<NavbarBase
|
||||
sx={{ background: "transparent", position: "absolute" }}
|
||||
sx={{
|
||||
background: "transparent",
|
||||
position: "absolute",
|
||||
// Override the default 16px we get from NavbarBase
|
||||
marginBottom: "12px",
|
||||
}}
|
||||
>
|
||||
{barMode == "hidden-albums" ? (
|
||||
<HiddenSectionNavbarContents
|
||||
@@ -1165,8 +1222,9 @@ export default function Gallery() {
|
||||
activeCollectionID,
|
||||
setActiveCollectionID,
|
||||
hiddenCollectionSummaries,
|
||||
people,
|
||||
activePerson,
|
||||
showPeopleSectionButton,
|
||||
people: galleryPeopleState?.people ?? [],
|
||||
activePerson: galleryPeopleState?.activePerson,
|
||||
onSelectPerson: handleSelectPerson,
|
||||
setCollectionNamerAttributes,
|
||||
setPhotoListHeader,
|
||||
@@ -1229,6 +1287,11 @@ export default function Gallery() {
|
||||
!hiddenFiles?.length &&
|
||||
activeCollectionID === ALL_SECTION ? (
|
||||
<GalleryEmptyState openUploader={openUploader} />
|
||||
) : !isInSearchMode &&
|
||||
!isFirstLoad &&
|
||||
barMode == "people" &&
|
||||
!galleryPeopleState?.activePerson ? (
|
||||
<PeopleEmptyState />
|
||||
) : (
|
||||
<PhotoFrame
|
||||
page={PAGES.GALLERY}
|
||||
@@ -1242,7 +1305,7 @@ export default function Gallery() {
|
||||
setTempDeletedFileIds={setTempDeletedFileIds}
|
||||
setIsPhotoSwipeOpen={setIsPhotoSwipeOpen}
|
||||
activeCollectionID={activeCollectionID}
|
||||
activePersonID={activePerson?.id}
|
||||
activePersonID={galleryPeopleState?.activePerson?.id}
|
||||
enableDownload={true}
|
||||
fileToCollectionsMap={fileToCollectionsMap}
|
||||
collectionNameMap={collectionNameMap}
|
||||
@@ -1254,6 +1317,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;
|
||||
};
|
||||
|
||||
@@ -140,7 +140,9 @@ export const encryptBlob = (data: BytesOrB64, key: BytesOrB64) =>
|
||||
* strings.
|
||||
*/
|
||||
export const encryptBlobB64 = (data: BytesOrB64, key: BytesOrB64) =>
|
||||
assertInWorker(ei._encryptBlobB64(data, key));
|
||||
inWorker()
|
||||
? ei._encryptBlobB64(data, key)
|
||||
: sharedCryptoWorker().then((w) => w._encryptBlobB64(data, key));
|
||||
|
||||
/**
|
||||
* Encrypt the thumbnail for a file.
|
||||
|
||||
@@ -15,6 +15,7 @@ import * as libsodium from "./libsodium";
|
||||
export class CryptoWorker {
|
||||
encryptBoxB64 = ei._encryptBoxB64;
|
||||
encryptThumbnail = ei._encryptThumbnail;
|
||||
_encryptBlobB64 = ei._encryptBlobB64;
|
||||
encryptMetadataJSON_New = ei._encryptMetadataJSON_New;
|
||||
encryptMetadataJSON = ei._encryptMetadataJSON;
|
||||
decryptBox = ei._decryptBox;
|
||||
|
||||
@@ -9,12 +9,36 @@ import log from "./log";
|
||||
* storage is limited to the main thread).
|
||||
*
|
||||
* The "kv" database consists of one object store, "kv". Keys are strings.
|
||||
* Values can be strings or number or booleans.
|
||||
* Values can be arbitrary JSON objects.
|
||||
*
|
||||
* [Note: Avoiding IndexedDB flakiness by avoiding indexes]
|
||||
*
|
||||
* Sporadically, rarely, but definitely, we ran into issues with IndexedDB
|
||||
* losing data. e.g. saves from the ML web worker would complete successfully,
|
||||
* but the saves would not reflect on the main thread, and that data would just
|
||||
* get lost when the app would refresh.
|
||||
*
|
||||
* I'm not sure where the problem lay - in our own code, in the library that we
|
||||
* are using (idb), within IndexedDB itself, or in Electron/Chrome's
|
||||
* implementation of it (these cases all came up in the context of the ML code
|
||||
* that only ran in our desktop app).
|
||||
*
|
||||
* A piece of advice I randomly ran across on the internet was to keep it very
|
||||
* simple, and avoid all indexes. I also recalled that we did not see such
|
||||
* issues with our older (but now unmaintained) library, localforage, which also
|
||||
* doesn't use any indexes, and just has a flat single-store schema.
|
||||
*
|
||||
* So this may be superstition, but for now the approach I'm taking is to use
|
||||
* IndexedDB as a key value store only.
|
||||
*/
|
||||
interface KVDBSchema extends DBSchema {
|
||||
kv: {
|
||||
key: string;
|
||||
value: string | number | boolean;
|
||||
/**
|
||||
* Typescript doesn't have a native JSON type, so this needs to be
|
||||
* unknown
|
||||
*/
|
||||
value: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,8 +125,16 @@ export const clearKVDB = async () => {
|
||||
/**
|
||||
* Return the string value stored corresponding to {@link key}, or `undefined`
|
||||
* if there is no such entry.
|
||||
*
|
||||
* Typescript doesn't have a native JSON type, so the return value is type as an
|
||||
* `unknown`. For primitive types, you can avoid casting by using the
|
||||
* {@link getKVS} (string), {@link getKVN} (number) or {@link getKVB} (boolean)
|
||||
* methods that do an additional runtime check of the type.
|
||||
*/
|
||||
export const getKV = async (key: string) => _getKV<string>(key, "string");
|
||||
export const getKV = async (key: string) => {
|
||||
const db = await kvDB();
|
||||
return db.get("kv", key);
|
||||
};
|
||||
|
||||
export const _getKV = async <T extends string | number | boolean>(
|
||||
key: string,
|
||||
@@ -113,11 +145,14 @@ export const _getKV = async <T extends string | number | boolean>(
|
||||
if (v === undefined) return undefined;
|
||||
if (typeof v != type)
|
||||
throw new Error(
|
||||
`Expected the value corresponding to key ${key} to be a ${type}, but instead got ${v}`,
|
||||
`Expected the value corresponding to key ${key} to be a ${type}, but instead got ${String(v)}`,
|
||||
);
|
||||
return v as T;
|
||||
};
|
||||
|
||||
/** String variant of {@link getKV}. */
|
||||
export const getKVS = async (key: string) => _getKV<string>(key, "string");
|
||||
|
||||
/** Numeric variant of {@link getKV}. */
|
||||
export const getKVN = async (key: string) => _getKV<number>(key, "number");
|
||||
|
||||
@@ -127,8 +162,11 @@ export const getKVB = async (key: string) => _getKV<boolean>(key, "boolean");
|
||||
/**
|
||||
* Save the given {@link value} corresponding to {@link key}, overwriting any
|
||||
* existing value.
|
||||
*
|
||||
* @param value Any arbitrary JSON object. Typescript doesn't have a native JSON
|
||||
* type, so this is typed as a unknown
|
||||
*/
|
||||
export const setKV = async (key: string, value: string | number | boolean) => {
|
||||
export const setKV = async (key: string, value: unknown) => {
|
||||
const db = await kvDB();
|
||||
await db.put("kv", value, key);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { ensure } from "@/utils/ensure";
|
||||
import { z } from "zod";
|
||||
import { getKV } from "./kv";
|
||||
import { getKVS } from "./kv";
|
||||
|
||||
// TODO: During login the only field present is email. Which makes this
|
||||
// optionality indicated by these types incorrect.
|
||||
@@ -57,4 +57,4 @@ export const ensureLocalUser = (): LocalUser => {
|
||||
* The underlying data is stored in IndexedDB, and can be accessed from web
|
||||
* workers.
|
||||
*/
|
||||
export const ensureAuthToken = async () => ensure(await getKV("token"));
|
||||
export const ensureAuthToken = async () => ensure(await getKVS("token"));
|
||||
|
||||
@@ -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": "通过附近的服务器路由上传",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getKV } from "@/base/kv";
|
||||
import { getKVS } from "@/base/kv";
|
||||
|
||||
/**
|
||||
* Return the origin (scheme, host, port triple) that should be used for making
|
||||
@@ -19,9 +19,23 @@ export const apiOrigin = async () =>
|
||||
* @param path The URL path usually, but can be anything that needs to be
|
||||
* suffixed to the origin. It must begin with a "/".
|
||||
*
|
||||
* @param queryParams An optional object containing query params. This is
|
||||
* appended to the generated URL after funneling it through
|
||||
* {@link URLSearchParams}.
|
||||
*
|
||||
* @returns path prefixed by {@link apiOrigin}.
|
||||
*/
|
||||
export const apiURL = async (path: string) => (await apiOrigin()) + path;
|
||||
export const apiURL = async (
|
||||
path: string,
|
||||
queryParams?: Record<string, string>,
|
||||
) => {
|
||||
let url = (await apiOrigin()) + path;
|
||||
if (queryParams) {
|
||||
const params = new URLSearchParams(queryParams);
|
||||
url = `${url}?${params.toString()}`;
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the overridden API origin, if one is defined by either (in priority
|
||||
@@ -35,7 +49,7 @@ export const apiURL = async (path: string) => (await apiOrigin()) + path;
|
||||
* Otherwise return undefined.
|
||||
*/
|
||||
export const customAPIOrigin = async () =>
|
||||
(await getKV("apiOrigin")) ??
|
||||
(await getKVS("apiOrigin")) ??
|
||||
process.env.NEXT_PUBLIC_ENTE_ENDPOINT ??
|
||||
undefined;
|
||||
|
||||
|
||||
@@ -553,12 +553,16 @@ export interface ElectronMLWorker {
|
||||
* See: [Note: Natural language search using CLIP]
|
||||
*
|
||||
* The input is a opaque float32 array representing the image. The layout
|
||||
* and exact encoding of the input is specific to our implementation and the
|
||||
* ML model (CLIP) we use.
|
||||
* and exact encoding of the input is specific to the runtime (ONNX) and the
|
||||
* ML model (a MobileCLIP variant) we use. In particular, the image
|
||||
* pre-processing happens within our model itself.
|
||||
*
|
||||
* @returns A CLIP embedding (an array of 512 floating point values).
|
||||
*/
|
||||
computeCLIPImageEmbedding: (input: Float32Array) => Promise<Float32Array>;
|
||||
computeCLIPImageEmbedding: (
|
||||
input: Uint8ClampedArray,
|
||||
inputShape: number[],
|
||||
) => Promise<Float32Array>;
|
||||
|
||||
/**
|
||||
* Return a CLIP embedding of the given image if we already have the model
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useIsMobileWidth } from "@/base/hooks";
|
||||
import { ensureOk } from "@/base/http";
|
||||
import { getKV, removeKV, setKV } from "@/base/kv";
|
||||
import { getKVS, removeKV, setKV } from "@/base/kv";
|
||||
import log from "@/base/log";
|
||||
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
|
||||
import {
|
||||
@@ -69,7 +69,8 @@ const Contents: React.FC<ContentsProps> = (props) => {
|
||||
>();
|
||||
|
||||
useEffect(
|
||||
() => void getKV("apiOrigin").then((o) => setInitialAPIOrigin(o ?? "")),
|
||||
() =>
|
||||
void getKVS("apiOrigin").then((o) => setInitialAPIOrigin(o ?? "")),
|
||||
[],
|
||||
);
|
||||
|
||||
|
||||
@@ -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,18 +98,18 @@ export interface GalleryBarImplProps {
|
||||
*/
|
||||
people: Person[];
|
||||
/**
|
||||
* The currently selected person.
|
||||
*
|
||||
* Required if mode is "people".
|
||||
* The currently selected person, if any.
|
||||
*/
|
||||
activePerson: Person | undefined;
|
||||
/**
|
||||
* Called when the user selects a new person in the bar.
|
||||
* 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`).
|
||||
*/
|
||||
onSelectPerson: (person: Person) => void;
|
||||
onSelectPerson: (person: Person | undefined) => void;
|
||||
}
|
||||
|
||||
export const GalleryBarImpl: React.FC<GalleryBarImplProps> = ({
|
||||
showPeopleSectionButton,
|
||||
mode,
|
||||
onChangeMode,
|
||||
collectionSummaries,
|
||||
@@ -209,9 +213,9 @@ export const GalleryBarImpl: React.FC<GalleryBarImplProps> = ({
|
||||
onSelectCollectionID,
|
||||
}
|
||||
: {
|
||||
type: "people",
|
||||
type: "people" as const,
|
||||
people,
|
||||
activePerson: ensure(activePerson),
|
||||
activePerson,
|
||||
onSelectPerson,
|
||||
},
|
||||
[
|
||||
@@ -225,7 +229,7 @@ export const GalleryBarImpl: React.FC<GalleryBarImplProps> = ({
|
||||
],
|
||||
);
|
||||
|
||||
const controls1 = isMobile && (
|
||||
const controls1 = isMobile && mode != "people" && (
|
||||
<Box display="flex" alignItems={"center"} gap={1}>
|
||||
<CollectionsSortOptions
|
||||
activeSortBy={collectionsSortBy}
|
||||
@@ -238,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}
|
||||
@@ -251,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>
|
||||
@@ -300,7 +309,7 @@ export const Row1 = styled(Box)`
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin-block-end: 8px;
|
||||
margin-block-end: 12px;
|
||||
`;
|
||||
|
||||
export const Row2 = styled(Box)`
|
||||
@@ -310,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} }
|
||||
`,
|
||||
);
|
||||
@@ -403,7 +425,7 @@ type ItemData =
|
||||
| {
|
||||
type: "people";
|
||||
people: Person[];
|
||||
activePerson: Person;
|
||||
activePerson: Person | undefined;
|
||||
onSelectPerson: (person: Person) => void;
|
||||
};
|
||||
|
||||
@@ -569,7 +591,7 @@ const ActiveIndicator = styled("div")`
|
||||
|
||||
interface PersonCardProps {
|
||||
person: Person;
|
||||
activePerson: Person;
|
||||
activePerson: Person | undefined;
|
||||
onSelectPerson: (person: Person) => void;
|
||||
}
|
||||
|
||||
@@ -582,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>
|
||||
);
|
||||
|
||||
242
web/packages/new/photos/components/Gallery/PeopleHeader.tsx
Normal file
242
web/packages/new/photos/components/Gallery/PeopleHeader.tsx
Normal file
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* @file code that really belongs to pages/gallery.tsx itself (or related
|
||||
* files), but it written here in a separate file so that we can write in this
|
||||
* package that has TypeScript strict mode enabled.
|
||||
*
|
||||
* Once the original gallery.tsx is strict mode, this code can be inlined back
|
||||
* there.
|
||||
*/
|
||||
|
||||
import { pt } from "@/base/i18n";
|
||||
import {
|
||||
addPerson,
|
||||
deletePerson,
|
||||
renamePerson,
|
||||
} from "@/new/photos/services/ml/";
|
||||
import { type Person } from "@/new/photos/services/ml/people";
|
||||
import OverflowMenu from "@ente/shared/components/OverflowMenu/menu";
|
||||
import { OverflowMenuOption } from "@ente/shared/components/OverflowMenu/option";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import EditIcon from "@mui/icons-material/Edit";
|
||||
import MoreHoriz from "@mui/icons-material/MoreHoriz";
|
||||
import { IconButton, Stack, Tooltip } from "@mui/material";
|
||||
import { ClearIcon } from "@mui/x-date-pickers";
|
||||
import { t } from "i18next";
|
||||
import React, { useState } from "react";
|
||||
import type { FaceCluster } from "../../services/ml/cluster";
|
||||
import type { CGroup } from "../../services/user-entity";
|
||||
import type { NewAppContextPhotos } from "../../types/context";
|
||||
import { SpaceBetweenFlex } from "../mui-custom";
|
||||
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;
|
||||
};
|
||||
|
||||
export const PeopleHeader: React.FC<PeopleHeaderProps> = ({
|
||||
person,
|
||||
appContext,
|
||||
onSelectPerson,
|
||||
}) => {
|
||||
return (
|
||||
<GalleryItemsHeaderAdapter>
|
||||
<SpaceBetweenFlex>
|
||||
<GalleryItemsSummary
|
||||
name={
|
||||
person.name ?? pt("Unnamed person") /* TODO-Cluster */
|
||||
}
|
||||
nameProps={person.name ? {} : { color: "text.muted" }}
|
||||
fileCount={person.fileIDs.length}
|
||||
/>
|
||||
{person.type == "cgroup" ? (
|
||||
<CGroupPersonOptions
|
||||
cgroup={person.cgroup}
|
||||
{...{ onSelectPerson, appContext }}
|
||||
/>
|
||||
) : (
|
||||
<ClusterPersonOptions
|
||||
cluster={person.cluster}
|
||||
appContext={appContext}
|
||||
/>
|
||||
)}
|
||||
</SpaceBetweenFlex>
|
||||
</GalleryItemsHeaderAdapter>
|
||||
);
|
||||
};
|
||||
|
||||
type CGroupPersonOptionsProps = Pick<
|
||||
PeopleHeaderProps,
|
||||
"appContext" | "onSelectPerson"
|
||||
> & {
|
||||
cgroup: CGroup;
|
||||
};
|
||||
|
||||
const CGroupPersonOptions: React.FC<CGroupPersonOptionsProps> = ({
|
||||
cgroup,
|
||||
appContext,
|
||||
onSelectPerson,
|
||||
}) => {
|
||||
const {
|
||||
startLoading,
|
||||
finishLoading,
|
||||
onGenericError,
|
||||
setDialogBoxAttributesV2,
|
||||
} = appContext;
|
||||
|
||||
const [openAddNameInput, setOpenAddNameInput] = useState(false);
|
||||
|
||||
const handleRenamePerson = () => setOpenAddNameInput(true);
|
||||
|
||||
const renamePersonUsingName = async (name: string) => {
|
||||
startLoading();
|
||||
try {
|
||||
await renamePerson(name, cgroup);
|
||||
} finally {
|
||||
finishLoading();
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeletePerson = () =>
|
||||
setDialogBoxAttributesV2({
|
||||
title: pt("Reset person?"),
|
||||
content: pt(
|
||||
"The name, face groupings and suggestions for this person will be reset",
|
||||
),
|
||||
close: { text: t("cancel") },
|
||||
proceed: {
|
||||
text: t("reset"),
|
||||
action: doDeletePerson,
|
||||
},
|
||||
buttonDirection: "row",
|
||||
});
|
||||
|
||||
const doDeletePerson = async () => {
|
||||
startLoading();
|
||||
try {
|
||||
await deletePerson(cgroup);
|
||||
// Reset the selection to the default state.
|
||||
onSelectPerson(undefined);
|
||||
} catch (e) {
|
||||
onGenericError(e);
|
||||
} finally {
|
||||
finishLoading();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<OverflowMenu
|
||||
ariaControls={"person-options"}
|
||||
triggerButtonIcon={<MoreHoriz />}
|
||||
>
|
||||
<OverflowMenuOption
|
||||
startIcon={<EditIcon />}
|
||||
centerAlign
|
||||
onClick={handleRenamePerson}
|
||||
>
|
||||
{t("rename")}
|
||||
</OverflowMenuOption>
|
||||
<OverflowMenuOption
|
||||
startIcon={<ClearIcon />}
|
||||
centerAlign
|
||||
onClick={handleDeletePerson}
|
||||
>
|
||||
{pt("Reset")}
|
||||
</OverflowMenuOption>
|
||||
</OverflowMenu>
|
||||
|
||||
<NameInputDialog
|
||||
open={openAddNameInput}
|
||||
onClose={() => setOpenAddNameInput(false)}
|
||||
title={pt("Rename person") /* TODO-Cluster pt()'s */}
|
||||
placeholder={t("enter_name")}
|
||||
initialValue={cgroup.data.name ?? ""}
|
||||
submitButtonTitle={t("rename")}
|
||||
onSubmit={renamePersonUsingName}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
type ClusterPersonOptionsProps = Pick<PeopleHeaderProps, "appContext"> & {
|
||||
cluster: FaceCluster;
|
||||
};
|
||||
|
||||
const ClusterPersonOptions: React.FC<ClusterPersonOptionsProps> = ({
|
||||
cluster,
|
||||
appContext,
|
||||
}) => {
|
||||
const { startLoading, finishLoading } = appContext;
|
||||
|
||||
const [openNameInput, setOpenNameInput] = useState(false);
|
||||
|
||||
const handleAddPerson = () => setOpenNameInput(true);
|
||||
|
||||
const addPersonWithName = async (name: string) => {
|
||||
startLoading();
|
||||
try {
|
||||
await addPerson(name, cluster);
|
||||
} finally {
|
||||
finishLoading();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack direction="row" sx={{ alignItems: "center", gap: 2 }}>
|
||||
<Tooltip title={pt("Add a name")}>
|
||||
<IconButton onClick={handleAddPerson}>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<OverflowMenu
|
||||
ariaControls={"person-options"}
|
||||
triggerButtonIcon={<MoreHoriz />}
|
||||
>
|
||||
<OverflowMenuOption
|
||||
startIcon={<AddIcon />}
|
||||
centerAlign
|
||||
onClick={handleAddPerson}
|
||||
>
|
||||
{pt("Add a name")}
|
||||
</OverflowMenuOption>
|
||||
</OverflowMenu>
|
||||
</Stack>
|
||||
|
||||
<NameInputDialog
|
||||
open={openNameInput}
|
||||
onClose={() => setOpenNameInput(false)}
|
||||
title={pt("Add person") /* TODO-Cluster */}
|
||||
placeholder={t("enter_name")}
|
||||
initialValue={""}
|
||||
submitButtonTitle={t("add")}
|
||||
onSubmit={addPersonWithName}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -7,16 +7,12 @@
|
||||
* there.
|
||||
*/
|
||||
|
||||
import type { Person } from "@/new/photos/services/ml/people";
|
||||
import { pt } from "@/base/i18n";
|
||||
import type { SearchOption } from "@/new/photos/services/search/types";
|
||||
import OverflowMenu from "@ente/shared/components/OverflowMenu/menu";
|
||||
import { OverflowMenuOption } from "@ente/shared/components/OverflowMenu/option";
|
||||
import EditIcon from "@mui/icons-material/Edit";
|
||||
import MoreHoriz from "@mui/icons-material/MoreHoriz";
|
||||
import { VerticallyCentered } from "@ente/shared/components/Container";
|
||||
import { Typography } from "@mui/material";
|
||||
import { t } from "i18next";
|
||||
import React from "react";
|
||||
import { SpaceBetweenFlex } from "../mui-custom";
|
||||
import { GalleryItemsHeaderAdapter, GalleryItemsSummary } from "./ListHeader";
|
||||
|
||||
/**
|
||||
@@ -47,36 +43,16 @@ export const SearchResultsHeader: React.FC<SearchResultsHeaderProps> = ({
|
||||
</GalleryItemsHeaderAdapter>
|
||||
);
|
||||
|
||||
interface PeopleListHeaderProps {
|
||||
person: Person;
|
||||
}
|
||||
|
||||
export const PersonListHeader: React.FC<PeopleListHeaderProps> = ({
|
||||
person,
|
||||
}) => {
|
||||
const hasOptions = process.env.NEXT_PUBLIC_ENTE_WIP_CL;
|
||||
return (
|
||||
<GalleryItemsHeaderAdapter>
|
||||
<SpaceBetweenFlex>
|
||||
<GalleryItemsSummary
|
||||
name={person.name ?? "Unnamed person"}
|
||||
nameProps={person.name ? {} : { color: "text.muted" }}
|
||||
fileCount={person.fileIDs.length}
|
||||
/>
|
||||
{hasOptions && (
|
||||
<OverflowMenu
|
||||
ariaControls={"person-options"}
|
||||
triggerButtonIcon={<MoreHoriz />}
|
||||
>
|
||||
<OverflowMenuOption
|
||||
startIcon={<EditIcon />}
|
||||
onClick={() => console.log("test")}
|
||||
>
|
||||
{t("download_album")}
|
||||
</OverflowMenuOption>
|
||||
</OverflowMenu>
|
||||
)}
|
||||
</SpaceBetweenFlex>
|
||||
</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>
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user