Merge branch 'main' into monorepo

This commit is contained in:
Abhinav 2023-04-29 21:59:20 +05:30
commit 2de3eff0e1
25 changed files with 1141 additions and 1120 deletions

File diff suppressed because it is too large Load diff

View file

@ -44,477 +44,477 @@
"CREATE_COLLECTION": "Nieuw album",
"ENTER_ALBUM_NAME": "Album naam",
"CLOSE_OPTION": "Sluiten (Esc)",
"ENTER_FILE_NAME": "",
"CLOSE": "",
"NO": "",
"NOTHING_HERE": "",
"UPLOAD": "",
"IMPORT": "",
"ADD_PHOTOS": "",
"ADD_MORE_PHOTOS": "",
"add_photos_one": "",
"add_photos_other": "",
"SELECT_PHOTOS": "",
"FILE_UPLOAD": "",
"ENTER_FILE_NAME": "Bestandsnaam",
"CLOSE": "Sluiten",
"NO": "Nee",
"NOTHING_HERE": "Nog niets te zien hier 👀",
"UPLOAD": "Uploaden",
"IMPORT": "Importeren",
"ADD_PHOTOS": "Foto's toevoegen",
"ADD_MORE_PHOTOS": "Meer foto's toevoegen",
"add_photos_one": "1 foto toevoegen",
"add_photos_other": "{{count, number}} foto's toevoegen",
"SELECT_PHOTOS": "Selecteer foto's",
"FILE_UPLOAD": "Bestand uploaden",
"UPLOAD_STAGE_MESSAGE": {
"0": "",
"1": "",
"2": "",
"3": "",
"4": "",
"5": ""
"0": "Upload wordt voorbereid",
"1": "Lezen van Google metadata bestanden",
"2": "{{uploadCounter.finished, number}} / {{uploadCounter.total, number}} bestanden metadata uitgepakt",
"3": "{{uploadCounter.finished, number}} / {{uploadCounter.total, number}} bestanden geback-upt",
"4": "Resterende uploads worden geannuleerd",
"5": "Back-up voltooid"
},
"FILE_NOT_UPLOADED_LIST": "",
"SUBSCRIPTION_EXPIRED": "",
"SUBSCRIPTION_EXPIRED_MESSAGE": "",
"STORAGE_QUOTA_EXCEEDED": "",
"INITIAL_LOAD_DELAY_WARNING": "",
"USER_DOES_NOT_EXIST": "",
"NO_ACCOUNT": "",
"ACCOUNT_EXISTS": "",
"CREATE": "",
"DOWNLOAD": "",
"DOWNLOAD_OPTION": "",
"DOWNLOAD_FAVORITES": "",
"DOWNLOAD_UNCATEGORIZED": "",
"COPY_OPTION": "",
"TOGGLE_FULLSCREEN": "",
"ZOOM_IN_OUT": "",
"PREVIOUS": "",
"NEXT": "",
"TITLE_PHOTOS": "",
"TITLE_ALBUMS": "",
"TITLE_AUTH": "",
"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": "",
"GO_BACK": "",
"RECOVERY_KEY": "",
"SAVE_LATER": "",
"SAVE": "",
"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": "",
"CONFIRM": "",
"CANCEL": "",
"LOGOUT": "",
"DELETE_ACCOUNT": "",
"DELETE_ACCOUNT_MESSAGE": "",
"LOGOUT_MESSAGE": "",
"CHANGE_EMAIL": "",
"OK": "",
"SUCCESS": "",
"ERROR": "",
"MESSAGE": "",
"INSTALL_MOBILE_APP": "",
"DOWNLOAD_APP_MESSAGE": "",
"DOWNLOAD_APP": "",
"EXPORT": "",
"SUBSCRIPTION": "",
"SUBSCRIBE": "",
"MANAGEMENT_PORTAL": "",
"MANAGE_FAMILY_PORTAL": "",
"LEAVE_FAMILY_PLAN": "",
"LEAVE": "",
"LEAVE_FAMILY_CONFIRM": "",
"CHOOSE_PLAN": "",
"MANAGE_PLAN": "",
"ACTIVE": "",
"OFFLINE_MSG": "",
"FREE_SUBSCRIPTION_INFO": "",
"FAMILY_SUBSCRIPTION_INFO": "",
"RENEWAL_ACTIVE_SUBSCRIPTION_STATUS": "",
"RENEWAL_CANCELLED_SUBSCRIPTION_STATUS": "",
"RENEWAL_CANCELLED_SUBSCRIPTION_INFO": "",
"STORAGE_QUOTA_EXCEEDED_SUBSCRIPTION_INFO": "",
"SUBSCRIPTION_PURCHASE_SUCCESS": "",
"SUBSCRIPTION_PURCHASE_CANCELLED": "",
"SUBSCRIPTION_PURCHASE_FAILED": "",
"SUBSCRIPTION_UPDATE_FAILED": "",
"UPDATE_PAYMENT_METHOD_MESSAGE": "",
"STRIPE_AUTHENTICATION_FAILED": "",
"UPDATE_PAYMENT_METHOD": "",
"MONTHLY": "",
"YEARLY": "",
"UPDATE_SUBSCRIPTION_MESSAGE": "",
"UPDATE_SUBSCRIPTION": "",
"CANCEL_SUBSCRIPTION": "",
"CANCEL_SUBSCRIPTION_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_COLLECTION": "",
"DELETE_COLLECTION_TITLE": "",
"DELETE_COLLECTION": "",
"DELETE_COLLECTION_MESSAGE": "",
"DELETE_PHOTOS": "",
"KEEP_PHOTOS": "",
"SHARE": "",
"SHARE_COLLECTION": "",
"SHAREES": "",
"SHARE_WITH_SELF": "",
"ALREADY_SHARED": "",
"SHARING_BAD_REQUEST_ERROR": "",
"SHARING_DISABLED_FOR_FREE_ACCOUNTS": "",
"DOWNLOAD_COLLECTION": "",
"DOWNLOAD_COLLECTION_MESSAGE": "",
"CREATE_ALBUM_FAILED": "",
"SEARCH": "",
"SEARCH_RESULTS": "",
"NO_RESULTS": "",
"SEARCH_HINT": "",
"FILE_NOT_UPLOADED_LIST": "De volgende bestanden zijn niet geüpload",
"SUBSCRIPTION_EXPIRED": "Abonnement verlopen",
"SUBSCRIPTION_EXPIRED_MESSAGE": "Uw abonnement is verlopen, gelieve <a>vernieuwen</a>",
"STORAGE_QUOTA_EXCEEDED": "Opslaglimiet overschreden",
"INITIAL_LOAD_DELAY_WARNING": "Eerste keer laden kan enige tijd duren",
"USER_DOES_NOT_EXIST": "Sorry, we konden geen account met dat e-mailadres vinden",
"NO_ACCOUNT": "Heb nog geen account",
"ACCOUNT_EXISTS": "Heb al een account",
"CREATE": "Creëren",
"DOWNLOAD": "Downloaden",
"DOWNLOAD_OPTION": "Downloaden (D)",
"DOWNLOAD_FAVORITES": "Favorieten downloaden",
"DOWNLOAD_UNCATEGORIZED": "Ongecategoriseerd downloaden",
"COPY_OPTION": "Kopiëren als PNG (Ctrl/Cmd - C)",
"TOGGLE_FULLSCREEN": "Schakelen volledig scherm modus (F)",
"ZOOM_IN_OUT": "In/uitzoomen",
"PREVIOUS": "Vorige (←)",
"NEXT": "Volgende (→)",
"TITLE_PHOTOS": "ente Photos",
"TITLE_ALBUMS": "ente Albums",
"TITLE_AUTH": "ente Auth",
"UPLOAD_FIRST_PHOTO": "Je eerste foto uploaden",
"IMPORT_YOUR_FOLDERS": "Importeer uw mappen",
"UPLOAD_DROPZONE_MESSAGE": "Sleep om een back-up van je bestanden te maken",
"WATCH_FOLDER_DROPZONE_MESSAGE": "Sleep om map aan watched folders toe te voegen",
"TRASH_FILES_TITLE": "Bestanden verwijderen?",
"TRASH_FILE_TITLE": "Verwijder bestand?",
"DELETE_FILES_TITLE": "Onmiddellijk verwijderen?",
"DELETE_FILES_MESSAGE": "Geselecteerde bestanden zullen permanent worden verwijderd van je ente account.",
"DELETE": "Verwijderen",
"DELETE_OPTION": "Verwijderen (DEL)",
"FAVORITE_OPTION": "Favoriet (L)",
"UNFAVORITE_OPTION": "Verwijderen uit Favorieten (L)",
"MULTI_FOLDER_UPLOAD": "Meerdere mappen gedetecteerd",
"UPLOAD_STRATEGY_CHOICE": "Wilt u deze uploaden naar",
"UPLOAD_STRATEGY_SINGLE_COLLECTION": "Één enkel album",
"OR": "of",
"UPLOAD_STRATEGY_COLLECTION_PER_FOLDER": "Aparte albums maken",
"SESSION_EXPIRED_MESSAGE": "Uw sessie is verlopen. Meld u opnieuw aan om verder te gaan",
"SESSION_EXPIRED": "Sessie verlopen",
"PASSWORD_GENERATION_FAILED": "Uw browser kon geen sterke sleutel genereren die voldoet aan onze versleutelingsstandaarden. Probeer de mobiele app of een andere browser te gebruiken",
"CHANGE_PASSWORD": "Wachtwoord wijzigen",
"GO_BACK": "Ga terug",
"RECOVERY_KEY": "Herstelsleutel",
"SAVE_LATER": "Doe dit later",
"SAVE": "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",
"FORGOT_PASSWORD": "Wachtwoord vergeten",
"RECOVER_ACCOUNT": "Account herstellen",
"RECOVERY_KEY_HINT": "Herstelsleutel",
"RECOVER": "Herstellen",
"NO_RECOVERY_KEY": "Geen herstelsleutel?",
"INCORRECT_RECOVERY_KEY": "Onjuiste herstelsleutel",
"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",
"CANCEL": "Annuleren",
"LOGOUT": "Uitloggen",
"DELETE_ACCOUNT": "Account verwijderen",
"DELETE_ACCOUNT_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",
"ERROR": "Foutmelding",
"MESSAGE": "Melding",
"INSTALL_MOBILE_APP": "Installeer onze <a>Android</a> of <b>iOS</b> app om automatisch een back-up te maken van al uw foto's",
"DOWNLOAD_APP_MESSAGE": "Sorry, deze bewerking wordt momenteel alleen ondersteund op onze desktop app",
"DOWNLOAD_APP": "Download de desktop app",
"EXPORT": "Data exporteren",
"SUBSCRIPTION": "Abonnement",
"SUBSCRIBE": "Abonneren",
"MANAGEMENT_PORTAL": "Betaalmethode beheren",
"MANAGE_FAMILY_PORTAL": "Familie abonnement beheren",
"LEAVE_FAMILY_PLAN": "Familie abonnement verlaten",
"LEAVE": "Verlaten",
"LEAVE_FAMILY_CONFIRM": "Weet je zeker dat je het familie-plan wilt verlaten?",
"CHOOSE_PLAN": "Kies uw abonnement",
"MANAGE_PLAN": "Beheer uw abonnement",
"ACTIVE": "Actief",
"OFFLINE_MSG": "Je bent offline, lokaal opgeslagen herinneringen worden getoond",
"FREE_SUBSCRIPTION_INFO": "Je hebt het <strong>gratis</strong> abonnement dat verloopt op {{date, dateTime}}",
"FAMILY_SUBSCRIPTION_INFO": "U hebt een familieplan dat beheerd wordt door",
"RENEWAL_ACTIVE_SUBSCRIPTION_STATUS": "Vernieuwt op {{date, dateTime}}",
"RENEWAL_CANCELLED_SUBSCRIPTION_STATUS": "Eindigt op {{date, dateTime}}",
"RENEWAL_CANCELLED_SUBSCRIPTION_INFO": "Uw abonnement loopt af op {{date, dateTime}}",
"STORAGE_QUOTA_EXCEEDED_SUBSCRIPTION_INFO": "U heeft uw opslaglimiet overschreden, gelieve <a>upgraden</a>",
"SUBSCRIPTION_PURCHASE_SUCCESS": "<p>We hebben uw betaling ontvangen</p><p>Uw abonnement is geldig tot <strong>{{date, dateTime}}</strong></p>",
"SUBSCRIPTION_PURCHASE_CANCELLED": "Uw aankoop is geannuleerd, probeer het opnieuw als u zich wilt abonneren",
"SUBSCRIPTION_PURCHASE_FAILED": "Betaling van abonnement mislukt Probeer het opnieuw",
"SUBSCRIPTION_UPDATE_FAILED": "Niet gelukt om abonnement bij te werken, probeer het opnieuw",
"UPDATE_PAYMENT_METHOD_MESSAGE": "Het spijt ons, maar de betaling is mislukt bij het in rekening brengen van uw kaart, gelieve uw betaalmethode bij te werken en het opnieuw te proberen",
"STRIPE_AUTHENTICATION_FAILED": "We zijn niet in staat om uw betaalmethode te verifiëren. Kies een andere betaalmethode en probeer het opnieuw",
"UPDATE_PAYMENT_METHOD": "Betalingsmethode bijwerken",
"MONTHLY": "Maandelijks",
"YEARLY": "Jaarlijks",
"UPDATE_SUBSCRIPTION_MESSAGE": "Weet u zeker dat u uw abonnement wilt wijzigen?",
"UPDATE_SUBSCRIPTION": "Abonnement wijzigen",
"CANCEL_SUBSCRIPTION": "Abonnement opzeggen",
"CANCEL_SUBSCRIPTION_MESSAGE": "<p>Al je gegevens zullen worden verwijderd van onze servers aan het einde van deze factureringsperiode.</p><p>Weet u zeker dat u uw abonnement wilt opzeggen?</p>",
"SUBSCRIPTION_CANCEL_FAILED": "Abonnement opzeggen mislukt",
"SUBSCRIPTION_CANCEL_SUCCESS": "Abonnement succesvol geannuleerd",
"REACTIVATE_SUBSCRIPTION": "Abonnement opnieuw activeren",
"REACTIVATE_SUBSCRIPTION_MESSAGE": "Zodra je weer bent geactiveerd, zal je worden gefactureerd op {{date, dateTime}}",
"SUBSCRIPTION_ACTIVATE_SUCCESS": "Abonnement succesvol geactiveerd ",
"SUBSCRIPTION_ACTIVATE_FAILED": "Heractiveren van abonnementsverlenging is mislukt",
"SUBSCRIPTION_PURCHASE_SUCCESS_TITLE": "Bedankt",
"CANCEL_SUBSCRIPTION_ON_MOBILE": "Mobiel abonnement opzeggen",
"CANCEL_SUBSCRIPTION_ON_MOBILE_MESSAGE": "Annuleer je abonnement via de mobiele app om je abonnement hier te activeren",
"MAIL_TO_MANAGE_SUBSCRIPTION": "Neem contact met ons op via <a>{{emailID}}</a> om uw abonnement te beheren",
"RENAME": "Naam wijzigen",
"RENAME_FILE": "Bestandsnaam wijzigen",
"RENAME_COLLECTION": "Albumnaam wijzigen",
"DELETE_COLLECTION_TITLE": "Verwijder album?",
"DELETE_COLLECTION": "Verwijder album",
"DELETE_COLLECTION_MESSAGE": "Verwijder de foto's (en video's) van dit album ook uit <a>alle</a> andere albums waar deze deel van uitmaken?",
"DELETE_PHOTOS": "Foto's verwijderen",
"KEEP_PHOTOS": "Foto's behouden",
"SHARE": "Delen",
"SHARE_COLLECTION": "Album delen",
"SHAREES": "Gedeeld met",
"SHARE_WITH_SELF": "Oeps, je kunt niet met jezelf delen",
"ALREADY_SHARED": "Oeps, je deelt dit al met {{email}}",
"SHARING_BAD_REQUEST_ERROR": "Album delen niet toegestaan",
"SHARING_DISABLED_FOR_FREE_ACCOUNTS": "Delen is uitgeschakeld voor gratis accounts",
"DOWNLOAD_COLLECTION": "Download album",
"DOWNLOAD_COLLECTION_MESSAGE": "<p>Weet je zeker dat je het volledige album wilt downloaden?</p><p>Alle bestanden worden in de wachtrij geplaatst voor downloaden</p>",
"CREATE_ALBUM_FAILED": "Aanmaken van album mislukt, probeer het opnieuw",
"SEARCH": "Zoeken",
"SEARCH_RESULTS": "Zoekresultaten",
"NO_RESULTS": "Geen resultaten gevonden",
"SEARCH_HINT": "Zoeken naar albums, datums ...",
"SEARCH_TYPE": {
"COLLECTION": "",
"LOCATION": "",
"DATE": "",
"FILE_NAME": "",
"THING": "",
"FILE_CAPTION": ""
"COLLECTION": "Album",
"LOCATION": "Locatie",
"DATE": "Datum",
"FILE_NAME": "Bestandsnaam",
"THING": "Inhoud",
"FILE_CAPTION": "Omschrijving"
},
"photos_count_zero": "",
"photos_count_one": "",
"photos_count_other": "",
"TERMS_AND_CONDITIONS": "",
"ADD_TO_COLLECTION": "",
"SELECTED": "",
"VIDEO_PLAYBACK_FAILED_DOWNLOAD_INSTEAD": "",
"PEOPLE": "",
"INDEXING_SCHEDULED": "",
"ANALYZING_PHOTOS": "",
"INDEXING_PEOPLE": "",
"INDEXING_DONE": "",
"UNIDENTIFIED_FACES": "",
"OBJECTS": "",
"TEXT": "",
"INFO": "",
"INFO_OPTION": "",
"FILE_NAME": "",
"CAPTION_PLACEHOLDER": "",
"LOCATION": "",
"SHOW_ON_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": "",
"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": "",
"DESTINATION": "",
"START": "",
"LAST_EXPORT_TIME": "",
"EXPORT_AGAIN": "",
"LOCAL_STORAGE_NOT_ACCESSIBLE": "",
"LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE": "",
"SEND_OTT": "",
"EMAIl_ALREADY_OWNED": "",
"ETAGS_BLOCKED": "",
"SKIPPED_VIDEOS_INFO": "",
"LIVE_PHOTOS_DETECTED": "",
"RETRY_FAILED": "",
"FAILED_UPLOADS": "",
"SKIPPED_FILES": "",
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "",
"UNSUPPORTED_FILES": "",
"SUCCESSFUL_UPLOADS": "",
"SKIPPED_INFO": "",
"UNSUPPORTED_INFO": "",
"BLOCKED_UPLOADS": "",
"SKIPPED_VIDEOS": "",
"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": "",
"UPLOAD_TO_COLLECTION": "",
"UNCATEGORIZED": "",
"ARCHIVE": "",
"ARCHIVE_COLLECTION": "",
"ARCHIVE_SECTION_NAME": "",
"ALL_SECTION_NAME": "",
"MOVE_TO_COLLECTION": "",
"UNARCHIVE": "",
"UNARCHIVE_COLLECTION": "",
"MOVE": "",
"ADD": "",
"REMOVE": "",
"YES_REMOVE": "",
"REMOVE_FROM_COLLECTION": "",
"TRASH": "",
"MOVE_TO_TRASH": "",
"TRASH_FILES_MESSAGE": "",
"TRASH_FILE_MESSAGE": "",
"DELETE_PERMANENTLY": "",
"RESTORE": "",
"RESTORE_TO_COLLECTION": "",
"EMPTY_TRASH": "",
"EMPTY_TRASH_TITLE": "",
"EMPTY_TRASH_MESSAGE": "",
"LEAVE_SHARED_ALBUM": "",
"LEAVE_ALBUM": "",
"LEAVE_SHARED_ALBUM_TITLE": "",
"LEAVE_SHARED_ALBUM_MESSAGE": "",
"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": "",
"COMPRESS_THUMBNAILS": "",
"THUMBNAIL_REPLACED": "",
"FIX_THUMBNAIL": "",
"FIX_THUMBNAIL_LATER": "",
"REPLACE_THUMBNAIL_NOT_STARTED": "",
"REPLACE_THUMBNAIL_COMPLETED": "",
"REPLACE_THUMBNAIL_NOOP": "",
"REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR": "",
"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": "",
"CUSTOM_TIME": "",
"REOPEN_PLAN_SELECTOR_MODAL": "",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "",
"INSTALL": "",
"SHARING_DETAILS": "",
"MODIFY_SHARING": "",
"NOT_FOUND": "",
"LINK_EXPIRED": "",
"LINK_EXPIRED_MESSAGE": "",
"MANAGE_LINK": "",
"LINK_TOO_MANY_REQUESTS": "",
"FILE_DOWNLOAD": "",
"LINK_PASSWORD_LOCK": "",
"PUBLIC_COLLECT": "",
"LINK_DEVICE_LIMIT": "",
"LINK_EXPIRY": "",
"NEVER": "",
"DISABLE_FILE_DOWNLOAD": "",
"DISABLE_FILE_DOWNLOAD_MESSAGE": "",
"MALICIOUS_CONTENT": "",
"COPYRIGHT": "",
"SHARED_USING": "",
"ENTE_IO": "",
"LIVE": "",
"DISABLE_PASSWORD": "",
"DISABLE_PASSWORD_MESSAGE": "",
"PASSWORD_LOCK": "",
"LOCK": "",
"DOWNLOAD_UPLOAD_LOGS": "",
"UPLOAD_FILES": "",
"UPLOAD_DIRS": "",
"UPLOAD_GOOGLE_TAKEOUT": "",
"DEDUPLICATE_FILES": "",
"AUTHENTICATOR_SECTION": "",
"NO_DUPLICATES_FOUND": "",
"CLUB_BY_CAPTURE_TIME": "",
"FILES": "",
"EACH": "",
"DEDUPLICATE_BASED_ON_SIZE": "",
"DEDUPLICATE_BASED_ON_SIZE_AND_CAPTURE_TIME": "",
"STOP_ALL_UPLOADS_MESSAGE": "",
"STOP_UPLOADS_HEADER": "",
"YES_STOP_UPLOADS": "",
"albums_one": "",
"albums_other": "",
"ALL_ALBUMS": "",
"ALBUMS": "",
"ENTER_TWO_FACTOR_OTP": "",
"CREATE_ACCOUNT": "",
"COPIED": "",
"CANVAS_BLOCKED_TITLE": "",
"CANVAS_BLOCKED_MESSAGE": "",
"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": "",
"MONTH_SHORT": "",
"YEAR": "",
"FAMILY_PLAN": "",
"DOWNLOAD_LOGS": "",
"DOWNLOAD_LOGS_MESSAGE": "",
"CHANGE_FOLDER": "",
"TWO_MONTHS_FREE": "",
"GB": "",
"POPULAR": "",
"FREE_PLAN_OPTION_LABEL": "",
"FREE_PLAN_DESCRIPTION": "",
"CURRENT_USAGE": "",
"WEAK_DEVICE": "",
"DRAG_AND_DROP_HINT": "",
"CONFIRM_ACCOUNT_DELETION_MESSAGE": "",
"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": "",
"ML_SEARCH": "",
"ENABLE_ML_SEARCH_DESCRIPTION": "",
"ML_MORE_DETAILS": "",
"ENABLE_FACE_SEARCH": "",
"ENABLE_FACE_SEARCH_TITLE": "",
"ENABLE_FACE_SEARCH_DESCRIPTION": "",
"DISABLE_BETA": "",
"DISABLE_FACE_SEARCH": "",
"DISABLE_FACE_SEARCH_TITLE": "",
"DISABLE_FACE_SEARCH_DESCRIPTION": "",
"ADVANCED": "",
"FACE_SEARCH_CONFIRMATION": "",
"LABS": "",
"YOURS": "",
"PASSPHRASE_STRENGTH_WEAK": "",
"PASSPHRASE_STRENGTH_MODERATE": "",
"PASSPHRASE_STRENGTH_STRONG": "",
"PREFERENCES": "",
"LANGUAGE": "",
"EXPORT_DIRECTORY_DOES_NOT_EXIST": "",
"EXPORT_DIRECTORY_DOES_NOT_EXIST_MESSAGE": "",
"SUBSCRIPTION_VERIFICATION_ERROR": "",
"photos_count_zero": "Geen herinneringen",
"photos_count_one": "1 herinnering",
"photos_count_other": "{{count, number}} herinneringen",
"TERMS_AND_CONDITIONS": "Ik ga akkoord met de <a>gebruiksvoorwaarden</a> en <b>privacybeleid</b>",
"ADD_TO_COLLECTION": "Toevoegen aan album",
"SELECTED": "geselecteerd",
"VIDEO_PLAYBACK_FAILED_DOWNLOAD_INSTEAD": "Deze video kan niet afgespeeld worden op uw browser",
"PEOPLE": "Personen",
"INDEXING_SCHEDULED": "indexering is gepland...",
"ANALYZING_PHOTOS": "analyseren van nieuwe foto's {{indexStatus.nSyncedFiles}} van {{indexStatus.nTotalFiles}} gedaan)...",
"INDEXING_PEOPLE": "mensen indexeren in {{indexStatus.nSyncedFiles}} foto's...",
"INDEXING_DONE": "{{indexStatus.nSyncedFiles}} geïndexeerde foto's",
"UNIDENTIFIED_FACES": "ongeïdentificeerde gezichten",
"OBJECTS": "objecten",
"TEXT": "tekst",
"INFO": "Info ",
"INFO_OPTION": "Info (I)",
"FILE_NAME": "Bestandsnaam",
"CAPTION_PLACEHOLDER": "Voeg een beschrijving toe",
"LOCATION": "Locatie",
"SHOW_ON_MAP": "Bekijk op OpenStreetMap",
"DETAILS": "Details",
"VIEW_EXIF": "Bekijk alle EXIF gegevens",
"NO_EXIF": "Geen EXIF gegevens",
"EXIF": "EXIF",
"ISO": "ISO",
"TWO_FACTOR": "Tweestaps",
"TWO_FACTOR_AUTHENTICATION": "Tweestapsverificatie",
"TWO_FACTOR_QR_INSTRUCTION": "Scan de onderstaande QR-code met uw favoriete verificatie app",
"ENTER_CODE_MANUALLY": "Voer de code handmatig in",
"TWO_FACTOR_MANUAL_CODE_INSTRUCTION": "Voer deze code in in uw favoriete verificatie app",
"SCAN_QR_CODE": "Scan QR-code in plaats daarvan",
"ENABLE_TWO_FACTOR": "Tweestapsverificatie inschakelen",
"ENABLE": "Inschakelen",
"LOST_DEVICE": "Tweestapsverificatie apparaat verloren",
"INCORRECT_CODE": "Onjuiste code",
"TWO_FACTOR_INFO": "Voeg een extra beveiligingslaag toe door meer dan uw e-mailadres en wachtwoord te vereisen om in te loggen op uw account",
"DISABLE_TWO_FACTOR_LABEL": "Schakel tweestapsverificatie uit",
"UPDATE_TWO_FACTOR_LABEL": "Update uw verificatie apparaat",
"DISABLE": "Uitschakelen",
"RECONFIGURE": "Herconfigureren",
"UPDATE_TWO_FACTOR": "Tweestapsverificatie bijwerken",
"UPDATE_TWO_FACTOR_MESSAGE": "Verder gaan zal elk eerder geconfigureerde verificatie apparaat ontzeggen",
"UPDATE": "Bijwerken",
"DISABLE_TWO_FACTOR": "Tweestapsverificatie uitschakelen",
"DISABLE_TWO_FACTOR_MESSAGE": "Weet u zeker dat u tweestapsverificatie wilt uitschakelen",
"TWO_FACTOR_DISABLE_FAILED": "Uitschakelen van tweestapsverificatie is mislukt, probeer het opnieuw",
"EXPORT_DATA": "Gegevens exporteren",
"SELECT_FOLDER": "Map selecteren",
"DESTINATION": "Bestemming",
"START": "Start",
"LAST_EXPORT_TIME": "Tijd laatste export",
"EXPORT_AGAIN": "Opnieuw synchroniseren",
"LOCAL_STORAGE_NOT_ACCESSIBLE": "Lokale opslag niet toegankelijk",
"LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE": "Je browser of een extensie blokkeert ente om gegevens op te slaan in de lokale opslag. Probeer deze pagina te laden na het aanpassen van de browser surfmodus.",
"SEND_OTT": "Stuur OTP",
"EMAIl_ALREADY_OWNED": "E-mail al in gebruik",
"ETAGS_BLOCKED": "<p>We kunnen de volgende bestanden niet uploaden vanwege uw browserconfiguratie.</p><p>Schakel alle extensies uit die mogelijk voorkomen dat ente <code>eTags</code> kan gebruiken om grote bestanden te uploaden, of gebruik onze <a>desktop app</a> voor een betrouwbaardere import ervaring.</p>",
"SKIPPED_VIDEOS_INFO": "<p>We ondersteunen het toevoegen van video's via openbare links momenteel niet.</p><p>Om video's te delen, <a>meld je aan</a> bij ente en deel met de beoogde ontvangers via hun e-mail</p>",
"LIVE_PHOTOS_DETECTED": "De foto en video bestanden van je Live Photos zijn samengevoegd tot één enkel bestand",
"RETRY_FAILED": "Probeer mislukte uploads nogmaals",
"FAILED_UPLOADS": "Mislukte uploads ",
"SKIPPED_FILES": "Genegeerde uploads",
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "Thumbnail generatie mislukt",
"UNSUPPORTED_FILES": "Niet-ondersteunde bestanden",
"SUCCESSFUL_UPLOADS": "Succesvolle uploads",
"SKIPPED_INFO": "Deze zijn overgeslagen omdat er bestanden zijn met overeenkomende namen in hetzelfde album",
"UNSUPPORTED_INFO": "ente ondersteunt deze bestandsformaten nog niet",
"BLOCKED_UPLOADS": "Geblokkeerde uploads",
"SKIPPED_VIDEOS": "Overgeslagen video's",
"INPROGRESS_METADATA_EXTRACTION": "In behandeling",
"INPROGRESS_UPLOADS": "Bezig met uploaden",
"TOO_LARGE_UPLOADS": "Grote bestanden",
"LARGER_THAN_AVAILABLE_STORAGE_UPLOADS": "Onvoldoende opslagruimte",
"LARGER_THAN_AVAILABLE_STORAGE_INFO": "Deze bestanden zijn niet geüpload omdat ze de maximale grootte van uw opslagplan overschrijden",
"TOO_LARGE_INFO": "Deze bestanden zijn niet geüpload omdat ze onze limiet voor bestandsgrootte overschrijden",
"THUMBNAIL_GENERATION_FAILED_INFO": "Deze bestanden zijn geüpload, maar helaas konden we geen thumbnails voor ze genereren.",
"UPLOAD_TO_COLLECTION": "Uploaden naar album",
"UNCATEGORIZED": "Ongecategoriseerd",
"ARCHIVE": "Archiveren",
"ARCHIVE_COLLECTION": "Album archiveren",
"ARCHIVE_SECTION_NAME": "Archief",
"ALL_SECTION_NAME": "Alle",
"MOVE_TO_COLLECTION": "Verplaats naar album",
"UNARCHIVE": "Uit archief halen",
"UNARCHIVE_COLLECTION": "Album uit archief halen",
"MOVE": "Verplaatsen",
"ADD": "Toevoegen",
"REMOVE": "Verwijderen",
"YES_REMOVE": "Ja, verwijderen",
"REMOVE_FROM_COLLECTION": "Verwijderen uit album",
"TRASH": "Prullenbak",
"MOVE_TO_TRASH": "Verplaatsen naar prullenbak",
"TRASH_FILES_MESSAGE": "De geselecteerde bestanden worden verwijderd uit alle albums en verplaatst naar de prullenbak.",
"TRASH_FILE_MESSAGE": "Het bestand wordt uit alle albums verwijderd en verplaatst naar de prullenbak.",
"DELETE_PERMANENTLY": "Permanent verwijderen",
"RESTORE": "Herstellen",
"RESTORE_TO_COLLECTION": "Terugzetten naar album",
"EMPTY_TRASH": "Prullenbak leegmaken",
"EMPTY_TRASH_TITLE": "Prullenbak leegmaken?",
"EMPTY_TRASH_MESSAGE": "Geselecteerde bestanden zullen permanent worden verwijderd van uw ente account.",
"LEAVE_SHARED_ALBUM": "Ja, verwijderen",
"LEAVE_ALBUM": "Album verlaten",
"LEAVE_SHARED_ALBUM_TITLE": "Gedeeld album verwijderen?",
"LEAVE_SHARED_ALBUM_MESSAGE": "Je verlaat het album, en het zal niet meer zichtbaar voor je zijn.",
"NOT_FILE_OWNER": "U kunt bestanden niet verwijderen in een gedeeld album",
"CONFIRM_SELF_REMOVE_MESSAGE": "De geselecteerde items worden verwijderd uit dit album. De items die alleen in dit album staan, worden verplaatst naar 'Niet gecategoriseerd'.",
"CONFIRM_SELF_AND_OTHER_REMOVE_MESSAGE": "Sommige van de items die u verwijdert zijn door andere mensen toegevoegd, en u verliest de toegang daartoe.",
"SORT_BY_CREATION_TIME_ASCENDING": "Oudste",
"SORT_BY_UPDATION_TIME_DESCENDING": "Laatst gewijzigd op",
"SORT_BY_NAME": "Naam",
"COMPRESS_THUMBNAILS": "Comprimeren van thumbnails",
"THUMBNAIL_REPLACED": "Thumbnails gecomprimeerd",
"FIX_THUMBNAIL": "Comprimeren",
"FIX_THUMBNAIL_LATER": "Later comprimeren",
"REPLACE_THUMBNAIL_NOT_STARTED": "Sommige van uw video thumbnails kunnen worden gecomprimeerd om ruimte te besparen. Wilt u dat ente ze comprimeert?",
"REPLACE_THUMBNAIL_COMPLETED": "Alle thumbnails zijn gecomprimeerd",
"REPLACE_THUMBNAIL_NOOP": "Je hebt geen thumbnails die verder gecomprimeerd kunnen worden",
"REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR": "Kon sommige van uw thumbnails niet comprimeren, probeer het opnieuw",
"FIX_CREATION_TIME": "Herstel tijd",
"FIX_CREATION_TIME_IN_PROGRESS": "Tijd aan het herstellen",
"CREATION_TIME_UPDATED": "Bestandstijd bijgewerkt",
"UPDATE_CREATION_TIME_NOT_STARTED": "Selecteer de optie die u wilt gebruiken",
"UPDATE_CREATION_TIME_COMPLETED": "Alle bestanden succesvol bijgewerkt",
"UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR": "Bestandstijd update mislukt voor sommige bestanden, probeer het opnieuw",
"CAPTION_CHARACTER_LIMIT": "5000 tekens max",
"DATE_TIME_ORIGINAL": "EXIF:DatumTijdOrigineel",
"DATE_TIME_DIGITIZED": "EXIF:DatumTijdDigitaliseerd",
"CUSTOM_TIME": "Aangepaste tijd",
"REOPEN_PLAN_SELECTOR_MODAL": "Abonnementen heropenen",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "Kon abonnementen niet openen",
"INSTALL": "Installeren",
"SHARING_DETAILS": "Delen van informatie",
"MODIFY_SHARING": "Delen wijzigen",
"NOT_FOUND": "404 - niet gevonden",
"LINK_EXPIRED": "Link verlopen",
"LINK_EXPIRED_MESSAGE": "Deze link is verlopen of uitgeschakeld!",
"MANAGE_LINK": "Link beheren",
"LINK_TOO_MANY_REQUESTS": "Dit album is te populair voor ons om te verwerken!",
"FILE_DOWNLOAD": "Downloads toestaan",
"LINK_PASSWORD_LOCK": "Wachtwoord versleuteling",
"PUBLIC_COLLECT": "Foto's toevoegen toestaan",
"LINK_DEVICE_LIMIT": "Apparaat limiet",
"LINK_EXPIRY": "Vervaldatum link",
"NEVER": "Nooit",
"DISABLE_FILE_DOWNLOAD": "Download uitschakelen",
"DISABLE_FILE_DOWNLOAD_MESSAGE": "<p>Weet u zeker dat u de downloadknop voor bestanden wilt uitschakelen?</p><p>Kijkers kunnen nog steeds screenshots maken of een kopie van uw foto's opslaan met behulp van externe hulpmiddelen.</p>",
"MALICIOUS_CONTENT": "Bevat kwaadwillende inhoud",
"COPYRIGHT": "Schending van het auteursrecht van iemand die ik mag vertegenwoordigen",
"SHARED_USING": "Gedeeld via ",
"ENTE_IO": "ente.io",
"LIVE": "LIVE",
"DISABLE_PASSWORD": "Schakel cijfercode vergrendeling uit",
"DISABLE_PASSWORD_MESSAGE": "Weet u zeker dat u de cijfercode vergrendeling wilt uitschakelen?",
"PASSWORD_LOCK": "Cijfercode vergrendeling",
"LOCK": "Vergrendeling",
"DOWNLOAD_UPLOAD_LOGS": "Logboeken voor foutmeldingen",
"UPLOAD_FILES": "Bestand",
"UPLOAD_DIRS": "Map",
"UPLOAD_GOOGLE_TAKEOUT": "Google takeout",
"DEDUPLICATE_FILES": "Dubbele bestanden verwijderen",
"AUTHENTICATOR_SECTION": "Verificatie apparaat",
"NO_DUPLICATES_FOUND": "Je hebt geen dubbele bestanden die kunnen worden gewist",
"CLUB_BY_CAPTURE_TIME": "Samenvoegen op tijd",
"FILES": "Bestanden",
"EACH": "Elke",
"DEDUPLICATE_BASED_ON_SIZE": "De volgende bestanden zijn samengevoegd op basis van hun groottes. Controleer en verwijder items waarvan je denkt dat ze dubbel zijn",
"DEDUPLICATE_BASED_ON_SIZE_AND_CAPTURE_TIME": "De volgende bestanden zijn samengevoegd op basis van hun groottes en opnametijd, bekijk en verwijder items waarvan je denkt dat ze dubbel zijn",
"STOP_ALL_UPLOADS_MESSAGE": "Weet u zeker dat u wilt stoppen met alle uploads die worden uitgevoerd?",
"STOP_UPLOADS_HEADER": "Stoppen met uploaden?",
"YES_STOP_UPLOADS": "Ja, stop uploaden",
"albums_one": "1 Album",
"albums_other": "{{count, number}} Albums",
"ALL_ALBUMS": "Alle albums",
"ALBUMS": "Albums",
"ENTER_TWO_FACTOR_OTP": "Voer de 6-cijferige code van uw verificatie app in.",
"CREATE_ACCOUNT": "Account aanmaken",
"COPIED": "Gekopieerd",
"CANVAS_BLOCKED_TITLE": "Kan thumbnail niet genereren",
"CANVAS_BLOCKED_MESSAGE": "<p>Het lijkt erop dat uw browser geen toegang heeft tot canvas, die nodig is om thumbnails voor uw foto's te genereren </p> <p> Schakel toegang tot het canvas van uw browser in, of bekijk onze desktop app</p>",
"WATCH_FOLDERS": "Monitor mappen",
"UPGRADE_NOW": "Nu upgraden",
"RENEW_NOW": "Nu verlengen",
"STORAGE": "Opslagruimte",
"USED": "gebruikt",
"YOU": "Jij",
"FAMILY": "Familie",
"FREE": "free",
"OF": "van",
"WATCHED_FOLDERS": "Gemonitorde mappen",
"NO_FOLDERS_ADDED": "Nog geen mappen toegevoegd!",
"FOLDERS_AUTOMATICALLY_MONITORED": "De mappen die u hier toevoegt worden automatisch gemonitord",
"UPLOAD_NEW_FILES_TO_ENTE": "Nieuwe bestanden uploaden naar ente",
"REMOVE_DELETED_FILES_FROM_ENTE": "Verwijderde bestanden van ente opruimen",
"ADD_FOLDER": "Map toevoegen",
"STOP_WATCHING": "Stop monitoren",
"STOP_WATCHING_FOLDER": "Stop monitoren van map?",
"STOP_WATCHING_DIALOG_MESSAGE": "Uw bestaande bestanden zullen niet worden verwijderd, maar ente stopt met het automatisch bijwerken van het gekoppelde ente album bij wijzigingen in deze map.",
"YES_STOP": "Ja, stop",
"MONTH_SHORT": "mo",
"YEAR": "jaar",
"FAMILY_PLAN": "Familie abonnement",
"DOWNLOAD_LOGS": "Logboek downloaden",
"DOWNLOAD_LOGS_MESSAGE": "<p>Dit zal logboeken downloaden, die u ons kunt e-mailen om te helpen bij het debuggen van uw probleem.</p><p> Houd er rekening mee dat bestandsnamen worden opgenomen om problemen met specifieke bestanden bij te houden. </p>",
"CHANGE_FOLDER": "Map wijzigen",
"TWO_MONTHS_FREE": "Krijg 2 maanden gratis op jaarlijkse abonnementen",
"GB": "GB",
"POPULAR": "Populair",
"FREE_PLAN_OPTION_LABEL": "Doorgaan met gratis account",
"FREE_PLAN_DESCRIPTION": "1 GB voor 1 jaar",
"CURRENT_USAGE": "Huidig gebruik is <strong>{{usage}}</strong>",
"WEAK_DEVICE": "De webbrowser die u gebruikt is niet krachtig genoeg om uw foto's te versleutelen. Probeer in te loggen op uw computer, of download de ente mobiel/desktop app.",
"DRAG_AND_DROP_HINT": "Of sleep en plaats in het ente venster",
"CONFIRM_ACCOUNT_DELETION_MESSAGE": "Uw geüploade gegevens worden gepland voor verwijdering, en uw account zal permanent worden verwijderd.<br/><br/>Deze actie is onomkeerbaar.",
"AUTHENTICATE": "Verifiëren",
"UPLOADED_TO_SINGLE_COLLECTION": "Geüpload naar enkele collectie",
"UPLOADED_TO_SEPARATE_COLLECTIONS": "Geüpload naar verschillende collecties",
"NEVERMIND": "Laat maar",
"UPDATE_AVAILABLE": "Update beschikbaar",
"UPDATE_INSTALLABLE_MESSAGE": "Er staat een nieuwe versie van ente klaar om te worden geïnstalleerd.",
"INSTALL_NOW": "Nu installeren",
"INSTALL_ON_NEXT_LAUNCH": "Installeren bij volgende start",
"UPDATE_AVAILABLE_MESSAGE": "Er is een nieuwe versie van ente vrijgegeven, maar deze kan niet automatisch worden gedownload en geïnstalleerd.",
"DOWNLOAD_AND_INSTALL": "Downloaden en installeren",
"IGNORE_THIS_VERSION": "Negeer deze versie",
"TODAY": "Vandaag",
"YESTERDAY": "Gisteren",
"NAME_PLACEHOLDER": "Naam...",
"ROOT_LEVEL_FILE_WITH_FOLDER_NOT_ALLOWED": "Kan geen albums maken uit bestand/map mix",
"ROOT_LEVEL_FILE_WITH_FOLDER_NOT_ALLOWED_MESSAGE": "<p>Je hebt een mix van bestanden en mappen gesleept en laten vallen.</p><p>Geef ofwel alleen bestanden aan, of alleen mappen bij het selecteren van de optie om afzonderlijke albums te maken</p>",
"CHOSE_THEME": "Kies thema",
"ML_SEARCH": "ML zoeken (bèta)",
"ENABLE_ML_SEARCH_DESCRIPTION": "<p>Dit zal algoritmes op het apparaat inschakelen die zullen beginnen met het lokaal analyseren van uw geüploade foto's.</p><p>Voor het eerst na inloggen of het inschakelen van deze functie zal het alle afbeeldingen op het lokale apparaat downloaden om ze te analyseren. Schakel dit dus alleen in als je akkoord bent met gegevensverbruik en lokale verwerking van alle afbeeldingen in uw fotobibliotheek.</p><p>Als dit de eerste keer is dat uw dit inschakelt, vragen we u ook om toestemming om gegevens te verwerken.</p>",
"ML_MORE_DETAILS": "Meer details",
"ENABLE_FACE_SEARCH": "Zoeken op gezichten inschakelen",
"ENABLE_FACE_SEARCH_TITLE": "Zoeken op gezichten inschakelen?",
"ENABLE_FACE_SEARCH_DESCRIPTION": "<p>Als u zoeken op gezichten inschakelt, analyseert ente de gezichtsgeometrie uit uw foto's. Dit gebeurt op uw apparaat en alle gegenereerde biometrische gegevens worden end-to-end versleuteld.<p/><p><a>Klik hier voor meer informatie over deze functie in ons privacybeleid</a></p>",
"DISABLE_BETA": "Bèta uitschakelen",
"DISABLE_FACE_SEARCH": "Zoeken op gezichten uitschakelen",
"DISABLE_FACE_SEARCH_TITLE": "Zoeken op gezichten uitschakelen?",
"DISABLE_FACE_SEARCH_DESCRIPTION": "<p>ente zal stoppen met het analyseren van de gezichtsgeometrie, en zal ML zoeken (beta) uitschakelen</p><p>U kan zoeken op gezichten opnieuw inschakelen wanneer u wilt, dus deze handeling is veilig.</p>",
"ADVANCED": "Geavanceerd",
"FACE_SEARCH_CONFIRMATION": "Ik begrijp het, en wil ente toestaan om gezichten te analyseren",
"LABS": "Lab's",
"YOURS": "jouw",
"PASSPHRASE_STRENGTH_WEAK": "Wachtwoord sterkte: Zwak",
"PASSPHRASE_STRENGTH_MODERATE": "Wachtwoord sterkte: Matig",
"PASSPHRASE_STRENGTH_STRONG": "Wachtwoord sterkte: Sterk",
"PREFERENCES": "Instellingen",
"LANGUAGE": "Taal",
"EXPORT_DIRECTORY_DOES_NOT_EXIST": "Ongeldige export map",
"EXPORT_DIRECTORY_DOES_NOT_EXIST_MESSAGE": "<p>De export map die u heeft geselecteerd bestaat niet.</p><p> Selecteer een geldige map.</p>",
"SUBSCRIPTION_VERIFICATION_ERROR": "Abonnementsverificatie mislukt",
"STORAGE_UNITS": {
"B": "",
"KB": "",
"MB": "",
"GB": "",
"TB": ""
"B": "B",
"KB": "KB",
"MB": "MB",
"GB": "GB",
"TB": "TB"
},
"AFTER_TIME": {
"HOUR": "",
"DAY": "",
"WEEK": "",
"MONTH": "",
"YEAR": ""
"HOUR": "na één uur",
"DAY": "na één dag",
"WEEK": "na één week",
"MONTH": "na één maand",
"YEAR": "na één jaar"
},
"COPY_LINK": "",
"DONE": "",
"ADD_EMAIL_TITLE": "",
"LINK_SHARE_TITLE": "",
"REMOVE_LINK": "",
"CREATE_PUBLIC_SHARING": "",
"PUBLIC_LINK_CREATED": "",
"PUBLIC_LINK_ENABLED": "",
"COLLECT_PHOTOS": "",
"PUBLIC_COLLECT_SUBTEXT": "",
"STOP_EXPORT": "",
"EXPORT_PROGRESS": "",
"COPY_LINK": "Link kopiëren",
"DONE": "Voltooid",
"ADD_EMAIL_TITLE": "Delen met specifieke mensen",
"LINK_SHARE_TITLE": "Of deel een link",
"REMOVE_LINK": "Link verwijderen",
"CREATE_PUBLIC_SHARING": "Maak publieke link",
"PUBLIC_LINK_CREATED": "Publieke link aangemaakt",
"PUBLIC_LINK_ENABLED": "Publieke link ingeschakeld",
"COLLECT_PHOTOS": "Foto's verzamelen",
"PUBLIC_COLLECT_SUBTEXT": "Sta toe dat mensen met de link ook foto's kunnen toevoegen aan het gedeelde album.",
"STOP_EXPORT": "Stoppen",
"EXPORT_PROGRESS": "<a>{{progress.success}} / {{progress.total}}</a> bestanden geëxporteerd",
"EXPORT_NOTIFICATION": {
"START": "",
"IN_PROGRESS": "",
"FINISH": "",
"UP_TO_DATE": ""
"START": "Exporteren begonnen",
"IN_PROGRESS": "Exporteren is al bezig",
"FINISH": "Exporteren voltooid",
"UP_TO_DATE": "Geen nieuwe bestanden om te exporteren"
},
"CONTINUOUS_EXPORT": "",
"TOTAL_ITEMS": "",
"PENDING_ITEMS": "",
"EXPORT_STARTING": "",
"DELETE_ACCOUNT_REASON_LABEL": "",
"DELETE_ACCOUNT_REASON_PLACEHOLDER": "",
"CONTINUOUS_EXPORT": "Continue synchroniseren",
"TOTAL_ITEMS": "Totaal aantal bestanden",
"PENDING_ITEMS": "Bestanden in behandeling",
"EXPORT_STARTING": "Exporteren begonnen...",
"DELETE_ACCOUNT_REASON_LABEL": "Wat is de belangrijkste reden waarom je jouw account verwijdert?",
"DELETE_ACCOUNT_REASON_PLACEHOLDER": "Kies een reden",
"DELETE_REASON": {
"MISSING_FEATURE": "",
"BROKEN_BEHAVIOR": "",
"FOUND_ANOTHER_SERVICE": "",
"NOT_LISTED": ""
"MISSING_FEATURE": "Ik mis een belangrijke functie",
"BROKEN_BEHAVIOR": "De app of een bepaalde functie functioneert niet zoals ik verwacht",
"FOUND_ANOTHER_SERVICE": "Ik heb een andere dienst gevonden die me beter bevalt",
"NOT_LISTED": "Mijn reden wordt niet vermeld"
},
"DELETE_ACCOUNT_FEEDBACK_LABEL": "",
"DELETE_ACCOUNT_FEEDBACK_PLACEHOLDER": "",
"CONFIRM_DELETE_ACCOUNT_CHECKBOX_LABEL": "",
"CONFIRM_DELETE_ACCOUNT": "",
"FEEDBACK_REQUIRED": "",
"FEEDBACK_REQUIRED_FOUND_ANOTHER_SERVICE": "",
"RECOVER_TWO_FACTOR": "",
"at": "",
"AUTH_NEXT": "",
"AUTH_DOWNLOAD_MOBILE_APP": ""
"DELETE_ACCOUNT_FEEDBACK_LABEL": "We vinden het jammer je te zien gaan. Deel alsjeblieft je feedback om ons te helpen verbeteren.",
"DELETE_ACCOUNT_FEEDBACK_PLACEHOLDER": "Feedback",
"CONFIRM_DELETE_ACCOUNT_CHECKBOX_LABEL": "Ja, ik wil permanent mijn account inclusief alle gegevens verwijderen",
"CONFIRM_DELETE_ACCOUNT": "Account verwijderen bevestigen",
"FEEDBACK_REQUIRED": "Help ons alsjeblieft met deze informatie",
"FEEDBACK_REQUIRED_FOUND_ANOTHER_SERVICE": "Wat doet de andere dienst beter?",
"RECOVER_TWO_FACTOR": "Herstel tweestaps",
"at": "om",
"AUTH_NEXT": "volgende",
"AUTH_DOWNLOAD_MOBILE_APP": "Download onze mobiele app om uw geheimen te beheren"
}

