From dbc7861cee2a69ee2742f2fb5ab7dfeeaf0ed8da Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Mon, 20 Jan 2025 00:34:13 +0000 Subject: [PATCH 1/8] New Crowdin translations by GitHub Action --- web/packages/base/locales/nl-NL/translation.json | 6 +++--- web/packages/base/locales/pt-BR/translation.json | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/web/packages/base/locales/nl-NL/translation.json b/web/packages/base/locales/nl-NL/translation.json index 4e5f3d175c..15827a92e5 100644 --- a/web/packages/base/locales/nl-NL/translation.json +++ b/web/packages/base/locales/nl-NL/translation.json @@ -221,9 +221,9 @@ "terms_and_conditions": "Ik ga akkoord met de gebruiksvoorwaarden en privacybeleid", "people": "Personen", "indexing_scheduled": "Indexering is gepland...", - "indexing_photos": "", - "indexing_fetching": "", - "indexing_people": "", + "indexing_photos": "Indexen bijwerken...", + "indexing_fetching": "Indexen synchroniseren...", + "indexing_people": "Mensen synchroniseren...", "syncing_wait": "Synchroniseren...", "people_empty_too_few": "Mensen worden hier getoond wanneer er voldoende foto's van een persoon zijn", "unnamed_person": "Naamloos persoon", diff --git a/web/packages/base/locales/pt-BR/translation.json b/web/packages/base/locales/pt-BR/translation.json index 3b17e2b4d0..02f0b68c99 100644 --- a/web/packages/base/locales/pt-BR/translation.json +++ b/web/packages/base/locales/pt-BR/translation.json @@ -307,13 +307,13 @@ "INPROGRESS_UPLOADS": "Envios em andamento", "TOO_LARGE_UPLOADS": "Arquivos grandes", "LARGER_THAN_AVAILABLE_STORAGE_UPLOADS": "Armazenamento insuficiente", - "LARGER_THAN_AVAILABLE_STORAGE_INFO": "Estes arquivos não foram carregados pois excedem o tamanho máximo para seu plano de armazenamento", - "TOO_LARGE_INFO": "Estes arquivos não foram carregados pois excedem nosso limite máximo de tamanho de arquivo", - "THUMBNAIL_GENERATION_FAILED_INFO": "Estes arquivos foram enviados, mas infelizmente não conseguimos gerar as miniaturas para eles.", - "upload_to_album": "Enviar para o álbum", + "LARGER_THAN_AVAILABLE_STORAGE_INFO": "Os arquivos não foram enviados, já que, eles ultrapassam o limite máximo do seu plano de armazenamento.", + "TOO_LARGE_INFO": "Os arquivos não foram enviados, já que, eles excedem o limite máximo do arquivo.", + "THUMBNAIL_GENERATION_FAILED_INFO": "Os arquivos foram enviados, porém, não conseguimos gerar miniaturas. (thumbnails)", + "upload_to_album": "Enviar ao álbum", "add_to_album": "Adicionar ao álbum", - "move_to_album": "Mover para álbum", - "unhide_to_album": "Reexibir para o álbum", + "move_to_album": "Mover ao álbum", + "unhide_to_album": "Desocultar do álbum", "restore_to_album": "Restaurar para álbum", "section_all": "Todos", "section_uncategorized": "Sem categoria", @@ -566,7 +566,7 @@ "unpreviewable_file_notification": "Este arquivo não pôde ser pré-visualizado. Clique aqui para baixar o original.", "download_complete": "Download concluído", "downloading_album": "Fazendo download de {{name}}", - "download_failed": "Falha no download", + "download_failed": "Falhou ao baixar", "download_progress": "{{count, number}} / {{total, number}} arquivos", "CHRISTMAS": "Natal", "CHRISTMAS_EVE": "Véspera de Natal", From db7f38a92674a9f95e69860b70a827376bc49d77 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:06:41 +0530 Subject: [PATCH 2/8] [auth] New translations (#4782) New translations from [Crowdin](https://crowdin.com/project/ente-authenticator-app) Co-authored-by: Crowdin Bot --- auth/lib/l10n/arb/app_it.arb | 2 ++ auth/lib/l10n/arb/app_nl.arb | 2 ++ auth/lib/l10n/arb/app_sv.arb | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/auth/lib/l10n/arb/app_it.arb b/auth/lib/l10n/arb/app_it.arb index 7f133b4d3a..17281db08a 100644 --- a/auth/lib/l10n/arb/app_it.arb +++ b/auth/lib/l10n/arb/app_it.arb @@ -88,6 +88,8 @@ "useRecoveryKey": "Utilizza un codice di recupero", "incorrectPasswordTitle": "Password sbagliata", "welcomeBack": "Bentornato!", + "emailAlreadyRegistered": "Email già registrata.", + "emailNotRegistered": "Email non registrata.", "madeWithLoveAtPrefix": "realizzato con ❤️ a ", "supportDevs": "Iscriviti a ente per supportare questo progetto.", "supportDiscount": "Utilizzare il codice coupon \"AUTH\" per ottenere il 10% di sconto al primo anno", diff --git a/auth/lib/l10n/arb/app_nl.arb b/auth/lib/l10n/arb/app_nl.arb index 765ad12168..9c382d7cea 100644 --- a/auth/lib/l10n/arb/app_nl.arb +++ b/auth/lib/l10n/arb/app_nl.arb @@ -88,6 +88,8 @@ "useRecoveryKey": "Herstelsleutel gebruiken", "incorrectPasswordTitle": "Onjuist wachtwoord", "welcomeBack": "Welkom terug!", + "emailAlreadyRegistered": "E-mail is al geregistreerd.", + "emailNotRegistered": "E-mail niet geregistreerd.", "madeWithLoveAtPrefix": "met ❤️ gemaakt door", "supportDevs": "Abonneer u op ente om ons te steunen", "supportDiscount": "Gebruik couponcode \"AUTH\" om het eerste jaar 10% korting te krijgen", diff --git a/auth/lib/l10n/arb/app_sv.arb b/auth/lib/l10n/arb/app_sv.arb index 776c61d41a..a096aa931e 100644 --- a/auth/lib/l10n/arb/app_sv.arb +++ b/auth/lib/l10n/arb/app_sv.arb @@ -88,6 +88,8 @@ "useRecoveryKey": "Använd återställningsnyckel", "incorrectPasswordTitle": "Felaktigt lösenord", "welcomeBack": "Välkommen tillbaka!", + "emailAlreadyRegistered": "E-postadress redan registrerad.", + "emailNotRegistered": "E-postadress ej registrerad.", "madeWithLoveAtPrefix": "gjord med ❤️ av ", "supportDevs": "Prenumerera på ente för att stödja oss", "supportDiscount": "Använd kupongkoden \"AUTH\" för att få 10% rabatt första året", @@ -321,7 +323,11 @@ "terminate": "Avsluta", "thisDevice": "Den här enheten", "thisEmailIsAlreadyInUse": "Denna e-postadress används redan", + "verificationFailedPleaseTryAgain": "Verifiering misslyckades, vänligen försök igen", + "yourVerificationCodeHasExpired": "Din verifieringskod har upphört att gälla", "incorrectCode": "Felaktig kod", + "sorryTheCodeYouveEnteredIsIncorrect": "Tyvärr, den kod som du har angett är felaktig", + "authenticationFailedPleaseTryAgain": "Autentisering misslyckades, vänligen försök igen", "authenticationSuccessful": "Autentisering lyckades!", "twofactorAuthenticationSuccessfullyReset": "Tvåfaktorsautentisering återställd", "incorrectRecoveryKey": "Felaktig återställningsnyckel", From 714a37d461914da6e96e77f61fb1608aea17bc65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Beli=C3=ABn?= Date: Mon, 20 Jan 2025 06:37:56 +0100 Subject: [PATCH 3/8] [auth] Update icons (#4777) ## Description - Remove "forced" color for Fastmail logo (colors are already defined in SVG file) - Add [CSAM ](https://www.csam.be/en/index.html) logo - Add [reMarkable](https://remarkable.com/) logo I was planning to add LinkedIn logo but noticed #4742 so I've removed it. ## Tests --- auth/assets/custom-icons/_data/custom-icons.json | 9 +++++++-- auth/assets/custom-icons/icons/csam.svg | 4 ++++ auth/assets/custom-icons/icons/remarkable.svg | 3 +++ 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 auth/assets/custom-icons/icons/csam.svg create mode 100644 auth/assets/custom-icons/icons/remarkable.svg diff --git a/auth/assets/custom-icons/_data/custom-icons.json b/auth/assets/custom-icons/_data/custom-icons.json index f63f8418cd..5d0d6e1dba 100644 --- a/auth/assets/custom-icons/_data/custom-icons.json +++ b/auth/assets/custom-icons/_data/custom-icons.json @@ -257,6 +257,9 @@ "Crypto com" ] }, + { + "title": "CSAM" + }, { "title": "CSFloat" }, @@ -342,8 +345,7 @@ "title": "Estateguru" }, { - "title": "Fastmail", - "hex": "0067B9" + "title": "Fastmail" }, { "title": "Fidelity", @@ -834,6 +836,9 @@ "Registro.br" ] }, + { + "title": "reMarkable" + }, { "title": "Restorecord" }, diff --git a/auth/assets/custom-icons/icons/csam.svg b/auth/assets/custom-icons/icons/csam.svg new file mode 100644 index 0000000000..9d4d51f25f --- /dev/null +++ b/auth/assets/custom-icons/icons/csam.svg @@ -0,0 +1,4 @@ + + + + diff --git a/auth/assets/custom-icons/icons/remarkable.svg b/auth/assets/custom-icons/icons/remarkable.svg new file mode 100644 index 0000000000..abbfb750cc --- /dev/null +++ b/auth/assets/custom-icons/icons/remarkable.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file From 7e320693a160110701ac0697340c17656b52ff39 Mon Sep 17 00:00:00 2001 From: Neeraj Date: Mon, 20 Jan 2025 11:09:31 +0530 Subject: [PATCH 4/8] [server] Remove unused endpoint (#4772) ## Description No logs for request in last 30 days and client code also don't refer to this endpoint. ## Tests --- server/cmd/museum/main.go | 1 - server/pkg/api/user.go | 17 ----------------- server/pkg/controller/user/user.go | 25 ------------------------- server/pkg/repo/user.go | 7 ------- 4 files changed, 50 deletions(-) diff --git a/server/cmd/museum/main.go b/server/cmd/museum/main.go index bb99f90c73..e1a3622011 100644 --- a/server/cmd/museum/main.go +++ b/server/cmd/museum/main.go @@ -496,7 +496,6 @@ func main() { privateAPI.POST("/users/two-factor/disable", userHandler.DisableTwoFactor) privateAPI.PUT("/users/attributes", userHandler.SetAttributes) privateAPI.PUT("/users/email-mfa", userHandler.UpdateEmailMFA) - privateAPI.PUT("/users/keys", userHandler.UpdateKeys) privateAPI.POST("/users/srp/setup", userHandler.SetupSRP) privateAPI.POST("/users/srp/complete", userHandler.CompleteSRPSetup) privateAPI.POST("/users/srp/update", userHandler.UpdateSrpAndKeyAttributes) diff --git a/server/pkg/api/user.go b/server/pkg/api/user.go index 5cdbd0ec9a..bfdd36ab7b 100644 --- a/server/pkg/api/user.go +++ b/server/pkg/api/user.go @@ -105,23 +105,6 @@ func (h *UserHandler) UpdateEmailMFA(c *gin.Context) { c.Status(http.StatusOK) } -// UpdateKeys updates the user key attributes on password change -func (h *UserHandler) UpdateKeys(c *gin.Context) { - userID := auth.GetUserID(c.Request.Header) - var request ente.UpdateKeysRequest - if err := c.ShouldBindJSON(&request); err != nil { - handler.Error(c, stacktrace.Propagate(err, "")) - return - } - token := auth.GetToken(c) - err := h.UserController.UpdateKeys(c, userID, request, token) - if err != nil { - handler.Error(c, stacktrace.Propagate(err, "")) - return - } - c.Status(http.StatusOK) -} - // SetRecoveryKey sets the recovery key attributes for a user. func (h *UserHandler) SetRecoveryKey(c *gin.Context) { userID := auth.GetUserID(c.Request.Header) diff --git a/server/pkg/controller/user/user.go b/server/pkg/controller/user/user.go index 3c1b0419f0..7f5d5d0374 100644 --- a/server/pkg/controller/user/user.go +++ b/server/pkg/controller/user/user.go @@ -184,31 +184,6 @@ func (c *UserController) UpdateEmailMFA(context *gin.Context, userID int64, isEn return c.UserAuthRepo.UpdateEmailMFA(context, userID, isEnabled) } -// UpdateKeys updates the user keys on password change -func (c *UserController) UpdateKeys(context *gin.Context, userID int64, - request ente.UpdateKeysRequest, token string) error { - /* - todo: send email to the user on password change and may be keep history of old keys for X days. - History will allow easy recovery of the account when password is changed by a bad actor - */ - isSRPSetupDone, err := c.UserAuthRepo.IsSRPSetupDone(context, userID) - if err != nil { - return err - } - if isSRPSetupDone { - return stacktrace.Propagate(ente.NewBadRequestWithMessage("Need to upgrade client"), "can not use old API to change password after SRP is setup") - } - err = c.UserRepo.UpdateKeys(userID, request) - if err != nil { - return stacktrace.Propagate(err, "") - } - err = c.UserAuthRepo.RemoveAllOtherTokens(userID, token) - if err != nil { - return stacktrace.Propagate(err, "") - } - return nil -} - // SetRecoveryKey sets the recovery key attributes for a user, if not already set func (c *UserController) SetRecoveryKey(userID int64, request ente.SetRecoveryKeyRequest) error { keyAttr, keyErr := c.UserRepo.GetKeyAttributes(userID) diff --git a/server/pkg/repo/user.go b/server/pkg/repo/user.go index b482a16a87..aa7d6145d5 100644 --- a/server/pkg/repo/user.go +++ b/server/pkg/repo/user.go @@ -265,13 +265,6 @@ func (repo *UserRepository) SetKeyAttributes(userID int64, keyAttributes ente.Ke return stacktrace.Propagate(err, "") } -// UpdateKeys sets the keys of a user -func (repo *UserRepository) UpdateKeys(userID int64, keys ente.UpdateKeysRequest) error { - _, err := repo.DB.Exec(`UPDATE key_attributes SET kek_salt = $1, encrypted_key = $2, key_decryption_nonce = $3, mem_limit = $4, ops_limit = $5 WHERE user_id = $6`, - keys.KEKSalt, keys.EncryptedKey, keys.KeyDecryptionNonce, keys.MemLimit, keys.OpsLimit, userID) - return stacktrace.Propagate(err, "") -} - // SetRecoveryKeyAttributes sets the recovery key and related attributes for a user func (repo *UserRepository) SetRecoveryKeyAttributes(userID int64, keys ente.SetRecoveryKeyRequest) error { _, err := repo.DB.Exec(`UPDATE key_attributes SET master_key_encrypted_with_recovery_key = $1, master_key_decryption_nonce = $2, recovery_key_encrypted_with_master_key = $3, recovery_key_decryption_nonce = $4 WHERE user_id = $5`, From 77db220327979f3c9cb36e12246539a447a15417 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:09:50 +0530 Subject: [PATCH 5/8] [mobile] New translations (#4781) New translations from [Crowdin](https://crowdin.com/project/ente-photos-app) Co-authored-by: Crowdin Bot --- .../metadata/android/ro/full_description.txt | 36 ++ .../metadata/android/ro/short_description.txt | 1 + mobile/fastlane/metadata/android/ro/title.txt | 1 + .../fastlane/metadata/ios/ro/description.txt | 33 ++ mobile/fastlane/metadata/ios/ro/keywords.txt | 1 + mobile/fastlane/metadata/ios/ro/name.txt | 1 + mobile/fastlane/metadata/ios/ro/subtitle.txt | 1 + mobile/lib/l10n/intl_nl.arb | 13 +- mobile/lib/l10n/intl_ro.arb | 366 +++++++++++++++++- 9 files changed, 451 insertions(+), 2 deletions(-) create mode 100644 mobile/fastlane/metadata/android/ro/full_description.txt create mode 100644 mobile/fastlane/metadata/android/ro/short_description.txt create mode 100644 mobile/fastlane/metadata/android/ro/title.txt create mode 100644 mobile/fastlane/metadata/ios/ro/description.txt create mode 100644 mobile/fastlane/metadata/ios/ro/keywords.txt create mode 100644 mobile/fastlane/metadata/ios/ro/name.txt create mode 100644 mobile/fastlane/metadata/ios/ro/subtitle.txt diff --git a/mobile/fastlane/metadata/android/ro/full_description.txt b/mobile/fastlane/metadata/android/ro/full_description.txt new file mode 100644 index 0000000000..764227d7a2 --- /dev/null +++ b/mobile/fastlane/metadata/android/ro/full_description.txt @@ -0,0 +1,36 @@ +ente este o aplicație simplă pentru a face copii de rezervă și a partaja fotografiile și videoclipurile. + +Dacă ați căutat o alternativă la Google Photos care să respecte confidențialitatea, ați ajuns la locul potrivit. Cu ente, acestea sunt stocate criptat integral (e2ee). Asta înseamnă că numai dvs. le puteți vedea. + +Avem aplicații open-source pentru Android, iOS, web și desktop, iar fotografiile dvs. se vor sincroniza perfect între toate acestea într-o manieră criptată integral (e2ee). + +De asemenea, ente simplifică partajarea albumelor dvs. cu cei dragi, chiar dacă aceștia nu folosesc ente. Puteți partaja linkuri vizibile public, unde aceștia pot vizualiza albumul dvs. și pot colabora prin adăugarea de fotografii la acesta, chiar și fără un cont sau o aplicație. + +Datele dvs. criptate sunt copiate în 3 locații diferite, inclusiv într-un adăpost antiatomic din Paris. Luăm posteritatea în serios și vă asigurăm că amintirile dvs. vor ajunge la următoarea generație. + +Suntem aici pentru a crea cea mai sigură aplicație de fotografii, veniți alături de noi! + +CARACTERISTICI +- Copii de rezervă în calitate originală, deoarece fiecare pixel contează +- Planuri de familie, astfel încât să puteți distribui spațiul cu familia dvs. +- Albume colaborative, astfel încât să puteți pune în comun fotografii după o excursie +- Dosare distribuite, în cazul în care doriți ca partenerul dvs. să se bucure și de fotografiile dvs. +- Linkuri pentru albume care pot fi protejate cu o parolă +- Abilitatea de a elibera spațiu, prin eliminarea fișierelor care au fost salvate în siguranță +- Asistență umană, pentru că meritați +- Descrieri, astfel încât să vă puteți subtitra amintirile și să le găsiți cu ușurință +- Editor de imagini, pentru a adăuga ultimele retușuri +- Evidențiați, ascundeți și retrăiți-vă amintirile, căci sunt prețioase +- Import cu o singură apăsare din Google, Apple, hard disk și multe altele +- Temă întunecată, deoarece fotografiile dvs. arată bine și cu ea +- 2FA, 3FA, autentificare biometrică +- și MULTE altele! + +PERMISIUNI +ente solicită anumite permisiuni pentru a servi scopului unui furnizor de stocare a fotografiilor, care poate fi revizuit aici: https://github.com/ente-io/ente/blob/f-droid/mobile/android/permissions.md + +PREȚURI +Nu oferim planuri gratuite pentru totdeauna, deoarece este important pentru noi să rămânem sustenabili și să trecem testul timpului. În schimb, oferim planuri accesibile pe care le puteți distribui liber familiei dvs. Puteți afla mai multe la ente.io. + +ASISTENȚĂ +Suntem mândri să oferim asistență umană. Dacă sunteți clientul nostru plătit, vă puteți adresa la team@ente.io și așteptați un răspuns din partea echipei noastre în termen de 24 de ore. diff --git a/mobile/fastlane/metadata/android/ro/short_description.txt b/mobile/fastlane/metadata/android/ro/short_description.txt new file mode 100644 index 0000000000..3827bb5098 --- /dev/null +++ b/mobile/fastlane/metadata/android/ro/short_description.txt @@ -0,0 +1 @@ +ente este o aplicație de stocare foto criptată integral \ No newline at end of file diff --git a/mobile/fastlane/metadata/android/ro/title.txt b/mobile/fastlane/metadata/android/ro/title.txt new file mode 100644 index 0000000000..f2fe25b264 --- /dev/null +++ b/mobile/fastlane/metadata/android/ro/title.txt @@ -0,0 +1 @@ +ente - stocare foto criptată \ No newline at end of file diff --git a/mobile/fastlane/metadata/ios/ro/description.txt b/mobile/fastlane/metadata/ios/ro/description.txt new file mode 100644 index 0000000000..968b73d0f0 --- /dev/null +++ b/mobile/fastlane/metadata/ios/ro/description.txt @@ -0,0 +1,33 @@ +Ente este o aplicație simplă pentru a face automat copii de rezervă și a vă organiza fotografiile și videoclipurile. + +Dacă erați în căutarea unei alternative care să respecte confidențialitatea pentru a vă păstra amintirile, ați ajuns la locul potrivit. Cu Ente, acestea sunt stocate criptate integral (e2ee). Asta înseamnă că numai dvs. le puteți vedea. + +Avem aplicații pentru toate platformele, iar fotografiile dvs. se vor sincroniza perfect între toate dispozitivele dvs. într-un mod criptat integral (e2ee). + +De asemenea, Ente simplifică distribuirea albumelor dvs. cu cei dragi. Puteți fie să le distribuiți direct altor utilizatori Ente, criptate integral, fie cu linkuri care pot fi vizualizate public. + +Datele dvs. criptate sunt stocate în mai multe locații, inclusiv într-un adăpost antiatomic din Paris. Luăm posteritatea în serios și vă asigurăm că amintirile dvs. vor ajunge la următoarea generație. + +Suntem aici pentru a crea cea mai sigură aplicație de fotografii, veniți alături de noi! + +CARACTERISTICI +- Copii de rezervă în calitate originală, deoarece fiecare pixel contează +- Planuri de familie, astfel încât să puteți distribui spațiul cu familia dvs. +- Dosare distribuite, în cazul în care doriți ca partenerul dvs. să se bucure și de fotografiile dvs. +- Linkuri pentru albume, care pot fi protejate cu o parolă și setate să expire +- Abilitatea de a elibera spațiu, prin eliminarea fișierelor care au fost salvate în siguranță +- Editor de imagini, pentru a adăuga ultimele retușuri +- Evidențiați, ascundeți și retrăiți-vă amintirile, căci sunt prețioase +- Import cu un singur clic de la toți furnizorii majori de stocare +- Temă întunecată, deoarece fotografiile dvs. arată bine și cu ea +- 2FA, 3FA, autentificare biometrică +- și MULTE altele! + +PREȚURI +Nu oferim planuri gratuite pentru totdeauna, deoarece este important pentru noi să rămânem sustenabili și să trecem testul timpului. În schimb, oferim planuri accesibile pe care le puteți distribui liber familiei dvs. Puteți afla mai multe la ente.io. + +ASISTENȚĂ +Suntem mândri să oferim asistență umană. Dacă sunteți clientul nostru plătit, vă puteți adresa la team@ente.io și așteptați un răspuns din partea echipei noastre în termen de 24 de ore. + +TERMENI +https://ente.io/terms diff --git a/mobile/fastlane/metadata/ios/ro/keywords.txt b/mobile/fastlane/metadata/ios/ro/keywords.txt new file mode 100644 index 0000000000..cbfa573772 --- /dev/null +++ b/mobile/fastlane/metadata/ios/ro/keywords.txt @@ -0,0 +1 @@ +fotografii,fotografie,familie,intimitate,cloud,backup,video,foto,criptare,stocare,album,alternative diff --git a/mobile/fastlane/metadata/ios/ro/name.txt b/mobile/fastlane/metadata/ios/ro/name.txt new file mode 100644 index 0000000000..4a5ff5dc4d --- /dev/null +++ b/mobile/fastlane/metadata/ios/ro/name.txt @@ -0,0 +1 @@ +Ente Foto diff --git a/mobile/fastlane/metadata/ios/ro/subtitle.txt b/mobile/fastlane/metadata/ios/ro/subtitle.txt new file mode 100644 index 0000000000..5deb38444e --- /dev/null +++ b/mobile/fastlane/metadata/ios/ro/subtitle.txt @@ -0,0 +1 @@ +Stocare foto criptată diff --git a/mobile/lib/l10n/intl_nl.arb b/mobile/lib/l10n/intl_nl.arb index 0d8fe12da3..36da3abf1b 100644 --- a/mobile/lib/l10n/intl_nl.arb +++ b/mobile/lib/l10n/intl_nl.arb @@ -2,6 +2,8 @@ "@@locale ": "en", "enterYourEmailAddress": "Voer uw e-mailadres in", "accountWelcomeBack": "Welkom terug!", + "emailAlreadyRegistered": "E-mail is al geregistreerd.", + "emailNotRegistered": "E-mail niet geregistreerd.", "email": "E-mail", "cancel": "Annuleer", "verify": "Verifiëren", @@ -185,6 +187,8 @@ }, "allowAddPhotosDescription": "Sta toe dat mensen met de link ook foto's kunnen toevoegen aan het gedeelde album.", "passwordLock": "Wachtwoord slot", + "canNotOpenTitle": "Kan dit album niet openen", + "canNotOpenBody": "Sorry, dit album kan niet worden geopend in de app.", "disableDownloadWarningTitle": "Let op", "disableDownloadWarningBody": "Kijkers kunnen nog steeds screenshots maken of een kopie van je foto's opslaan met behulp van externe tools", "allowDownloads": "Downloads toestaan", @@ -1378,6 +1382,7 @@ "extraPhotosFound": "Extra foto's gevonden", "configuration": "Configuratie", "localIndexing": "Lokaal indexeren", + "processed": "Verwerkt", "resetPerson": "Verwijderen", "areYouSureYouWantToResetThisPerson": "Weet u zeker dat u deze persoon wilt resetten?", "allPersonGroupingWillReset": "Alle groepen voor deze persoon worden gereset, en je verliest alle suggesties die voor deze persoon zijn gedaan", @@ -1578,5 +1583,11 @@ }, "legacyInvite": "{email} heeft je uitgenodigd om een vertrouwd contact te zijn", "authToManageLegacy": "Verifieer om je vertrouwde contacten te beheren", - "useDifferentPlayerInfo": "Problemen met het afspelen van deze video? Hier ingedrukt houden om een andere speler te proberen." + "useDifferentPlayerInfo": "Problemen met het afspelen van deze video? Hier ingedrukt houden om een andere speler te proberen.", + "hideSharedItemsFromHomeGallery": "Verberg gedeelde bestanden uit de galerij", + "gallery": "Galerij", + "joinAlbum": "Deelnemen aan album", + "joinAlbumSubtext": "om je foto's te bekijken en toe te voegen", + "joinAlbumSubtextViewer": "om dit aan gedeelde albums toe te voegen", + "join": "Deelnemen" } \ No newline at end of file diff --git a/mobile/lib/l10n/intl_ro.arb b/mobile/lib/l10n/intl_ro.arb index edca5b42e5..aea8bb673e 100644 --- a/mobile/lib/l10n/intl_ro.arb +++ b/mobile/lib/l10n/intl_ro.arb @@ -2,6 +2,8 @@ "@@locale ": "en", "enterYourEmailAddress": "Introduceți adresa de e-mail", "accountWelcomeBack": "Bine ați revenit!", + "emailAlreadyRegistered": "E-mail deja înregistrat.", + "emailNotRegistered": "E-mailul nu este înregistrat.", "email": "E-mail", "cancel": "Anulare", "verify": "Verificare", @@ -185,6 +187,8 @@ }, "allowAddPhotosDescription": "Permiteți persoanelor care au linkul să adauge și fotografii la albumul distribuit.", "passwordLock": "Blocare cu parolă", + "canNotOpenTitle": "Nu se poate deschide acest album", + "canNotOpenBody": "Ne pare rău, acest album nu poate fi deschis în aplicație.", "disableDownloadWarningTitle": "Rețineți", "disableDownloadWarningBody": "Observatorii pot să facă capturi de ecran sau să salveze o copie a fotografiilor dvs. folosind instrumente externe", "allowDownloads": "Permiteți descărcările", @@ -351,6 +355,7 @@ "failedToLoadAlbums": "Încărcarea albumelor nu a reușit", "hidden": "Ascunse", "authToViewYourHiddenFiles": "Vă rugăm să vă autentificați pentru a vedea fișierele ascunse", + "authToViewTrashedFiles": "Vă rugăm să vă autentificați pentru a vedea fișierele din coșul de gunoi", "trash": "Coș de gunoi", "uncategorized": "Necategorisite", "videoSmallCase": "videoclip", @@ -412,6 +417,7 @@ "description": "The text to display in the advanced settings section" }, "photoGridSize": "Dimensiunea grilei foto", + "manageDeviceStorage": "Gestionați memoria cache a dispozitivului", "manageDeviceStorageDesc": "Revizuiți și ștergeți spațiul din memoria cache locală.", "machineLearning": "Învățare automată", "mlConsent": "Activați învățarea automată", @@ -688,6 +694,8 @@ "noPhotosAreBeingBackedUpRightNow": "Nicio fotografie nu este salvată în acest moment", "preserveMore": "Păstrați mai multe", "grantFullAccessPrompt": "Vă rugăm să permiteți accesul la toate fotografiile în aplicația Setări", + "allowPermTitle": "Permiteți accesul la fotografii", + "allowPermBody": "Vă rugăm să permiteți accesul la fotografiile dvs. din Setări, astfel încât Ente să vă poată afișa și salva biblioteca.", "openSettings": "Deschideți Setări", "selectMorePhotos": "Selectați mai multe fotografii", "existingUser": "Utilizator existent", @@ -1090,6 +1098,83 @@ "description": "Label of confirm button to add a new custom radius to the radius selector of a location tag" }, "setRadius": "Setare rază", + "familyPlanPortalTitle": "Familie", + "familyPlanOverview": "Adăugați 5 membri ai familiei la planul dvs. existent fără a plăti suplimentar.\n\nFiecare membru primește propriul spațiu privat și nu poate vedea fișierele celuilalt decât dacă acestea sunt partajate.\n\nPlanurile de familie sunt disponibile pentru clienții care au un abonament Ente plătit.\n\nAbonați-vă acum pentru a începe!", + "androidBiometricHint": "Verificați-vă identitatea", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Neidentificat. Încercați din nou.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Succes", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Anulare", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Autentificare necesară", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometrice necesare", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Sunt necesare acreditările dispozitivului", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Sunt necesare acreditările dispozitivului", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Mergeți la setări", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Autentificarea biometrică nu este configurată pe dispozitivul dvs. Mergeți la „Setări > Securitate” pentru a adăuga autentificarea biometrică.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Autentificarea biometrică este dezactivată. Vă rugăm să blocați și să deblocați ecranul pentru a o activa.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSGoToSettingsDescription": "Autentificarea biometrică nu este configurată pe dispozitivul dvs. Vă rugăm să activați Touch ID sau Face ID pe telefonul dvs.", + "@iOSGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure Biometrics for their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "openstreetmapContributors": "Contribuitori OpenStreetMap", + "hostedAtOsmFrance": "Găzduit la OSM Franţa", + "map": "Hartă", + "@map": { + "description": "Label for the map view" + }, + "maps": "Hărţi", + "enableMaps": "Activare hărți", + "enableMapsDesc": "Se va afișa fotografiile dvs. pe o hartă a lumii.\n\nAceastă hartă este găzduită de Open Street Map, iar locațiile exacte ale fotografiilor dvs. nu sunt niciodată partajate.\n\nPuteți dezactiva această funcție oricând din Setări.", + "quickLinks": "Link-uri rapide", + "selectItemsToAdd": "Selectați elementele de adăugat", + "addSelected": "Adăugați selectate", + "addFromDevice": "Adăugați de pe dispozitiv", + "addPhotos": "Adăugați fotografii", + "noPhotosFoundHere": "Nu s-au găsit fotografii aici", + "zoomOutToSeePhotos": "Micșorați pentru a vedea fotografiile", + "noImagesWithLocation": "Nicio imagine cu locație", + "unpinAlbum": "Anulați fixarea albumului", + "pinAlbum": "Fixați albumul", + "create": "Creare", + "viewAll": "Vizualizați tot", + "nothingSharedWithYouYet": "Nimic distribuit cu dvs. încă", + "noAlbumsSharedByYouYet": "Niciun album nu a fost distribuit de dvs. încă", "sharedWithYou": "Distribuite cu dvs.", "sharedByYou": "Distribuite de dvs.", "inviteYourFriendsToEnte": "Invitați-vă prietenii la Ente", @@ -1152,6 +1237,21 @@ "changeLocationOfSelectedItems": "Schimbați locația articolelor selectate?", "editsToLocationWillOnlyBeSeenWithinEnte": "Editările locației vor fi vizibile doar pe Ente", "cleanUncategorized": "Curățare Necategorisite", + "cleanUncategorizedDescription": "Eliminați toate fișierele din „Fără categorie” care sunt prezente în alte albume", + "waitingForVerification": "Se așteaptă verificarea...", + "passkey": "Cheie de acces", + "passkeyAuthTitle": "Verificare cheie de acces", + "loginWithTOTP": "Autentificare cu parolă unică (TOTP)", + "passKeyPendingVerification": "Verificarea este încă în așteptare", + "loginSessionExpired": "Sesiune expirată", + "loginSessionExpiredDetails": "Sesiunea a expirat. Vă rugăm să vă autentificați din nou.", + "verifyPasskey": "Verificați cheia de acces", + "playOnTv": "Redare album pe TV", + "pair": "Asociere", + "deviceNotFound": "Dispozitivul nu a fost găsit", + "castInstruction": "Accesați cast.ente.io de pe dispozitivul pe care doriți să îl asociați.\n\nIntroduceți codul de mai jos pentru a reda albumul pe TV.", + "deviceCodeHint": "Introduceți codul", + "joinDiscord": "Alăturați-vă pe Discord", "locations": "Locații", "addAName": "Adăugați un nume", "findThemQuickly": "Găsiți rapid", @@ -1225,5 +1325,269 @@ "panorama": "Panoramă", "reenterPassword": "Reintroduceți parola", "reenterPin": "Reintroduceți codul PIN", - "deviceLock": "Blocare dispozitiv" + "deviceLock": "Blocare dispozitiv", + "pinLock": "Blocare PIN", + "next": "Înainte", + "setNewPassword": "Setați parola noua", + "enterPin": "Introduceţi codul PIN", + "setNewPin": "Setați un cod nou PIN", + "appLock": "Blocare aplicație", + "noSystemLockFound": "Nu s-a găsit nicio blocare de sistem", + "tapToUnlock": "Atingeți pentru a debloca", + "tooManyIncorrectAttempts": "Prea multe încercări incorecte", + "videoInfo": "Informaţii video", + "autoLock": "Blocare automată", + "immediately": "Imediat", + "autoLockFeatureDescription": "Timpul după care aplicația se blochează după ce a fost pusă în fundal", + "hideContent": "Ascundeți conținutul", + "hideContentDescriptionAndroid": "Ascunde conținutul aplicației în comutatorul de aplicații și dezactivează capturile de ecran", + "hideContentDescriptionIos": "Ascunde conținutul aplicației în comutatorul de aplicații", + "passwordStrengthInfo": "Puterea parolei este calculată luând în considerare lungimea parolei, caracterele utilizate și dacă parola apare sau nu în top 10.000 cele mai utilizate parole", + "noQuickLinksSelected": "Nu au fost găsite linkuri rapide", + "pleaseSelectQuickLinksToRemove": "Vă rugăm să selectați linkurile rapide de eliminat", + "removePublicLinks": "Eliminați linkurile publice", + "thisWillRemovePublicLinksOfAllSelectedQuickLinks": "Se vor elimina linkurile publice ale linkurilor rapide selectate.", + "guestView": "Mod oaspete", + "guestViewEnablePreSteps": "Pentru a activa modul oaspete, vă rugăm să configurați codul de acces al dispozitivului sau blocarea ecranului în setările sistemului.", + "nameTheAlbum": "Denumiți albumul", + "collectPhotosDescription": "Creați un link unde prietenii dvs. pot încărca fotografii la calitatea originală.", + "collect": "Colectare", + "appLockDescriptions": "Alegeți între ecranul de blocare implicit al dispozitivului dvs. și un ecran de blocare personalizat cu PIN sau parolă.", + "toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Pentru a activa blocarea aplicației, vă rugăm să configurați codul de acces al dispozitivului sau blocarea ecranului în setările sistemului.", + "authToViewPasskey": "Vă rugăm să vă autentificați pentru a vizualiza cheia de acces", + "loopVideoOn": "Repetare video activată", + "loopVideoOff": "Repetare video dezactivată", + "localSyncErrorMessage": "Se pare că ceva nu a mers bine, deoarece sincronizarea fotografiilor locale durează mai mult decât ne așteptam. Vă rugăm să contactați echipa noastră de asistență", + "showPerson": "Afișare persoană", + "sort": "Sortare", + "mostRecent": "Cele mai recente", + "mostRelevant": "Cele mai relevante", + "loadingYourPhotos": "Se încarcă fotografiile dvs...", + "processingImport": "Se procesează {folderName}...", + "personName": "Numele persoanei", + "addNewPerson": "Adăugare persoană nouă", + "addNameOrMerge": "Adăugare nume sau îmbinare", + "mergeWithExisting": "Îmbinare cu unul existent", + "newPerson": "Persoană nouă", + "addName": "Adăugare nume", + "add": "Adăugare", + "extraPhotosFoundFor": "S-au găsit fotografii extra pentru {text}", + "@extraPhotosFoundFor": { + "placeholders": { + "text": { + "type": "String" + } + } + }, + "extraPhotosFound": "S-au găsit fotografii extra", + "configuration": "Configurare", + "localIndexing": "Indexare locală", + "processed": "Procesate", + "resetPerson": "Eliminare", + "areYouSureYouWantToResetThisPerson": "Sunteți sigur că doriți să resetaţi această persoană?", + "allPersonGroupingWillReset": "Toate grupările pentru această persoană vor fi resetate și veți pierde toate sugestiile făcute pentru această persoană", + "yesResetPerson": "Da, resetează persoana", + "onlyThem": "Numai el/ea", + "checkingModels": "Se verifică modelele...", + "enableMachineLearningBanner": "Activați învățarea automată pentru a folosi căutarea magică și recunoașterea facială", + "searchDiscoverEmptySection": "Imaginile vor fi afișate aici odată ce procesarea și sincronizarea este completă", + "searchPersonsEmptySection": "Persoanele vor fi afișate aici odată ce procesarea și sincronizarea este completă", + "viewersSuccessfullyAdded": "{count, plural, one {} few {S-au adăugat {count} observatori}=0 {S-au adăugat 0 observatori} =1 {S-a adăugat 1 observator} other {S-au adăugat {count} de observatori}}", + "@viewersSuccessfullyAdded": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + }, + "description": "Number of viewers that were successfully added to an album." + }, + "collaboratorsSuccessfullyAdded": "{count, plural, one {} few {S-au adăugat {count} colaboratori}=0 {S-au adăugat 0 colaboratori} =1 {S-a adăugat 1 colaborator} other {S-au adăugat {count} de colaboratori}}", + "@collaboratorsSuccessfullyAdded": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + }, + "description": "Number of collaborators that were successfully added to an album." + }, + "accountIsAlreadyConfigured": "Contul este deja configurat.", + "sessionIdMismatch": "Nepotrivire ID sesiune", + "@sessionIdMismatch": { + "description": "In passkey page, deeplink is ignored because of session ID mismatch." + }, + "failedToFetchActiveSessions": "Nu s-a reușit preluarea sesiunilor active", + "@failedToFetchActiveSessions": { + "description": "In session page, warn user (in toast) that active sessions could not be fetched." + }, + "failedToRefreshStripeSubscription": "Nu s-a reușit reîmprospătarea abonamentului", + "failedToPlayVideo": "Eroare la redarea videoclipului", + "uploadIsIgnoredDueToIgnorereason": "Încărcare ignorată din motivul {ignoreReason}", + "@uploadIsIgnoredDueToIgnorereason": { + "placeholders": { + "ignoreReason": { + "type": "String", + "example": "no network" + } + } + }, + "typeOfGallerGallerytypeIsNotSupportedForRename": "Tipul de galerie {galleryType} nu este acceptat pentru redenumire", + "@typeOfGallerGallerytypeIsNotSupportedForRename": { + "placeholders": { + "galleryType": { + "type": "String", + "example": "no network" + } + } + }, + "tapToUploadIsIgnoredDue": "Atingeți pentru a încărca, încărcarea este ignorată în prezent datorită {ignoreReason}", + "@tapToUploadIsIgnoredDue": { + "description": "Shown in upload icon widet, inside a tooltip.", + "placeholders": { + "ignoreReason": { + "type": "String", + "example": "no network" + } + } + }, + "tapToUpload": "Atingeți pentru a încărca", + "@tapToUpload": { + "description": "Shown in upload icon widet, inside a tooltip." + }, + "info": "Informații", + "addFiles": "Adăugați fișiere", + "castAlbum": "Difuzați albumul", + "imageNotAnalyzed": "Imaginea nu a fost analizată", + "noFacesFound": "Nu au fost găsite fețe", + "fileNotUploadedYet": "Fișierul nu a fost încărcat încă", + "noSuggestionsForPerson": "Nicio sugestie pentru {personName}", + "@noSuggestionsForPerson": { + "placeholders": { + "personName": { + "type": "String", + "example": "Alice" + } + } + }, + "month": "lună", + "yearShort": "an", + "@yearShort": { + "description": "Appears in pricing page (/yr)" + }, + "currentlyRunning": "rulează în prezent", + "ignored": "ignorat", + "photosCount": "{count, plural, one {} few {{count} fotografii}=0 {0 fotografii} =1 {1 fotografie} other {{count} de fotografii}}", + "@photosCount": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, + "file": "Fișier", + "searchSectionsLengthMismatch": "Lungimea secțiunilor nu se potrivesc: {snapshotLength} != {searchLength}", + "@searchSectionsLengthMismatch": { + "description": "Appears in search tab page", + "placeholders": { + "snapshotLength": { + "type": "int", + "example": "1" + }, + "searchLength": { + "type": "int", + "example": "2" + } + } + }, + "selectMailApp": "Selectați aplicația de e-mail", + "selectAllShort": "Toate", + "@selectAllShort": { + "description": "Text that appears in bottom right when you start to select multiple photos. When clicked, it selects all photos." + }, + "changeLogMagicSearchImprovementTitle": "Îmbunătățire a căutării magice", + "changeLogMagicSearchImprovementContent": "Am îmbunătățit căutarea magică pentru a deveni mult mai rapidă, astfel încât să nu trebuiască să așteptați pentru a găsi ceea ce căutați.", + "changeLogBackupStatusTitle": "Starea copiei de rezervă", + "changeLogBackupStatusContent": "Am adăugat un jurnal al tuturor fișierelor care au fost încărcate pe Ente, inclusiv eșecurile și coada de așteptare.", + "changeLogDiscoverTitle": "Descoperire", + "changeLogDiscoverContent": "Căutați fotografii ale cărților de identitate, notițe sau chiar meme-uri? Mergeți la fila de căutare și dați o privire la Descoperire. Bazat pe căutarea noastră semantică, este un loc în care puteți găsi fotografii care ar putea fi importante pentru dvs.\\n\\nEste disponibilă numai dacă ați activat învățarea automată.", + "selectCoverPhoto": "Selectați fotografia de copertă", + "newLocation": "Locație nouă", + "faceNotClusteredYet": "Fața nu este încă grupată, vă rugăm să reveniți mai târziu", + "theLinkYouAreTryingToAccessHasExpired": "Linkul pe care încercați să îl accesați a expirat.", + "openFile": "Deschidere fișier", + "backupFile": "Salvare fișier", + "openAlbumInBrowser": "Deschideți albumul în browser", + "openAlbumInBrowserTitle": "Vă rugăm să utilizați aplicația web pentru a adăuga fotografii la acest album", + "allow": "Permiteți", + "allowAppToOpenSharedAlbumLinks": "Permiteți aplicației să deschidă link-uri de album partajate", + "seePublicAlbumLinksInApp": "Vedeți linkurile albumelor publice în aplicație", + "emergencyContacts": "Contacte de urgență", + "acceptTrustInvite": "Acceptați invitația", + "declineTrustInvite": "Refuzați invitația", + "removeYourselfAsTrustedContact": "Eliminați-vă ca persoană de contact de încredere", + "legacy": "Moștenire", + "legacyPageDesc": "Moștenirea permite contactelor de încredere să vă acceseze contul în absența dvs.", + "legacyPageDesc2": "Persoanele de contact de încredere pot iniția recuperarea contului și, dacă nu este blocată în termen de 30 de zile, vă pot reseta parola și accesa contul.", + "legacyAccounts": "Conturi de moștenire", + "trustedContacts": "Contacte de încredere", + "addTrustedContact": "Adăugare contact de încredere", + "removeInvite": "Eliminare invitație", + "recoveryWarning": "Un contact de încredere încearcă să vă acceseze contul", + "rejectRecovery": "Respingeți recuperarea", + "recoveryInitiated": "Recuperare inițiată", + "recoveryInitiatedDesc": "Puteți accesa contul după {days} zile. O notificare va fi trimisă la {email}.", + "@recoveryInitiatedDesc": { + "placeholders": { + "days": { + "type": "int", + "example": "30" + }, + "email": { + "type": "String", + "example": "me@example.com" + } + } + }, + "cancelAccountRecovery": "Anulare recuperare", + "recoveryAccount": "Recuperare cont", + "cancelAccountRecoveryBody": "Sunteți sigur că doriți să anulați recuperarea?", + "startAccountRecoveryTitle": "Începeți recuperarea", + "whyAddTrustContact": "Contactul de încredere vă poate ajuta la recuperarea datelor.", + "recoveryReady": "Acum puteți recupera contul {email} setând o nouă parolă.", + "@recoveryReady": { + "placeholders": { + "email": { + "type": "String", + "example": "me@example.com" + } + } + }, + "recoveryWarningBody": "{email} încearcă să vă recupereze contul.", + "trustedInviteBody": "Ați fost învitat să fiți un contact de moștenire de către {email}.", + "warning": "Atenție", + "proceed": "Continuați", + "confirmAddingTrustedContact": "Sunteți pe cale să adăugați {email} ca persoană de contact de încredere. Acesta va putea să vă recupereze contul dacă lipsiți timp de {numOfDays} de zile.", + "@confirmAddingTrustedContact": { + "placeholders": { + "email": { + "type": "String", + "example": "me@example.com" + }, + "numOfDays": { + "type": "int", + "example": "30" + } + } + }, + "legacyInvite": "{email} v-a invitat să fiți un contact de încredere", + "authToManageLegacy": "Vă rugăm să vă autentificați pentru a gestiona contactele de încredere", + "useDifferentPlayerInfo": "Aveți probleme cu redarea acestui videoclip? Apăsați lung aici pentru a încerca un alt player.", + "hideSharedItemsFromHomeGallery": "Ascundeți elementele distribuite din galeria principală", + "gallery": "Galerie", + "joinAlbum": "Alăturați-vă albumului", + "joinAlbumSubtext": "pentru a vedea și a adăuga fotografii", + "joinAlbumSubtextViewer": "pentru a adăuga la albumele distribuite", + "join": "Alăturare" } \ No newline at end of file From e92b5c3397a6b061ab60dae74c1c22205f8995c7 Mon Sep 17 00:00:00 2001 From: Trekky12 Date: Mon, 20 Jan 2025 06:46:37 +0100 Subject: [PATCH 6/8] [cli] sync deleted files before syncing new/updates files (#4776) ## Description The CLI sorts deleted photos to the bottom of the sync queue. When an album is synced, a photo is removed from the album and later re-added to the album (same name) and doing another sync the new file is added first and a new name is generated since the original filename is (still) taken. Afterwards the initial photo is deleted and the filename would be available. This leads to having a file, e.g. IMG_0001_1.JPG instead of the original filename IMG_0001.JPG despite the initial filename is no longer on disk and could have been used. This PR changes the sort order so that deleted files are first removed and afterwards new files are created. In this case all files where the filename is now available, but were taken, are named like they were uploaded. --- cli/pkg/model/remote.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/pkg/model/remote.go b/cli/pkg/model/remote.go index 494f36ef3d..7429256936 100644 --- a/cli/pkg/model/remote.go +++ b/cli/pkg/model/remote.go @@ -65,7 +65,7 @@ type AlbumFileEntry struct { func SortAlbumFileEntry(entries []*AlbumFileEntry) { sort.Slice(entries, func(i, j int) bool { if entries[i].IsDeleted != entries[j].IsDeleted { - return !entries[i].IsDeleted && entries[j].IsDeleted + return entries[i].IsDeleted && !entries[j].IsDeleted } return entries[i].AlbumID < entries[j].AlbumID }) From 8e26433dd26aa01ae7be9070d9c1a3bb5bd67f05 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 18 Jan 2025 16:08:56 +0530 Subject: [PATCH 7/8] [mob] LowerMem & increase ops limit for key derivation --- mobile/lib/utils/crypto_util.dart | 40 ++++++++++++++++++------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/mobile/lib/utils/crypto_util.dart b/mobile/lib/utils/crypto_util.dart index 2005e7882f..e48ca1587d 100644 --- a/mobile/lib/utils/crypto_util.dart +++ b/mobile/lib/utils/crypto_util.dart @@ -18,6 +18,8 @@ const int loginSubKeyLen = 32; const int loginSubKeyId = 1; const String loginSubKeyContext = "loginctx"; +const int _keyDerivcationStregth = 1073741824 * 4; + Uint8List cryptoSecretboxEasy(Map args) { return Sodium.cryptoSecretboxEasy(args["source"], args["nonce"], args["key"]); } @@ -392,24 +394,28 @@ class CryptoUtil { Uint8List salt, ) async { final logger = Logger("pwhash"); - int memLimit = Sodium.cryptoPwhashMemlimitSensitive; - int opsLimit = Sodium.cryptoPwhashOpslimitSensitive; - if (await isLowSpecDevice()) { - logger.info("low spec device detected"); - // When sensitive memLimit (1 GB) is used, on low spec device the OS might - // kill the app with OOM. To avoid that, start with 256 MB and - // corresponding ops limit (16). - // This ensures that the product of these two variables - // (the area under the graph that determines the amount of work required) - // stays the same - // SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE: 1073741824 - // SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE: 268435456 - // SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE: 4 - memLimit = Sodium.cryptoPwhashMemlimitModerate; - final factor = Sodium.cryptoPwhashMemlimitSensitive ~/ - Sodium.cryptoPwhashMemlimitModerate; // = 4 - opsLimit = opsLimit * factor; // = 16 + final int desiredStrength = Sodium.cryptoPwhashMemlimitSensitive * + Sodium.cryptoPwhashOpslimitSensitive; + // When sensitive memLimit (1 GB) is used, on low spec device the OS might + // kill the app with OOM. To avoid that, start with 256 MB and + // corresponding ops limit (16). + // This ensures that the product of these two variables + // (the area under the graph that determines the amount of work required) + // stays the same + // SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE: 1073741824 + // SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE: 268435456 + // SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE: 4 + int memLimit = Sodium.cryptoPwhashMemlimitModerate; + final factor = Sodium.cryptoPwhashMemlimitSensitive ~/ + Sodium.cryptoPwhashMemlimitModerate; // = 4 + int opsLimit = Sodium.cryptoPwhashOpslimitSensitive * factor; // = 16 + if (memLimit * opsLimit != desiredStrength || + desiredStrength != _keyDerivcationStregth) { + throw UnsupportedError( + "unexpcted values for memLimit $memLimit and opsLimit: $opsLimit or desiredStrength: $desiredStrength", + ); } + Uint8List key; while (memLimit >= Sodium.cryptoPwhashMemlimitMin && opsLimit <= Sodium.cryptoPwhashOpslimitMax) { From ffde143247e50d6e55848ac3bf5a4dd0decf8352 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 18 Jan 2025 16:14:00 +0530 Subject: [PATCH 8/8] [mob] Fixed typo and lint --- mobile/lib/utils/crypto_util.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mobile/lib/utils/crypto_util.dart b/mobile/lib/utils/crypto_util.dart index e48ca1587d..d9d515d47e 100644 --- a/mobile/lib/utils/crypto_util.dart +++ b/mobile/lib/utils/crypto_util.dart @@ -8,7 +8,6 @@ import 'package:logging/logging.dart'; import "package:photos/core/errors.dart"; import 'package:photos/models/derived_key_result.dart'; import 'package:photos/models/encryption_result.dart'; -import "package:photos/utils/device_info.dart"; const int encryptionChunkSize = 4 * 1024 * 1024; final int decryptionChunkSize = @@ -18,7 +17,7 @@ const int loginSubKeyLen = 32; const int loginSubKeyId = 1; const String loginSubKeyContext = "loginctx"; -const int _keyDerivcationStregth = 1073741824 * 4; +const int _keyDerivationStregth = 1073741824 * 4; Uint8List cryptoSecretboxEasy(Map args) { return Sodium.cryptoSecretboxEasy(args["source"], args["nonce"], args["key"]); @@ -410,7 +409,7 @@ class CryptoUtil { Sodium.cryptoPwhashMemlimitModerate; // = 4 int opsLimit = Sodium.cryptoPwhashOpslimitSensitive * factor; // = 16 if (memLimit * opsLimit != desiredStrength || - desiredStrength != _keyDerivcationStregth) { + desiredStrength != _keyDerivationStregth) { throw UnsupportedError( "unexpcted values for memLimit $memLimit and opsLimit: $opsLimit or desiredStrength: $desiredStrength", );