View file

@ -9,7 +9,7 @@
"7": "cloudy sky",
"8": "greenery",
"9": "autumn leaves",
"10": "potrait",
"10": "portrait",
"11": "flower",
"12": "night shot",
"13": "stage concert",
@ -20,7 +20,7 @@
"18": "backlight",
"19": "text documents",
"20": "qr images",
"21": "group potrait",
"21": "group portrait",
"22": "computer screens",
"23": "kids",
"24": "dog",

View file

@ -1,7 +1,7 @@
import React, { useEffect } from 'react';
import React from 'react';
import { useRouter } from 'next/router';
import { sendOtt } from 'services/userService';
import { setData, LS_KEYS, getData } from 'utils/storage/localStorage';
import { setData, LS_KEYS } from 'utils/storage/localStorage';
import { PAGES } from 'constants/pages';
import FormPaperTitle from './Form/FormPaper/Title';
import FormPaperFooter from './Form/FormPaper/Footer';
@ -17,17 +17,6 @@ interface LoginProps {
export default function Login(props: LoginProps) {
const router = useRouter();
useEffect(() => {
const main = async () => {
router.prefetch(PAGES.VERIFY);
const user = getData(LS_KEYS.USER);
if (user?.email) {
await router.push(PAGES.VERIFY);
}
};
main();
}, []);
const loginUser: SingleInputFormProps['callback'] = async (
email,
setFieldError

View file

@ -1,8 +1,7 @@
import React, { useContext, useEffect, useState } from 'react';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { RenderFileName } from './RenderFileName';
import { RenderCreationTime } from './RenderCreationTime';
import { Box, DialogProps, Link, Stack, styled } from '@mui/material';
import { Location } from 'types/upload';
import { getEXIFLocation } from 'services/upload/exifService';
import { RenderCaption } from './RenderCaption';
@ -90,7 +89,6 @@ export function FileInfo({
isTrashCollection,
}: Iprops) {
const appContext = useContext(AppContext);
const [location, setLocation] = useState<Location>(null);
const [parsedExifData, setParsedExifData] = useState<Record<string, any>>();
const [showExif, setShowExif] = useState(false);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -99,25 +97,23 @@ export function FileInfo({
const openExif = () => setShowExif(true);
const closeExif = () => setShowExif(false);
useEffect(() => {
if (!location && file && file.metadata) {
const location = useMemo(() => {
if (file && file.metadata) {
if (file.metadata.longitude || file.metadata.longitude === 0) {
setLocation({
return {
latitude: file.metadata.latitude,
longitude: file.metadata.longitude,
});
};
}
}
}, [file]);
useEffect(() => {
if (!location && exif) {
if (exif) {
const exifLocation = getEXIFLocation(exif);
if (exifLocation.latitude || exifLocation.latitude === 0) {
setLocation(exifLocation);
return exifLocation;
}
}
}, [exif]);
return null;
}, [file, exif]);
useEffect(() => {
if (!exif) {
@ -205,10 +201,7 @@ export function FileInfo({
title={t('LOCATION')}
caption={
<Link
href={getOpenStreetMapLink({
latitude: file.metadata.latitude,
longitude: file.metadata.longitude,
})}
href={getOpenStreetMapLink(location)}
target="_blank"
sx={{ fontWeight: 'bold' }}>
{t('SHOW_ON_MAP')}
@ -216,10 +209,7 @@ export function FileInfo({
}
customEndButton={
<CopyButton
code={getOpenStreetMapLink({
latitude: file.metadata.latitude,
longitude: file.metadata.longitude,
})}
code={getOpenStreetMapLink(location)}
color="secondary"
size="medium"
/>

View file

@ -3,8 +3,6 @@ import { t } from 'i18next';
import ExportModal from 'components/ExportModal';
import exportService from 'services/exportService';
import { getEndpoint } from 'utils/common/apiUtil';
import { getToken } from 'utils/common/key';
import isElectron from 'is-electron';
import { AppContext } from 'pages/_app';
import EnteSpinner from 'components/EnteSpinner';
@ -13,17 +11,16 @@ import { NoStyleAnchor } from 'components/pages/sharedAlbum/GoToEnte';
import { openLink } from 'utils/common';
import { EnteMenuItem } from 'components/Menu/EnteMenuItem';
import { Typography } from '@mui/material';
import { REDIRECTS, getRedirectURL } from 'constants/redirects';
export default function HelpSection() {
const [exportModalView, setExportModalView] = useState(false);
const { setDialogMessage } = useContext(AppContext);
function openFeedbackURL() {
const feedbackURL: string = `${getEndpoint()}/users/feedback?token=${encodeURIComponent(
getToken()
)}`;
openLink(feedbackURL, true);
async function openRoadmapURL() {
const roadmapRedirectURL = getRedirectURL(REDIRECTS.ROADMAP);
openLink(roadmapRedirectURL, true);
}
function openExportModal() {
@ -37,7 +34,7 @@ export default function HelpSection() {
return (
<>
<EnteMenuItem
onClick={openFeedbackURL}
onClick={openRoadmapURL}
label={t('REQUEST_FEATURE')}
variant="secondary"
/>

View file

@ -12,6 +12,10 @@ const getLocaleDisplayName = (l: Language) => {
return 'English';
case Language.fr:
return 'Français';
case Language.zh:
return '中文';
case Language.nl:
return 'Nederlands';
}
};

View file

@ -51,6 +51,7 @@ function UploadButton({
cursor: !uploadManager.shouldAllowNewUpload() && 'not-allowed',
}}>
<Button
sx={{ whiteSpace: 'nowrap' }}
onClick={onClickHandler}
disabled={!uploadManager.shouldAllowNewUpload()}
className="desktop-button"

View file

@ -28,9 +28,6 @@ export enum COLLECTION_SORT_BY {
UPDATION_TIME_DESCENDING,
}
export const UNCATEGORIZED_COLLECTION_NAME = 'Uncategorized';
export const FAVORITE_COLLECTION_NAME = 'Favorites';
export const COLLECTION_SHARE_DEFAULT_VALID_DURATION =
10 * 24 * 60 * 60 * 1000 * 1000;
export const COLLECTION_SHARE_DEFAULT_DEVICE_LIMIT = 4;

View file

@ -2,4 +2,6 @@
export enum Language {
en = 'en',
fr = 'fr',
zh = 'zh',
nl = 'nl',
}

View file

@ -19,9 +19,7 @@ export const FILE_TYPE_LIB_MISSED_FORMATS: FileTypeInfo[] = [
export const KNOWN_NON_MEDIA_FORMATS = ['xmp', 'html', 'txt'];
export const EXIFLESS_FORMATS = ['image/gif'];
export const EXIF_LIBRARY_UNSUPPORTED_FORMATS = ['image/webp'];
export const EXIFLESS_FORMATS = ['gif', 'bmp'];
// this is the chunk size of the un-encrypted file which is read and encrypted before uploading it as a single part.
export const MULTIPART_PART_SIZE = 20 * 1024 * 1024;

View file

@ -9,7 +9,7 @@ import AppNavbar from 'components/Navbar/app';
import { t } from 'i18next';
import { useRouter } from 'next/router';
import VerticallyCentered from 'components/Container';
import { Overlay } from 'components/Container';
import 'bootstrap/dist/css/bootstrap.min.css';
import 'photoswipe/dist/photoswipe.css';
import 'styles/global.css';
@ -73,10 +73,11 @@ import {
CLIENT_PACKAGE_NAMES,
getAppNameAndTitle,
} from 'constants/apps';
import { REDIRECTS } from 'constants/redirects';
const redirectMap = new Map([
['roadmap', getRoadmapRedirectURL],
['families', getFamilyPortalRedirectURL],
[REDIRECTS.ROADMAP, getRoadmapRedirectURL],
[REDIRECTS.FAMILIES, getFamilyPortalRedirectURL],
]);
export const MessageContainer = styled('div')`
@ -456,15 +457,21 @@ export default function App(props) {
somethingWentWrong,
setDialogBoxAttributesV2,
}}>
{loading || !isI18nReady ? (
<VerticallyCentered>
{(loading || !isI18nReady) && (
<Overlay
sx={(theme) => ({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
zIndex: 2000,
backgroundColor: theme.colors.background.base,
})}>
<EnteSpinner>
<span className="sr-only">Loading...</span>
</EnteSpinner>
</VerticallyCentered>
) : (
<Component setLoading={setLoading} {...pageProps} />
</Overlay>
)}
<Component setLoading={setLoading} {...pageProps} />
</AppContext.Provider>
</ThemeProvider>
</CacheProvider>

View file

@ -97,7 +97,7 @@ export default function ChangePassword() {
callback={onSubmit}
buttonText={t('CHANGE_PASSWORD')}
/>
{getData(LS_KEYS.SHOW_BACK_BUTTON)?.value && (
{(getData(LS_KEYS.SHOW_BACK_BUTTON)?.value ?? true) && (
<FormPaperFooter>
<LinkButton onClick={router.back}>
{t('GO_BACK')}

View file

@ -65,7 +65,6 @@ import {
CollectionType,
DUMMY_UNCATEGORIZED_SECTION,
TRASH_SECTION,
UNCATEGORIZED_COLLECTION_NAME,
} from 'constants/collection';
import { AppContext } from 'pages/_app';
import { CustomError, ServerErrorCodes } from 'utils/error';
@ -307,11 +306,11 @@ export default function Gallery() {
if (activeCollection !== ALL_SECTION) {
collectionURL += '?collection=';
if (activeCollection === ARCHIVE_SECTION) {
collectionURL += t('ARCHIVE');
collectionURL += t('ARCHIVE_SECTION_NAME');
} else if (activeCollection === TRASH_SECTION) {
collectionURL += t('TRASH');
} else if (activeCollection === DUMMY_UNCATEGORIZED_SECTION) {
collectionURL += UNCATEGORIZED_COLLECTION_NAME;
collectionURL += t('UNCATEGORIZED');
} else {
collectionURL += activeCollection;
}

View file

@ -38,8 +38,6 @@ import {
COLLECTION_SORT_ORDER,
ALL_SECTION,
CollectionSummaryType,
UNCATEGORIZED_COLLECTION_NAME,
FAVORITE_COLLECTION_NAME,
DUMMY_UNCATEGORIZED_SECTION,
} from 'constants/collection';
import {
@ -67,6 +65,9 @@ const ENDPOINT = getEndpoint();
const COLLECTION_TABLE = 'collections';
const COLLECTION_UPDATION_TIME = 'collection-updation-time';
const UNCATEGORIZED_COLLECTION_NAME = 'Uncategorized';
const FAVORITE_COLLECTION_NAME = 'Favorites';
export const getCollectionLastSyncTime = async (collection: Collection) =>
(await localForage.getItem<number>(`${collection.id}-time`)) ?? 0;

View file

@ -27,7 +27,7 @@ import downloadManager from './downloadManager';
import { getLocalFiles } from './fileService';
import { EnteFile } from 'types/file';
import { decodeMotionPhoto } from './motionPhotoService';
import { decodeLivePhoto } from './livePhotoService';
import {
generateStreamFromArrayBuffer,
getFileExtension,
@ -539,7 +539,7 @@ class ExportService {
fileStream = updatedFileBlob.stream();
}
if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
await this.exportMotionPhoto(fileStream, file, collectionPath);
await this.exportLivePhoto(fileStream, file, collectionPath);
} else {
await this.saveMediaFile(
collectionPath,
@ -554,26 +554,26 @@ class ExportService {
}
}
private async exportMotionPhoto(
private async exportLivePhoto(
fileStream: ReadableStream<any>,
file: EnteFile,
collectionPath: string
) {
const fileBlob = await new Response(fileStream).blob();
const motionPhoto = await decodeMotionPhoto(file, fileBlob);
const imageStream = generateStreamFromArrayBuffer(motionPhoto.image);
const livePhoto = await decodeLivePhoto(file, fileBlob);
const imageStream = generateStreamFromArrayBuffer(livePhoto.image);
const imageSaveName = getUniqueFileSaveName(
collectionPath,
motionPhoto.imageNameTitle,
livePhoto.imageNameTitle,
file.id
);
await this.saveMediaFile(collectionPath, imageSaveName, imageStream);
await this.saveMetadataFile(collectionPath, imageSaveName, file);
const videoStream = generateStreamFromArrayBuffer(motionPhoto.video);
const videoStream = generateStreamFromArrayBuffer(livePhoto.video);
const videoSaveName = getUniqueFileSaveName(
collectionPath,
motionPhoto.videoNameTitle,
livePhoto.videoNameTitle,
file.id
);
await this.saveMediaFile(collectionPath, videoSaveName, videoStream);

View file

@ -2,45 +2,41 @@ import JSZip from 'jszip';
import { EnteFile } from 'types/file';
import { fileExtensionWithDot, fileNameWithoutExtension } from 'utils/file';
class MotionPhoto {
class LivePhoto {
image: Uint8Array;
video: Uint8Array;
imageNameTitle: string;
videoNameTitle: string;
}
export const decodeMotionPhoto = async (file: EnteFile, zipBlob: Blob) => {
export const decodeLivePhoto = async (file: EnteFile, zipBlob: Blob) => {
const originalName = fileNameWithoutExtension(file.metadata.title);
const zip = await JSZip.loadAsync(zipBlob, { createFolders: true });
const motionPhoto = new MotionPhoto();
const livePhoto = new LivePhoto();
for (const zipFilename in zip.files) {
if (zipFilename.startsWith('image')) {
motionPhoto.imageNameTitle =
livePhoto.imageNameTitle =
originalName + fileExtensionWithDot(zipFilename);
motionPhoto.image = await zip.files[zipFilename].async(
'uint8array'
);
livePhoto.image = await zip.files[zipFilename].async('uint8array');
} else if (zipFilename.startsWith('video')) {
motionPhoto.videoNameTitle =
livePhoto.videoNameTitle =
originalName + fileExtensionWithDot(zipFilename);
motionPhoto.video = await zip.files[zipFilename].async(
'uint8array'
);
livePhoto.video = await zip.files[zipFilename].async('uint8array');
}
}
return motionPhoto;
return livePhoto;
};
export const encodeMotionPhoto = async (motionPhoto: MotionPhoto) => {
export const encodeLivePhoto = async (livePhoto: LivePhoto) => {
const zip = new JSZip();
zip.file(
'image' + fileExtensionWithDot(motionPhoto.imageNameTitle),
motionPhoto.image
'image' + fileExtensionWithDot(livePhoto.imageNameTitle),
livePhoto.image
);
zip.file(
'video' + fileExtensionWithDot(motionPhoto.videoNameTitle),
motionPhoto.video
'video' + fileExtensionWithDot(livePhoto.videoNameTitle),
livePhoto.video
);
return await zip.generateAsync({ type: 'uint8array' });
};

View file

@ -1,8 +1,4 @@
import {
EXIFLESS_FORMATS,
EXIF_LIBRARY_UNSUPPORTED_FORMATS,
NULL_LOCATION,
} from 'constants/upload';
import { EXIFLESS_FORMATS, NULL_LOCATION } from 'constants/upload';
import { Location } from 'types/upload';
import exifr from 'exifr';
import piexif from 'piexifjs';
@ -11,6 +7,8 @@ import { logError } from 'utils/sentry';
import { getUnixTimeInMicroSeconds } from 'utils/time';
import { CustomError } from 'utils/error';
const EXIFR_UNSUPPORTED_FILE_FORMAT_MESSAGE = 'Unknown file format';
type ParsedEXIFData = Record<string, any> &
Partial<{
DateTimeOriginal: Date;
@ -27,8 +25,10 @@ type RawEXIFData = Record<string, any> &
CreateDate: string;
ModifyDate: string;
DateCreated: string;
latitude: number;
longitude: number;
GPSLatitude: number[];
GPSLongitude: number[];
GPSLatitudeRef: string;
GPSLongitudeRef: string;
}>;
export async function getParsedExifData(
@ -37,6 +37,9 @@ export async function getParsedExifData(
tags?: string[]
): Promise<ParsedEXIFData> {
try {
if (EXIFLESS_FORMATS.includes(fileTypeInfo.exactType)) {
return null;
}
const exifData: RawEXIFData = await exifr.parse(receivedFile, {
reviveValues: false,
tiff: true,
@ -46,6 +49,9 @@ export async function getParsedExifData(
jfif: true,
ihdr: true,
});
if (!exifData) {
return null;
}
const filteredExifData = tags
? Object.fromEntries(
Object.entries(exifData).filter(([key]) => tags.includes(key))
@ -53,20 +59,16 @@ export async function getParsedExifData(
: exifData;
return parseExifData(filteredExifData);
} catch (e) {
if (!EXIFLESS_FORMATS.includes(fileTypeInfo.mimeType)) {
if (
EXIF_LIBRARY_UNSUPPORTED_FORMATS.includes(fileTypeInfo.mimeType)
) {
logError(e, 'exif library unsupported format', {
fileType: fileTypeInfo.exactType,
});
} else {
logError(e, 'get parsed exif data failed', {
fileType: fileTypeInfo.exactType,
});
}
if (e.message === EXIFR_UNSUPPORTED_FILE_FORMAT_MESSAGE) {
logError(e, 'exif library unsupported format', {
fileType: fileTypeInfo.exactType,
});
} else {
logError(e, 'get parsed exif data failed', {
fileType: fileTypeInfo.exactType,
});
throw e;
}
throw e;
}
}
@ -89,12 +91,7 @@ function parseExifData(exifData: RawEXIFData): ParsedEXIFData {
if (DateCreated) {
parsedExif.DateCreated = parseEXIFDate(exifData.DateCreated);
}
if (
exifData.GPSLatitude &&
exifData.GPSLongitude &&
exifData.GPSLatitudeRef &&
exifData.GPSLongitudeRef
) {
if (exifData.GPSLatitude && exifData.GPSLongitude) {
const parsedLocation = parseEXIFLocation(
exifData.GPSLatitude,
exifData.GPSLatitudeRef,
@ -107,23 +104,49 @@ function parseExifData(exifData: RawEXIFData): ParsedEXIFData {
return parsedExif;
}
function parseEXIFDate(dataTimeString: string) {
function parseEXIFDate(dateTimeString: string) {
try {
if (typeof dataTimeString !== 'string') {
if (typeof dateTimeString !== 'string' || dateTimeString === '') {
throw Error(CustomError.NOT_A_DATE);
}
const [year, month, day, hour, minute, second] = dataTimeString
// Check and parse date in the format YYYYMMDD
if (dateTimeString.length === 8) {
const year = Number(dateTimeString.slice(0, 4));
const month = Number(dateTimeString.slice(4, 6));
const day = Number(dateTimeString.slice(6, 8));
if (
!Number.isNaN(year) &&
!Number.isNaN(month) &&
!Number.isNaN(day)
) {
const date = new Date(year, month - 1, day);
if (!Number.isNaN(+date)) {
return date;
}
}
}
const [year, month, day, hour, minute, second] = dateTimeString
.match(/\d+/g)
.map((component) => parseInt(component, 10));
.map(Number);
if (Number.isNaN(year) || Number.isNaN(month) || Number.isNaN(day)) {
if (
typeof year === 'undefined' ||
Number.isNaN(year) ||
typeof month === 'undefined' ||
Number.isNaN(month) ||
typeof day === 'undefined' ||
Number.isNaN(day)
) {
throw Error(CustomError.NOT_A_DATE);
}
let date: Date;
if (
typeof hour === 'undefined' ||
Number.isNaN(hour) ||
typeof minute === 'undefined' ||
Number.isNaN(minute) ||
typeof second === 'undefined' ||
Number.isNaN(second)
) {
date = new Date(year, month - 1, day);
@ -136,7 +159,7 @@ function parseEXIFDate(dataTimeString: string) {
return date;
} catch (e) {
logError(e, 'parseEXIFDate failed', {
dataTimeString,
dateTimeString,
});
return null;
}
@ -149,8 +172,13 @@ export function parseEXIFLocation(
gpsLongitudeRef: string
) {
try {
if (!gpsLatitude || !gpsLongitude) {
return NULL_LOCATION;
if (
!Array.isArray(gpsLatitude) ||
!Array.isArray(gpsLongitude) ||
gpsLatitude.length !== 3 ||
gpsLongitude.length !== 3
) {
throw Error(CustomError.NOT_A_LOCATION);
}
const latitude = convertDMSToDD(
gpsLatitude[0],
@ -188,13 +216,16 @@ function convertDMSToDD(
}
export function getEXIFLocation(exifData: ParsedEXIFData): Location {
if (!exifData.latitude || !exifData.longitude) {
if (!exifData || (!exifData.latitude && exifData.latitude !== 0)) {
return NULL_LOCATION;
}
return { latitude: exifData.latitude, longitude: exifData.longitude };
}
export function getEXIFTime(exifData: ParsedEXIFData): number {
if (!exifData) {
return null;
}
const dateTime =
exifData.DateTimeOriginal ??
exifData.DateCreated ??

View file

@ -1,6 +1,6 @@
import { FILE_TYPE } from 'constants/file';
import { LIVE_PHOTO_ASSET_SIZE_LIMIT } from 'constants/upload';
import { encodeMotionPhoto } from 'services/motionPhotoService';
import { encodeLivePhoto } from 'services/livePhotoService';
import { getFileType } from 'services/typeDetectionService';
import {
ElectronFile,
@ -99,7 +99,7 @@ export async function readLivePhoto(
const video = await getUint8ArrayView(livePhotoAssets.video);
return {
filedata: await encodeMotionPhoto({
filedata: await encodeLivePhoto({
image,
video,
imageNameTitle: livePhotoAssets.image.name,

View file

@ -7,11 +7,11 @@ import { SetLoading } from 'types/gallery';
import { getData, LS_KEYS } from '../storage/localStorage';
import { logError } from '../sentry';
import { SetDialogBoxAttributes } from 'types/dialogBox';
import { getFamilyPortalRedirectURL } from 'services/userService';
import { openLink } from 'utils/common';
import { isPartOfFamily, getTotalFamilyUsage } from 'utils/user/family';
import { UserDetails } from 'types/user';
import { getSubscriptionPurchaseSuccessMessage } from 'utils/ui';
import { getRedirectURL, REDIRECTS } from 'constants/redirects';
const PAYMENT_PROVIDER_STRIPE = 'stripe';
const PAYMENT_PROVIDER_APPSTORE = 'appstore';
@ -234,8 +234,8 @@ export async function manageFamilyMethod(
) {
try {
setLoading(true);
const url = await getFamilyPortalRedirectURL();
openLink(url, true);
const familyPortalRedirectURL = getRedirectURL(REDIRECTS.FAMILIES);
openLink(familyPortalRedirectURL, true);
} catch (error) {
logError(error, 'failed to redirect to family portal');
setDialogMessage({

View file

@ -40,6 +40,7 @@ export const CustomError = {
NO_METADATA: 'no metadata',
TOO_LARGE_LIVE_PHOTO_ASSETS: 'too large live photo assets',
NOT_A_DATE: 'not a date',
NOT_A_LOCATION: 'not a location',
FILE_ID_NOT_FOUND: 'file with id not found',
WEAK_DEVICE: 'password decryption failed on the device',
INCORRECT_PASSWORD: 'incorrect password',

View file

@ -1,6 +1,6 @@
import { SelectedState } from 'types/gallery';
import { EnteFile, EncryptedEnteFile } from 'types/file';
import { decodeMotionPhoto } from 'services/motionPhotoService';
import { decodeLivePhoto } from 'services/livePhotoService';
import { getFileType } from 'services/typeDetectionService';
import DownloadManager from 'services/downloadManager';
import { logError } from 'utils/sentry';
@ -82,25 +82,19 @@ export async function downloadFile(
}
if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
const motionPhoto = await decodeMotionPhoto(file, fileBlob);
const image = new File(
[motionPhoto.image],
motionPhoto.imageNameTitle
);
const livePhoto = await decodeLivePhoto(file, fileBlob);
const image = new File([livePhoto.image], livePhoto.imageNameTitle);
const imageType = await getFileType(image);
const tempImageURL = URL.createObjectURL(
new Blob([motionPhoto.image], { type: imageType.mimeType })
);
const video = new File(
[motionPhoto.video],
motionPhoto.videoNameTitle
new Blob([livePhoto.image], { type: imageType.mimeType })
);
const video = new File([livePhoto.video], livePhoto.videoNameTitle);
const videoType = await getFileType(video);
const tempVideoURL = URL.createObjectURL(
new Blob([motionPhoto.video], { type: videoType.mimeType })
new Blob([livePhoto.video], { type: videoType.mimeType })
);
downloadUsingAnchor(tempImageURL, motionPhoto.imageNameTitle);
downloadUsingAnchor(tempVideoURL, motionPhoto.videoNameTitle);
downloadUsingAnchor(tempImageURL, livePhoto.imageNameTitle);
downloadUsingAnchor(tempVideoURL, livePhoto.videoNameTitle);
} else {
const fileType = await getFileType(
new File([fileBlob], file.metadata.title)
@ -315,11 +309,11 @@ async function getRenderableLivePhoto(
file: EnteFile,
fileBlob: Blob
): Promise<Blob[]> {
const motionPhoto = await decodeMotionPhoto(file, fileBlob);
const imageBlob = new Blob([motionPhoto.image]);
const livePhoto = await decodeLivePhoto(file, fileBlob);
const imageBlob = new Blob([livePhoto.image]);
return await Promise.all([
getRenderableImage(motionPhoto.imageNameTitle, imageBlob),
getPlayableVideo(motionPhoto.videoNameTitle, motionPhoto.video),
getRenderableImage(livePhoto.imageNameTitle, imageBlob),
getPlayableVideo(livePhoto.videoNameTitle, livePhoto.video),
]);
}

View file

@ -12,9 +12,12 @@ export function getBestPossibleUserLocale(): Language {
for (const lc of userLocales) {
if (lc.startsWith('en')) {
return Language.en;
}
if (lc.startsWith('fr')) {
} else if (lc.startsWith('fr')) {
return Language.fr;
} else if (lc.startsWith('zh')) {
return Language.zh;
} else if (lc.startsWith('nl')) {
return Language.nl;
}
}
return Language.en;

View file

@ -38,7 +38,7 @@ import {
} from './faceCrop';
import { CACHES } from 'constants/cache';
import { FILE_TYPE } from 'constants/file';
import { decodeMotionPhoto } from 'services/motionPhotoService';
import { decodeLivePhoto } from 'services/livePhotoService';
import { addLogLine } from 'utils/logging';
import { Remote } from 'comlink';
import { DedicatedCryptoWorker } from 'worker/crypto.worker';
@ -361,10 +361,10 @@ async function getOriginalConvertedFile(
if (file.metadata.fileType === FILE_TYPE.IMAGE) {
return await getRenderableImage(file.metadata.title, fileBlob);
} else {
const motionPhoto = await decodeMotionPhoto(file, fileBlob);
const livePhoto = await decodeLivePhoto(file, fileBlob);
return await getRenderableImage(
motionPhoto.imageNameTitle,
new Blob([motionPhoto.image])
livePhoto.imageNameTitle,
new Blob([livePhoto.image])
);
}
}

View file

@ -0,0 +1,11 @@
export enum REDIRECTS {
ROADMAP = 'roadmap',
FAMILIES = 'families',
}
export const getRedirectURL = (redirect: REDIRECTS) => {
// open current app with query param of redirect = roadmap
const url = new URL(window.location.href);
url.searchParams.set('redirect', redirect);
return url.href;
};