[mob][photos] Resolve merge conflicts and merge main

This commit is contained in:
ashilkn 2024-09-02 14:52:22 +05:30
commit d42d8965e6
107 changed files with 3265 additions and 1257 deletions

View File

@ -8,7 +8,7 @@ on:
- ".github/workflows/auth-lint.yml"
env:
FLUTTER_VERSION: "3.24.0"
FLUTTER_VERSION: "3.24.1"
jobs:
lint:

View File

@ -29,7 +29,7 @@ on:
- "auth-v*"
env:
FLUTTER_VERSION: "3.24.0"
FLUTTER_VERSION: "3.24.1"
jobs:
build-ubuntu:

View File

@ -24,6 +24,27 @@
{
"title": "AscendEX"
},
{
"title": "Battle.net",
"slug": "battlenet",
"altNames": [
"Battle net",
"Blizzard"
]
},
{
"title": "Bethesda",
"altNames": [
"Bethesda Softworks"
]
},
{
"title": "BinanceUS",
"slug": "binance_us",
"altNames": [
"Binance US"
]
},
{
"title": "Bitfinex"
},
@ -31,12 +52,12 @@
"title": "bitget"
},
{
"titile":"bitget wallet",
"slug":"bitget_wallet"
"title": "bitget wallet",
"slug": "bitget_wallet"
},
{
"title": "Bitmart",
"hex":"000000"
"hex": "000000"
},
{
"title": "BitMEX"
@ -51,8 +72,7 @@
"title": "Bitstamp"
},
{
"title": "Bitvavo",
"hex": "0051FF"
"title": "Bitvavo"
},
{
"title": "Bitwarden"
@ -70,27 +90,24 @@
"blockchain.com",
"blockchain.com Wallet",
"blockchain.com Exchange"
],
"slug": "blockchain"
]
},
{
"title": "BorgBase",
"altNames": [
"borg"
],
"slug": "BorgBase"
]
},
{
"title": "Booking",
"slug": "booking",
"altNames":[
"altNames": [
"Booking.com"
]
},
{
"title": "Brave Creators",
"slug": "brave_creators",
"altNames":[
"altNames": [
"Brave",
"Brave Rewards",
"Brave Browser"
@ -110,15 +127,11 @@
"slug": "cih",
"hex": "D14633"
},
{
"title": "Cloudflare"
},
{
"title": "CloudAMQP"
},
{
"title": "ConfigCat",
"slug": "configcat"
"title": "Cloudflare"
},
{
"title": "CoinDCX"
@ -128,8 +141,7 @@
},
{
"title": "Control D",
"slug": "controld",
"hex": "5FD800"
"slug": "controld"
},
{
"title": "Crowdpear"
@ -139,7 +151,6 @@
"slug": "crypto",
"altNames": [
"crypto",
"Crypto.com",
"Crypto com"
]
},
@ -147,8 +158,7 @@
"title": "DCS",
"altNames": [
"Digital Combat Simulator"
],
"slug": "dcs"
]
},
{
"title": "DEGIRO"
@ -173,16 +183,22 @@
"slug": "dusnet"
},
{
"title":"ecitizen kenya",
"slug":"ecitizen_kenya"
"title": "ecitizen kenya",
"slug": "ecitizen_kenya"
},
{
"title": "ecloud",
"altNames": [
"Murena"
]
},
{
"title": "ente",
"hex": "1DB954"
},
{
"title": "enom"
},
"title": "enom"
},
{
"title": "Epic Games",
"slug": "epic_games",
@ -218,10 +234,10 @@
},
{
"title": "Gosuslugi",
"slug": "Gosuslugi",
"altNames": [
"Госуслуги"
],
"slug": "Gosuslugi"
]
},
{
"title": "Habbo"
@ -240,16 +256,13 @@
"title": "HuggingFace",
"altNames": [
"Hugging Face"
],
"slug": "huggingface"
]
},
{
"title": "IceDrive",
"slug": "Icedrive"
"title": "IceDrive"
},
{
"titile": "Infomaniak",
"slug": "infomaniak"
"title": "Infomaniak"
},
{
"title": "ING"
@ -270,8 +283,7 @@
"hex": "000000"
},
{
"title": "IVPN",
"slug": "IVPN"
"title": "IVPN"
},
{
"title": "Jagex",
@ -281,8 +293,7 @@
"title": "Kagi"
},
{
"title": "Kick",
"hex": "53FC19"
"title": "Kick"
},
{
"title": "Kite"
@ -295,15 +306,13 @@
"color": "00CC00"
},
{
"title": "Kraken",
"hex": "5848D5"
"title": "Kraken"
},
{
"title": "Kronos"
},
{
"title": "KuCoin",
"hex": "01BC8D"
"title": "KuCoin"
},
{
"title": "La Poste",
@ -333,7 +342,6 @@
"mathstodon",
"fosstodon"
],
"slug": "mastodon",
"hex": "6364FF"
},
{
@ -364,13 +372,6 @@
{
"title": "Mozilla"
},
{
"title": "Murena",
"altNames": [
"eCloud"
],
"slug": "ecloud"
},
{
"title": "MyFRITZ!Net",
"slug": "myfritz",
@ -406,6 +407,12 @@
{
"title": "NextDNS"
},
{
"title": "Newton",
"altNames": [
"Newton Crypto"
]
},
{
"title": "ngrok",
"hex": "858585"
@ -420,8 +427,7 @@
"title": "Notion"
},
{
"title": "NuCommunity",
"slug": "nucommunity"
"title": "NuCommunity"
},
{
"title": "NVIDIA"
@ -430,16 +436,17 @@
"title": "Odido"
},
{
"titile": "OpenObserve",
"title": "OpenObserve",
"slug": "open_observe",
"altNames":[
"altNames": [
"openobserve.ai",
"openobserve ai"
]
},
{
"title": "okx",
"hex": "000000" },
"hex": "000000"
},
{
"title": "Parsec"
},
@ -447,16 +454,13 @@
"title": "PayPal"
},
{
"title": "pCloud",
"slug": "pCloud"
"title": "pCloud"
},
{
"title": "Peerberry",
"hex": "03E5A5"
"title": "Peerberry"
},
{
"title": "Pingvin Share",
"hex": "485099"
"title": "Pingvin Share"
},
{
"title": "Plutus",
@ -466,12 +470,10 @@
"title": "Poloniex"
},
{
"title": "Porkbun",
"hex": "F27777"
"title": "Porkbun"
},
{
"title": "PostNL",
"color": "EF8300"
"title": "PostNL"
},
{
"title": "Privacy Guides",
@ -495,7 +497,6 @@
"title": "Registro br",
"slug": "registro_br",
"altNames": [
"Registro br",
"registrobr",
"Registro.br"
]
@ -507,13 +508,15 @@
"title": "Revolt",
"hex": "858585"
},
{
"title": "RippleMatch"
},
{
"title": "Rockstar Games",
"slug": "rockstar_games"
},
{
"title": "RuneMate",
"hex": "2ECC71"
"title": "RuneMate"
},
{
"title": "Rust Language Forum",
@ -529,6 +532,9 @@
{
"title": "service-bw"
},
{
"title": "Shakepay"
},
{
"title": "SimpleLogin"
},
@ -551,16 +557,14 @@
]
},
{
"title": "SMTP2GO",
"slug": "smtp2go"
"title": "SMTP2GO"
},
{
"title": "Snapchat"
},
{
"title": "Standard Notes",
"slug": "standardnotes",
"hex": "2173E6"
"slug": "standardnotes"
},
{
"title": "Surfshark"
@ -570,9 +574,7 @@
"slug": "synology_dsm"
},
{
"title": "TCPShield",
"slug": "tcpshield",
"hex": "FFFFFF"
"title": "TCPShield"
},
{
"title": "Techlore",
@ -596,9 +598,7 @@
"title": "TorGuard"
},
{
"title": "Trading 212",
"slug": "trading212",
"hex": "4BA4DE"
"title": "Trading 212"
},
{
"title": "TradingView"
@ -635,16 +635,16 @@
"hex": "858585"
},
{
"title": "Uphold",
"slug": "uphold",
"hex": "6FE68A"
"title": "Uphold"
},
{
"title": "Upstox"
},
{
"titile": "Vikunja",
"slug": "vikunja"
"title": "Vikunja"
},
{
"title": "Wealthfront"
},
{
"title": "Wealthsimple"
@ -659,71 +659,30 @@
{
"title": "Wise"
},
{
"title": "WYZE",
"slug": "wyze"
},
{
"title": "WorkOS",
"slug": "workos",
"altNames": [
"Work OS"
]
},
{
"title": "WYZE"
},
{
"title": "yahoo"
},
{
"title": "Yandex",
"altNames": [
"Ya",
"Яндекс"
],
"slug": "Yandex"
},
{
"title": "yahoo"
]
},
{
"title": "YNAB",
"altNames": [
"You Need A Budget"
],
"slug": "ynab",
"hex": "3B5EDA"
},
{
"title": "Shakepay",
"slug": "shakepay"
},
{
"title": "Newton",
"altNames": ["Newton Crypto"],
"slug": "newton"
},
{
"title": "RippleMatch",
"slug": "ripplematch"
},
{
"title": "T-Mobile ID",
"altNames": [
"T-Mobile"
],
"slug": "t-mobile"
},
{
"title": "Wealthfront",
"slug": "wealthfront"
},
{
"title": "BinanceUS",
"altNames": [
"Binance US"
],
"slug": "binance_us"
},
{
"title": "Bethesda Softworks",
"altNames": ["Bethesda"],
"slug": "bethesda"
]
}
]
}
}

View File

@ -0,0 +1,10 @@
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_181_130)">
<path d="M394.583 172.833C331.25 143.583 240.292 125 154.708 131.917C159 103.583 169.583 83.8334 186.958 79.6667C210.875 73.9376 236.958 89.6667 261.833 118.292C278.125 120.417 297.458 124.125 310.896 127.229C265.562 41.9375 204.75 -5.87495 158.479 11.7709C123.312 25.1876 105.292 73.6459 107.479 138.729C61.6457 148.479 25.9166 165.813 3.04156 190.708C1.8749 192.063 -0.729272 195.438 0.187395 197.063C0.895728 198.271 3.22906 196.896 4.22906 196.229C30.7707 177.688 64.7291 167.625 108.958 161.313C115.271 230.75 144.646 318.813 193.437 389.479C166.771 399.938 144.354 400.646 132.062 387.667C115.146 369.833 115.729 339.375 128.104 303.5C121.802 288.106 116.192 272.437 111.292 256.542C60.0624 338.5 49.0832 415.083 87.4791 446.333C116.687 470.083 167.625 461.438 222.937 427.042C254.292 461.875 287.167 484.125 320.167 491.5C321.917 491.813 326.146 492.396 327.083 490.792C327.792 489.542 325.417 488.208 324.354 487.688C295.021 474 269.354 449.604 241.771 414.458C298.75 374.271 360.333 304.813 397.125 227.208C419.542 245.125 431.292 264.146 426.25 281.271C419.25 304.833 392.604 319.604 355.354 326.792C345.157 339.931 334.397 352.624 323.104 364.833C419.646 368.167 491.437 339.417 499.312 290.563C505.271 253.354 472.333 213.563 414.875 182.854C429.375 138.292 432.208 98.6876 422.083 66.4376C421.479 64.7709 419.875 60.8126 418 60.8126C416.583 60.8126 416.604 63.5209 416.687 64.7084C419.5 96.9376 411.208 131.375 394.562 172.833H394.583ZM216.812 378.438C175.979 312.917 152.437 235.75 152.604 157.729C229.729 155.146 308.312 173.313 375.812 212.458C339.5 280.563 284.458 339.542 216.792 378.417L216.812 378.438Z" fill="#148EFF"/>
</g>
<defs>
<clipPath id="clip0_181_130">
<rect width="500" height="500" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2500 2500" xmlns:v="https://vecta.io/nano"><path d="M830.3 1250.3l740.5 740.6 467.4-467.4c83.6-75.2 211.4-71.9 290.9 7.6s82.9 207.4 7.6 290.9l-616.8 616.9c-82.9 81.5-215.7 81.5-298.6 0l-889.8-890.1v529c0 116.8-94.6 211.4-211.4 211.4s-211.4-94.6-211.4-211.4V422c0-116.7 94.6-211.4 211.4-211.4S531.5 305.3 531.5 422v529l889.7-889.9c82.8-81.5 215.8-81.5 298.6 0L2337 677.9c75.2 83.6 71.9 211.4-7.6 290.9s-207.4 82.9-290.9 7.6L1571.1 509l-740.8 741.3h0zm740.8-211.7h0c-85.6 0-162.8 51.5-195.6 130.6s-14.7 170.1 45.8 230.7 151.6 78.7 230.7 46c79.1-32.8 130.7-109.9 130.7-195.6a211.87 211.87 0 0 0-61.9-149.7c-39.6-39.7-93.5-62-149.6-62h-.1 0z" fill-rule="evenodd" fill="#23af91"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2500 2500" xmlns:v="https://vecta.io/nano"><path d="M830.3 1250.3l740.5 740.6 467.4-467.4c83.6-75.2 211.4-71.9 290.9 7.6s82.9 207.4 7.6 290.9l-616.8 616.9c-82.9 81.5-215.7 81.5-298.6 0l-889.8-890.1v529c0 116.8-94.6 211.4-211.4 211.4s-211.4-94.6-211.4-211.4V422c0-116.7 94.6-211.4 211.4-211.4S531.5 305.3 531.5 422v529l889.7-889.9c82.8-81.5 215.8-81.5 298.6 0L2337 677.9c75.2 83.6 71.9 211.4-7.6 290.9s-207.4 82.9-290.9 7.6L1571.1 509l-740.8 741.3h0zm740.8-211.7h0c-85.6 0-162.8 51.5-195.6 130.6s-14.7 170.1 45.8 230.7 151.6 78.7 230.7 46c79.1-32.8 130.7-109.9 130.7-195.6a211.87 211.87 0 0 0-61.9-149.7c-39.6-39.7-93.5-62-149.6-62h-.1 0z" fill-rule="evenodd" fill="#01BC8D"/></svg>

Before

Width:  |  Height:  |  Size: 735 B

After

Width:  |  Height:  |  Size: 735 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 18 KiB

@ -1 +1 @@
Subproject commit 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
Subproject commit 5874a72aa4c779a02553007c47dacbefba2374dc

View File

@ -67,7 +67,7 @@
"pleaseWait": "Моля изчакайте...",
"generatingEncryptionKeysTitle": "Генерират се ключове за шифроване...",
"recreatePassword": "Създайте отново парола",
"recreatePasswordMessage": "Текущото устройство не е достатъчно мощно, за да потвърди паролата Ви, така че трябва да го генерираме отново веднъж по начин, който работи с всички устройства. \n\nВлезте с Вашия ключ за възстановяване и генерирайте отново паролата си (можете да използвате същата отново, ако желаете).",
"recreatePasswordMessage": "Текущото устройство не е достатъчно мощно, за да потвърди паролата Ви, така че трябва да го генерираме отново веднъж по начин, който работи с всички устройства. \n\nМоля, влезте с Вашия ключ за възстановяване и генерирайте отново паролата си (можете да използвате същата отново, ако желаете).",
"useRecoveryKey": "Използвайте ключ за възстановяване",
"incorrectPasswordTitle": "Грешна парола",
"welcomeBack": "Добре дошли отново!",
@ -130,7 +130,61 @@
"faq_q_3": "Как мога да изтрия кодове?",
"faq_a_3": "Можете да изтриете код, като плъзнете наляво върху него.",
"faq_q_4": "Как мога да подкрепя този проект?",
"faq_a_4": "Можете да подкрепите развитието на този проект, като се абонирате за нашето приложение за снимки @ ente.io.",
"faq_q_5": "Как мога да активирам заключване чрез FaceID в Auth",
"faq_a_5": "Можете да активирате заключване чрез FaceID в Настройки → Сигурност → Заключен екран.",
"somethingWentWrongMessage": "Нещо се обърка, моля опитайте отново",
"leaveFamily": "Напуснете семейството",
"leaveFamilyMessage": "Сигурни ли сте, че искате да напуснете семейния план?",
"inFamilyPlanMessage": "Вие сте на семеен план!",
"swipeHint": "Плъзнете наляво, за да редактирате или премахнете кодове",
"scan": "Сканиране",
"scanACode": "Скениране на код",
"verify": "Потвърждаване",
"verifyEmail": "Потвърдете имейла",
"enterCodeHint": "Въведете 6-цифрения код от\nВашето приложение за удостоверяване",
"lostDeviceTitle": "Загубено устройство?",
"twoFactorAuthTitle": "Двуфакторно удостоверяване",
"passkeyAuthTitle": "Удостоверяване с ключ за парола",
"verifyPasskey": "Потвърдете ключ за парола",
"recoverAccount": "Възстановяване на акаунт",
"enterRecoveryKeyHint": "Въведете Вашия ключ за възстановяване",
"recover": "Възстановяване",
"contactSupportViaEmailMessage": "Моля, изпратете имейл до {email} от Вашия регистриран имейл адрес",
"@contactSupportViaEmailMessage": {
"placeholders": {
"email": {
"type": "String"
}
}
},
"invalidQRCode": "Невалиден QR код",
"noRecoveryKeyTitle": "Няма ключ за възстановяване?",
"enterEmailHint": "Въведете Вашият имейл адрес",
"invalidEmailTitle": "Невалиден имейл адрес",
"invalidEmailMessage": "Моля, въведете валиден имейл адрес.",
"deleteAccount": "Изтриване на акаунта",
"deleteAccountQuery": "Ще съжаляваме да си тръгнете. Изправени ли сте пред някакъв проблем?",
"yesSendFeedbackAction": "Да, изпращане на обратна връзка",
"noDeleteAccountAction": "Не, изтриване на акаунта",
"initiateAccountDeleteTitle": "Моля, удостоверете се, за да инициирате изтриването на акаунта",
"sendEmail": "Изпратете имейл",
"createNewAccount": "Създаване на нов акаунт",
"weakStrength": "Слаба",
"strongStrength": "Силна",
"moderateStrength": "Умерена",
"confirmPassword": "Потвърждаване на паролата",
"close": "Затваряне",
"oopsSomethingWentWrong": "Ами сега, нещо се обърка.",
"selectLanguage": "Изберете език",
"language": "Език",
"social": "Социални мрежи",
"security": "Сигурност",
"lockscreen": "Заключен екран",
"authToChangeLockscreenSetting": "Моля, удостоверете се, за да промените настройката за заключен екран",
"deviceLockEnablePreSteps": "За да активирате заключването на устройството, моля, задайте парола за устройството или заключване на екрана в системните настройки.",
"viewActiveSessions": "Вижте активните сесии",
"authToViewYourActiveSessions": "Моля, удостоверете се, за да видите Вашите активни сесии",
"searchHint": "Търсене...",
"search": "Търсене",
"sorryUnableToGenCode": "За съжаление не може да се генерира код за {issuerName}",
@ -183,46 +237,118 @@
"insecureDevice": "Несигурно устройство",
"sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "За съжаление не можахме да генерираме защитени ключове на това устройство.\n\nМоля, регистрирайте се от друго устройство.",
"howItWorks": "Как работи",
"ackPasswordLostWarning": "Разбирам, че ако загубя паролата си, може да загубя данните си, тъй като данните ми са <underline>шифровани от край до край</underline>.",
"loginTerms": "С натискането на вход, се съгласявам с <u-terms>условията за ползване</u-terms> и <u-policy>политиката за поверителност</u-policy>",
"logInLabel": "Вход",
"logout": "Изход",
"areYouSureYouWantToLogout": "Наистина ли искате да излезете от профила си?",
"yesLogout": "Да, излез",
"exit": "Изход",
"verifyingRecoveryKey": "Проверка на ключа за възстановяване...",
"recoveryKeyVerified": "Ключът за възстановяване е проверен",
"recoveryKeySuccessBody": "Страхотно! Вашият ключ за възстановяване е валиден. Благодарим Ви за проверката.\n\nМоля, не забравяйте да запазите безопасно архивирания си ключ за възстановяване.",
"invalidRecoveryKey": "Въведеният от Вас ключ за възстановяване не е валиден. Моля, уверете се, че съдържа 24 думи и проверете правописа на всяка.\n\nАко сте въвели по-стар код за възстановяване, уверете се, че е дълъг 64 знака и проверете всеки от тях.",
"recreatePasswordTitle": "Създайте отново парола",
"recreatePasswordBody": "Текущото устройство не е достатъчно мощно, за да потвърди паролата Ви, но можем да я регенерираме по начин, който работи с всички устройства.\n\nМоля, влезте с Вашия ключ за възстановяване и генерирайте отново паролата си (можете да използвате същата отново, ако желаете).",
"invalidKey": "Невалиден ключ",
"tryAgain": "Опитайте отново",
"viewRecoveryKey": "Вижте ключа за възстановяване",
"confirmRecoveryKey": "Потвърдете ключа за възстановяване",
"recoveryKeyVerifyReason": "Вашият ключ за възстановяване е единственият начин да възстановите Вашите снимки, ако забравите паролата си. Можете да намерите своя ключ за възстановяване в Настройки > Акаунт.\n\nМоля, въведете Вашия ключ за възстановяване тук, за да проверите дали сте го запазили правилно.",
"confirmYourRecoveryKey": "Потвърдете Вашия ключ за възстановяване",
"confirm": "Потвърждаване",
"emailYourLogs": "Изпратете Вашата история на действията на имейл",
"pleaseSendTheLogsTo": "Моля, изпратете историята на действията на \n{toEmail}",
"copyEmailAddress": "Копиране на имейл адрес",
"exportLogs": "Експорт на файловете с историята",
"enterYourRecoveryKey": "Въведете Вашия ключ за възстановяване",
"tempErrorContactSupportIfPersists": "Изглежда нещо се обърка. Моля, опитайте отново след известно време. Ако грешката продължава, моля, свържете се с нашия екип за поддръжка.",
"networkHostLookUpErr": "Не може да се свърже с Ente, моля, проверете мрежовите си настройки и се свържете с поддръжката, ако проблемът продължава.",
"networkConnectionRefusedErr": "Не може да се свърже с Ente, моля, опитайте отново след известно време. Ако проблемът продължава, моля, свържете се с поддръжката.",
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Изглежда нещо се обърка. Моля, опитайте отново след известно време. Ако грешката продължава, моля, свържете се с нашия екип за поддръжка.",
"about": "Относно",
"weAreOpenSource": "Ние сме с отворен код!",
"privacy": "Поверителност",
"terms": "Условия",
"checkForUpdates": "Провери за актуализации",
"checkStatus": "Проверка на състоянието",
"downloadUpdate": "Изтегляне",
"criticalUpdateAvailable": "Налична е критична актуализация",
"updateAvailable": "Налична актуализация",
"update": "Актуализиране",
"checking": "Извършва се проверка...",
"youAreOnTheLatestVersion": "Вие сте с най-новата версия",
"warning": "Предупреждение",
"exportWarningDesc": "Експортираният файл съдържа поверителна информация. Моля, съхранявайте го безопасно.",
"iUnderStand": "Разбрах",
"@iUnderStand": {
"description": "Text for the button to confirm the user understands the warning"
},
"authToExportCodes": "Моля, удостоверете се, за да експортирате Вашите кодове",
"importSuccessTitle": "Ура!",
"importSuccessDesc": "Импортирахте {count} кода!",
"@importSuccessDesc": {
"placeholders": {
"count": {
"description": "The number of codes imported",
"type": "int",
"example": "1"
}
}
},
"sorry": "Съжаляваме",
"importFailureDesc": "Неуспешен анализ на избрания файл.\nМоля, пишете на support@ente.io, ако имате нужда от помощ!",
"pendingSyncs": "Предупреждение",
"pendingSyncsWarningBody": "Някои от вашите кодове не са архивирани.\n\nМоля, уверете се, че имате резервно копие на тези кодове, преди да излезете.",
"checkInboxAndSpamFolder": "Моля, проверете входящата си поща (и спама), за да завършите проверката",
"tapToEnterCode": "Докоснете, за да въведете код",
"resendEmail": "Повторно изпращане на имейл",
"weHaveSendEmailTo": "Изпратихме имейл до <green>{email}</green>",
"@weHaveSendEmailTo": {
"description": "Text to indicate that we have sent a mail to the user",
"placeholders": {
"email": {
"description": "The email address of the user",
"type": "String",
"example": "example@ente.io"
}
}
},
"activeSessions": "Активни сесии",
"somethingWentWrongPleaseTryAgain": "Нещо се обърка, моля опитайте отново",
"thisWillLogYouOutOfThisDevice": "Това ще Ви изкара от профила на това устройство!",
"thisWillLogYouOutOfTheFollowingDevice": "Това ще Ви изкара от профила на следното устройство:",
"terminateSession": "Прекратяване на сесията?",
"terminate": "Прекратяване",
"thisDevice": "Това устройство",
"toResetVerifyEmail": "За да нулирате паролата си, моля, първо потвърдете своя имейл.",
"thisEmailIsAlreadyInUse": "Този имейл вече се използва",
"verificationFailedPleaseTryAgain": "Неуспешно проверка, моля опитайте отново",
"yourVerificationCodeHasExpired": "Вашият код за потвърждение е изтекъл",
"incorrectCode": "Неправилен код",
"sorryTheCodeYouveEnteredIsIncorrect": "За съжаление кодът, който сте въвели, е неправилен",
"emailChangedTo": "Имейлът е променен на {newEmail}",
"authenticationFailedPleaseTryAgain": "Неуспешно удостоверяване, моля опитайте отново",
"authenticationSuccessful": "Успешно удостоверяване!",
"twofactorAuthenticationSuccessfullyReset": "Двуфакторното удостоверяване бе успешно нулирано",
"incorrectRecoveryKey": "Неправилен ключ за възстановяване",
"theRecoveryKeyYouEnteredIsIncorrect": "Въведеният от Вас ключ за възстановяване е неправилен",
"enterPassword": "Въведете парола",
"selectExportFormat": "Изберете формат за експортиране",
"exportDialogDesc": "Шифрованите експорти ще бъдат защитени с парола по Ваш избор.",
"encrypted": "Шифровано",
"plainText": "Обикновен текст",
"passwordToEncryptExport": "Парола за шифроване на експортирането",
"export": "Експортиране",
"useOffline": "Използвайте без резервни копия",
"signInToBackup": "Влезте, за да архивирате Вашите кодове",
"singIn": "Вход",
"sigInBackupReminder": "Моля, експортирайте Вашите кодове, за да сте сигурни, че имате резервно копие, от което можете да ги възстановите.",
"offlineModeWarning": "Избрахте да продължите без резервни копия. Моля, направете ръчни резервни копия, за да сте сигурни, че Вашите кодове са в безопасност.",
"showLargeIcons": "Показване на големи икони",
"shouldHideCode": "Скриване на кодове",
"doubleTapToViewHiddenCode": "Можете да докоснете два пъти върху запис, за да видите кода",
"focusOnSearchBar": "Фокусиране на търсенето при стартиране на приложението",
"confirmUpdatingkey": "Сигурни ли сте, че искате да актуализирате секретния ключ?",
"minimizeAppOnCopy": "Минимизиране на приложението при копиране",
"editCodeAuthMessage": "Удостоверете се, за да редактирате кода",

View File

@ -72,7 +72,7 @@
"incorrectPasswordTitle": "Falsches Passwort",
"welcomeBack": "Willkommen zurück!",
"madeWithLoveAtPrefix": "gemacht mit ❤️ bei ",
"supportDevs": "Bei <bold-green>ente</bold-green> registrieren um das Projekt zu unterstützen.",
"supportDevs": "Bei <bold-green>ente</bold-green> registrieren, um das Projekt zu unterstützen",
"supportDiscount": "Benutze den Rabattcode \"AUTH\" für 10% Rabatt im ersten Jahr",
"changeEmail": "E-Mail ändern",
"changePassword": "Passwort ändern",
@ -182,6 +182,7 @@
"security": "Sicherheit",
"lockscreen": "Sperrbildschirm",
"authToChangeLockscreenSetting": "Bitte authentifizieren um die Einstellungen des Sperrbildschirms zu ändern",
"deviceLockEnablePreSteps": "Um die Gerätesperre zu aktivieren, richte bitte einen Gerätepasscode oder eine Bildschirmsperre in den Systemeinstellungen ein.",
"viewActiveSessions": "Aktive Sitzungen anzeigen",
"authToViewYourActiveSessions": "Bitte authentifizieren um, die aktiven Sitzungen zu sehen",
"searchHint": "Suchen...",
@ -441,5 +442,29 @@
"deleteTagTitle": "Tag löschen?",
"deleteTagMessage": "Sind Sie sicher, dass Sie diesen Code löschen wollen? Diese Aktion ist unumkehrbar.",
"somethingWentWrongParsingCode": "Wir konnten {x} Codes nicht parsen.",
"updateNotAvailable": "Update ist nicht verfügbar"
"updateNotAvailable": "Update ist nicht verfügbar",
"viewRawCodes": "Rohcodes anzeigen",
"rawCodes": "Rohcodes",
"rawCodeData": "Rohcode Daten",
"appLock": "App-Sperre",
"noSystemLockFound": "Keine Systemsperre gefunden",
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Um die App-Sperre zu aktivieren, konfiguriere bitte den Gerätepasscode oder die Bildschirmsperre in den Systemeinstellungen.",
"autoLock": "Automatisches Sperren",
"immediately": "Sofort",
"reEnterPassword": "Passwort erneut eingeben",
"reEnterPin": "PIN erneut eingeben",
"next": "Weiter",
"tooManyIncorrectAttempts": "Zu viele fehlerhafte Versuche",
"tapToUnlock": "Zum Entsperren antippen",
"setNewPassword": "Neues Passwort festlegen",
"deviceLock": "Gerätesperre",
"hideContent": "Inhalte verstecken",
"hideContentDescriptionAndroid": "Versteckt Inhalte der App beim Wechseln zwischen Apps und deaktiviert Screenshots",
"hideContentDescriptioniOS": "Versteckt Inhalte der App beim Wechseln zwischen Apps",
"autoLockFeatureDescription": "Zeit, nach der die App gesperrt wird, nachdem sie in den Hintergrund verschoben wurde",
"appLockDescription": "Wähle zwischen dem Standard-Sperrbildschirm deines Gerätes und einem eigenen Sperrbildschirm mit PIN oder Passwort.",
"pinLock": "PIN-Sperre",
"enterPin": "PIN eingeben",
"setNewPin": "Neue PIN festlegen",
"importFailureDescNew": "Die ausgewählte Datei konnte nicht verarbeitet werden."
}

View File

@ -182,6 +182,7 @@
"security": "Ασφάλεια",
"lockscreen": "Οθόνη κλειδώματος",
"authToChangeLockscreenSetting": "Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να αλλάξετε τις ρυθμίσεις οθόνης κλειδώματος",
"deviceLockEnablePreSteps": "Για να ενεργοποιήσετε το κλείδωμα της συσκευής, παρακαλώ ρυθμίστε τον κωδικό πρόσβασης της συσκευής ή το κλείδωμα οθόνης στις ρυθμίσεις του συστήματός σας.",
"viewActiveSessions": "Προβολή ενεργών συνεδριών",
"authToViewYourActiveSessions": "Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να δείτε τις ενεργές συνεδρίες σας",
"searchHint": "Αναζήτηση...",
@ -458,6 +459,10 @@
"setNewPassword": "Ορίστε νέο κωδικό πρόσβασης",
"deviceLock": "Κλείδωμα συσκευής",
"hideContent": "Απόκρυψη περιεχομένου",
"hideContentDescriptionAndroid": "Απόκρυψη περιεχομένου εφαρμογής στην εναλλαγή εφαρμογών και απενεργοποίηση στιγμιότυπων οθόνης",
"hideContentDescriptioniOS": "Απόκρυψη περιεχομένου εφαρμογών στην εναλλαγή εφαρμογών",
"autoLockFeatureDescription": "Χρόνος μετά τον οποίο η εφαρμογή κλειδώνει μετά την τοποθέτηση στο παρασκήνιο",
"appLockDescription": "Επιλέξτε ανάμεσα στην προεπιλεγμένη οθόνη κλειδώματος της συσκευής σας και σε μια προσαρμοσμένη οθόνη κλειδώματος με ένα PIN ή έναν κωδικό πρόσβασης.",
"pinLock": "Κλείδωμα καρφιτσωμάτων",
"enterPin": "Εισαγωγή PIN",
"setNewPin": "Ορίστε νέο PIN",

View File

@ -19,7 +19,7 @@
"pleaseVerifyDetails": "Veuillez vérifier vos informations et réessayez",
"codeIssuerHint": "Émetteur",
"codeSecretKeyHint": "Clé secrète",
"codeAccountHint": "Compte (vous@exemple.com)",
"codeAccountHint": "Compte (nom@exemple.com)",
"codeTagHint": "Tag",
"accountKeyType": "Type de clé",
"sessionExpired": "Session expirée",
@ -118,7 +118,7 @@
"existingUser": "Utilisateur existant",
"newUser": "Nouveau dans Ente",
"delete": "Supprimer",
"enterYourPasswordHint": "Saisir votre mot de passe",
"enterYourPasswordHint": "Entrez votre mot de passe",
"forgotPassword": "Mot de passe oublié",
"oops": "Oups",
"suggestFeatures": "Suggérer des fonctionnalités",
@ -135,14 +135,14 @@
"faq_a_5": "Vous pouvez activer le verrouillage FaceID dans Paramètres → Sécurité → Écran de verrouillage.",
"somethingWentWrongMessage": "Quelque chose s'est mal passé, veuillez recommencer",
"leaveFamily": "Quitter le plan familial",
"leaveFamilyMessage": "Êtes-vous certains de vouloir quitter le plan familial?",
"leaveFamilyMessage": "Êtes-vous sûr de vouloir quitter le plan familial ?",
"inFamilyPlanMessage": "Vous êtes sur un plan familial !",
"swipeHint": "Glisser vers la gauche pour modifier ou supprimer des codes",
"scan": "Analyser",
"scanACode": "Scanner un code",
"verify": "Vérifier",
"verifyEmail": "Vérifier l'e-mail",
"enterCodeHint": "Saisir le code à 6 caractères de votre appli d'authentification",
"enterCodeHint": "Entrez le code à 6 chiffres de votre application d'authentification",
"lostDeviceTitle": "Appareil perdu ?",
"twoFactorAuthTitle": "Authentification à deux facteurs",
"passkeyAuthTitle": "Vérification du code d'accès",
@ -446,7 +446,9 @@
"viewRawCodes": "Afficher les codes bruts",
"rawCodes": "Codes bruts",
"rawCodeData": "Données de code brut",
"appLock": "Verrouillage d'application",
"noSystemLockFound": "Aucun verrou système trouvé",
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Pour activer le verrouillage d'application, veuillez configurer un code d'accès ou le verrouillage de l'écran dans les paramètres de votre appareil.",
"autoLock": "Verrouillage automatique",
"immediately": "Immédiatement",
"reEnterPassword": "Ressaisir le mot de passe",
@ -457,6 +459,9 @@
"setNewPassword": "Définir un nouveau mot de passe",
"deviceLock": "Verrouillage de l'appareil",
"hideContent": "Masquer le contenu",
"autoLockFeatureDescription": "Délai après lequel l'application se verrouille une fois qu'elle a été mise en arrière-plan",
"appLockDescription": "Choisissez entre l'écran de verrouillage par défaut de votre appareil et un écran de verrouillage par code PIN ou mot de passe personnalisé.",
"pinLock": "Verrouillage par code PIN",
"enterPin": "Saisir le code PIN",
"setNewPin": "Définir un nouveau code PIN",
"importFailureDescNew": "Impossible de lire le fichier sélectionné."

View File

@ -20,10 +20,11 @@
"@sessionExpired": {
"description": "Title of the dialog when the users current session is invalid/expired"
},
"pleaseLoginAgain": "Vänligen logga in igen",
"pleaseLoginAgain": "Logga in igen",
"loggingOut": "Loggar ut...",
"saveAction": "Spara",
"nextTotpTitle": "nästa",
"deleteCodeTitle": "Radera kod?",
"deleteCodeMessage": "Vill du ta bort den här koden? Det går inte att ångra den här åtgärden.",
"viewLogsAction": "Visa loggar",
"emailLogsTitle": "E-posta loggar",
@ -63,6 +64,7 @@
"importCodes": "Importera koder",
"exportCodes": "Exportera koder",
"importLabel": "Importera",
"ok": "OK",
"cancel": "Avbryt",
"yes": "Ja",
"no": "Nej",
@ -109,8 +111,21 @@
"recoveryKeyOnForgotPassword": "Om du glömmer ditt lösenord är det enda sättet du kan återställa dina data med denna nyckel.",
"saveKey": "Spara nyckel",
"save": "Spara",
"send": "Skicka",
"back": "Tillbaka",
"createAccount": "Skapa konto",
"passwordStrength": "Lösenordsstyrka: {passwordStrengthValue}",
"@passwordStrength": {
"description": "Text to indicate the password strength",
"placeholders": {
"passwordStrengthValue": {
"description": "The strength of the password as a string",
"type": "String",
"example": "Weak or Moderate or Strong"
}
},
"message": "Password Strength: {passwordStrengthText}"
},
"password": "Lösenord",
"privacyPolicyTitle": "Integritetspolicy",
"termsOfServicesTitle": "Villkor",
@ -151,6 +166,7 @@
"incorrectRecoveryKey": "Felaktig återställningsnyckel",
"enterPassword": "Ange lösenord",
"export": "Exportera",
"signInToBackup": "Logga in för att säkerhetskopiera dina koder",
"singIn": "Logga in",
"shouldHideCode": "Dölj koder",
"androidCancelButton": "Avbryt",
@ -163,7 +179,11 @@
},
"noInternetConnection": "Ingen internetanslutning",
"pleaseCheckYourInternetConnectionAndTryAgain": "Kontrollera din internetanslutning och försök igen.",
"signOutOtherDevices": "Logga ut andra enheter",
"loginSessionExpiredDetails": "Din session har upphört. Logga in igen.",
"create": "Skapa",
"editTag": "Redigera tagg",
"deleteTagTitle": "Radera tagg?",
"immediately": "Omedelbart",
"reEnterPassword": "Ange lösenord igen",
"reEnterPin": "Ange PIN-kod igen",

View File

@ -0,0 +1 @@
{}

View File

@ -21,12 +21,13 @@
"codeSecretKeyHint": "Khóa bí mật",
"codeAccountHint": "Tài khoản (bạn@miền.com)",
"codeTagHint": "Thẻ",
"accountKeyType": "Loại khóa",
"sessionExpired": "Phiên làm việc đã hết hạn",
"@sessionExpired": {
"description": "Title of the dialog when the users current session is invalid/expired"
},
"pleaseLoginAgain": "Vui lòng đăng nhập lại",
"loggingOut": "Đang thoát...",
"loggingOut": "Đang đăng xuất...",
"timeBasedKeyType": "Dựa trên thời gian (TOTP)",
"counterBasedKeyType": "Dựa trên bộ đếm (HOTP)",
"saveAction": "Lưu",
@ -74,7 +75,7 @@
"supportDevs": "Đăng ký <bold-green>ente</bold-green> để hỗ trợ dự án này.",
"supportDiscount": "Sử dụng mã giảm giá \"AUTH\" để được giảm 10% trong năm đầu tiên",
"changeEmail": "Thay đổi email",
"changePassword": "Thay đổi mật khẩu",
"changePassword": "Đổi mật khẩu",
"data": "Dữ liệu",
"importCodes": "Nhập mã",
"importTypePlainText": "Văn bản thuần",
@ -131,7 +132,7 @@
"faq_a_4": "Bạn có thể hỗ trợ sự phát triển của dự án này bằng cách đăng ký ứng dụng Ảnh @ ente.io của chúng tôi.",
"faq_q_5": "Làm sao để tôi bật FaceID trong ente",
"faq_a_5": "Bạn có thể bật khóa FaceID trong Cài đặt → Bảo mật → Màn hình khóa.",
"somethingWentWrongMessage": "Phát hiện có lỗi, xin thử lại",
"somethingWentWrongMessage": "Đã xảy ra lỗi, xin thử lại",
"leaveFamily": "Rời khỏi gia đình",
"leaveFamilyMessage": "Bạn có chắc chắn muốn thoát khỏi gói dành cho gia đình không?",
"inFamilyPlanMessage": "Bạn đang sử dụng gói dành cho gia đình!",
@ -141,7 +142,7 @@
"verify": "Xác minh",
"verifyEmail": "Xác nhận địa chỉ Email",
"enterCodeHint": "Nhập mã gồm 6 chữ số từ ứng dụng xác thực của bạn",
"lostDeviceTitle": "Bạn đã mất thiết bị?",
"lostDeviceTitle": "Mất thiết bị?",
"twoFactorAuthTitle": "Xác thực hai yếu tố",
"recoverAccount": "Khôi phục tài khoản",
"enterRecoveryKeyHint": "Nhập khóa khôi phục của bạn",
@ -154,6 +155,7 @@
}
}
},
"invalidQRCode": "Mã QR không hợp lệ",
"noRecoveryKeyTitle": "Không có khóa khôi phục?",
"enterEmailHint": "Nhập địa chỉ email của bạn",
"invalidEmailTitle": "Địa chỉ email không hợp lệ",
@ -177,6 +179,7 @@
"security": "Bảo mật",
"lockscreen": "Màn hình khoá",
"authToChangeLockscreenSetting": "Vui lòng xác thực để thay đổi cài đặt màn hình khóa",
"deviceLockEnablePreSteps": "Để bật khoá thiết bị, vui lòng thiết lập mật khẩu thiết bị hoặc khóa màn hình trong cài đặt hệ thống của bạn.",
"viewActiveSessions": "Xem danh sách phiên làm việc hiện tại",
"authToViewYourActiveSessions": "Vui lòng xác thực để xem danh sách phiên làm việc của bạn",
"searchHint": "Tìm kiếm...",
@ -195,6 +198,8 @@
"recoveryKeySaveDescription": "Chúng tôi không lưu trữ khóa này, vui lòng lưu khóa 24 từ này ở nơi an toàn.",
"doThisLater": "Để sau",
"saveKey": "Lưu khóa",
"save": "Lưu",
"send": "Gửi",
"back": "Quay lại",
"createAccount": "Tạo tài khoản",
"passwordStrength": "Độ mạnh mật khẩu: {passwordStrengthValue}",
@ -253,6 +258,8 @@
"exportLogs": "Xuất nhật ký",
"enterYourRecoveryKey": "Nhập khóa khôi phục của bạn",
"tempErrorContactSupportIfPersists": "Có vẻ như đã xảy ra sự cố. Vui lòng thử lại sau một thời gian. Nếu lỗi vẫn tiếp diễn, vui lòng liên hệ với nhóm hỗ trợ của chúng tôi.",
"networkHostLookUpErr": "Không thể kết nối đến Ente, vui lòng kiểm tra lại kết nối mạng. Nếu vẫn còn lỗi, xin vui lòng liên hệ hỗ trợ.",
"networkConnectionRefusedErr": "Không thể kết nối đến Ente, vui lòng thử lại sau. Nếu vẫn còn lỗi, xin vui lòng liên hệ hỗ trợ.",
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Có vẻ như đã xảy ra sự cố. Vui lòng thử lại sau một thời gian. Nếu lỗi vẫn tiếp diễn, vui lòng liên hệ với nhóm hỗ trợ của chúng tôi.",
"about": "Về chúng tôi",
"weAreOpenSource": "Chúng tôi có mã nguồn mở!",
@ -342,6 +349,7 @@
"deleteCodeAuthMessage": "Xác minh để xóa mã",
"showQRAuthMessage": "Xác minh để hiển thị mã QR",
"confirmAccountDeleteTitle": "Xác nhận xóa tài khoản",
"confirmAccountDeleteMessage": "Tài khoản này được liên kết với các ứng dụng Ente trên các nền tảng khác, nếu bạn có sử dụng.\n\nDữ liệu đã tải lên của bạn, trên mọi nền tảng, sẽ bị lên lịch xóa và tài khoản của bạn sẽ bị xóa vĩnh viễn.",
"androidBiometricHint": "Xác định danh tính",
"@androidBiometricHint": {
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
@ -402,11 +410,37 @@
"doNotSignOut": "Không được đăng xuất",
"hearUsWhereTitle": "Bạn biết đến Ente bằng cách nào? (không bắt buộc)",
"hearUsExplanation": "Chúng tôi không theo dõi lượt cài đặt ứng dụng. Sẽ rất hữu ích nếu bạn cho chúng tôi biết nơi bạn tìm thấy chúng tôi!",
"recoveryKeySaved": "Đã lưu khoá dự phòng vào thư mục Tải về!",
"waitingForVerification": "Đang chờ xác thực",
"loginSessionExpired": "Phiên làm việc hết hạn",
"loginSessionExpiredDetails": "Phiên làm việc hết hạn. Vui lòng đăng nhập lại.",
"developerSettings": "Cài đặt cho nhà phát triển",
"customEndpoint": "Đã kết nối đến",
"pinText": "Ghim",
"unpinText": "Bỏ ghim",
"tags": "Thẻ",
"createNewTag": "Tạo thẻ mới",
"tag": "Thẻ",
"create": "Tạo",
"editTag": "Sửa thẻ",
"deleteTagTitle": "Xóa thẻ?",
"deleteTagMessage": "Bạn có chắc chắn muốn xóa thẻ này không? Hành động này không thể đảo ngược.",
"updateNotAvailable": "Cập nhật không khả dụng",
"viewRawCodes": "Xem mã nguồn",
"rawCodes": "Mã nguồn",
"appLock": "Khóa ứng dụng",
"autoLock": "Tự động khóa",
"immediately": "Tức thì",
"reEnterPassword": "Nhập lại mật khẩu",
"reEnterPin": "Nhập lại mã PIN",
"next": "Tiếp",
"tooManyIncorrectAttempts": "Quá nhiều lần thử không chính xác",
"tapToUnlock": "Nhấn để mở khóa",
"setNewPassword": "Đặt lại mật khẩu",
"deviceLock": "Khóa thiết bị",
"hideContent": "Ẩn nội dung",
"hideContentDescriptionAndroid": "Ẩn nội dung khi chuyển ứng dụng và chặn chụp màn hình",
"hideContentDescriptioniOS": "Ẩn nội dung khi chuyển ứng dụng",
"pinLock": "Mã PIN",
"enterPin": "Nhập mã PIN",
"setNewPin": "Đổi mã PIN"

View File

@ -27,15 +27,15 @@
"description": "Title of the dialog when the users current session is invalid/expired"
},
"pleaseLoginAgain": "请重新登录",
"loggingOut": "正在退出登录...",
"timeBasedKeyType": "基于时间 (TOTP)",
"counterBasedKeyType": "基于计数器(HOTP)",
"loggingOut": "正在登出...",
"timeBasedKeyType": "基于时间 (TOTP)",
"counterBasedKeyType": "基于计数器 (HOTP)",
"saveAction": "保存",
"nextTotpTitle": "下一个",
"deleteCodeTitle": "要删除代码吗?",
"deleteCodeMessage": "您确定要删除此代码吗?此操作不可逆。",
"deleteCodeMessage": "您确定要删除此代码吗?此操作不可逆。",
"viewLogsAction": "查看日志",
"sendLogsDescription": "这将跨日志发送以帮助我们调试您的问题。 虽然我们采取预防措施以确保不记录敏感信息,但我们鼓励您在共享这些日志之前先查看它们。",
"sendLogsDescription": "这将发送日志以帮助我们调试您的问题。虽然我们采取预防措施确保不记录敏感信息,但我们建议您在共享这些日志之前先查看它们。",
"preparingLogsTitle": "正在准备日志...",
"emailLogsTitle": "电子邮件日志",
"emailLogsMessage": "请将日志发送至 {email}",
@ -67,13 +67,13 @@
"pleaseWait": "请稍候...",
"generatingEncryptionKeysTitle": "正在生成加密密钥...",
"recreatePassword": "重新创建密码",
"recreatePasswordMessage": "当前设备的强度不足以验证您的密码, 所以我们需要以一种能够与所有设备一起运行的方式重新生成它。 \n\n请使用您的恢复密钥登录并重新生成您的密码 (如果您愿意,您可以再次使用相同密钥)。",
"recreatePasswordMessage": "当前设备的功能不足以验证您的密码,因此我们需要以一种适用于所有设备的方式重新生成一次密码。\n\n请使用您的恢复密钥登录并重新生成您的密码如果您愿意可以再次使用相同的密码。",
"useRecoveryKey": "使用恢复密钥",
"incorrectPasswordTitle": "密码错误",
"welcomeBack": "欢迎回来!",
"madeWithLoveAtPrefix": "用❤️制成 ",
"supportDevs": "订阅 <bold-green>ente</bold-green> 以支持此项目。",
"supportDiscount": "使用优惠券号码“AUTH”获得第一年优惠10%的折扣",
"supportDiscount": "使用优惠码“AUTH”可享受首年 10% 折扣",
"changeEmail": "修改邮箱",
"changePassword": "修改密码",
"data": "数据",
@ -83,29 +83,29 @@
"passwordForDecryptingExport": "用来解密导出的密码",
"passwordEmptyError": "密码不能为空",
"importFromApp": "从 {appName} 导入代码",
"importGoogleAuthGuide": "使用“转移帐户”选项将您的帐户从 Google 身份验证器导出到二维码。然后使用另一台设备扫描二维码。\n\n提示您可以使用笔记本电脑的网络摄像头拍摄二维码的照片。",
"importGoogleAuthGuide": "使用“转移账户”选项将您的账户从 Google Authenticator 导出到二维码。然后使用另一台设备扫描二维码。\n\n提示您可以使用笔记本电脑的摄像头拍摄二维码的照片。",
"importSelectJsonFile": "选择 JSON 文件",
"importSelectAppExport": "选择 {appName} 的导出文件",
"importEnteEncGuide": "选择从 Ente 导出的 JSON 加密文件",
"importRaivoGuide": "使用 Raivo 设置中的“将 OTP 导出到 Zip 存档”选项。\n\n解压 zip 文件并导入 JSON 文件。",
"importBitwardenGuide": "使用 Bitwarden 工具中的“导出保管库”选项并导入未加密的 JSON 文件。",
"importAegisGuide": "在Aegis的设置中使用\"导出密码库\"选项。\n\n如果您的密码库已加密您需要输入密码才能解密密码库。",
"import2FasGuide": "使用 2FAS 中的“设置 -> 备份 - 导出”选项。\n\n如果您的备份已被加密则需要输入密码才能解密备份",
"importBitwardenGuide": "使用 Bitwarden 工具中的“导出密码库”选项并导入未加密的 JSON 文件。",
"importAegisGuide": "使用 Aegis 设置中的“导出密码库”选项。\n\n如果您的密码库已加密则需要输入密码库密码才能解密密码库。",
"import2FasGuide": "使用 2FAS 中的“设置 -> 备份 -> 导出”选项。\n\n如果您的备份已加密则需要输入密码来解密备份",
"importLastpassGuide": "使用 Lastpass Authenticator 设置中的“转移账户”选项,然后按“将账户导出到文件”。导入下载的 JSON。",
"exportCodes": "导出代码",
"importLabel": "导入",
"importInstruction": "请以以下格式选择包含代码列表的文件",
"importInstruction": "请选择一个包含以下格式的代码列表的文件",
"importCodeDelimiterInfo": "代码可以用逗号或新行分隔。",
"selectFile": "选择文件",
"emailVerificationToggle": "电子邮件验证",
"emailVerificationEnableWarning": "如果您将 2FA 存储到我们的电子邮件中,则打开电子邮件验证可能会导致僵局。如果您被一项服务锁定,您可能无法登录另一项服务。",
"emailVerificationEnableWarning": "为避免被锁在您的账户之外,请在启用电子邮件验证之前确保在 Ente Auth 之外存储电子邮件双重验证的副本。",
"authToChangeEmailVerificationSetting": "请进行身份验证以更改电子邮件验证",
"authToViewYourRecoveryKey": "请验证以查看您的恢复密钥",
"authToChangeYourEmail": "请验证以更改您的电子邮件",
"authToChangeYourPassword": "请验证以更改密码",
"authToViewSecrets": "请进行身份验证以查看您的密",
"authToViewSecrets": "请进行身份验证以查看您的密",
"authToInitiateSignIn": "请进行身份验证以启动登录进行备份。",
"ok": "好的",
"ok": "确定",
"cancel": "取消",
"yes": "是",
"no": "否",
@ -125,8 +125,8 @@
"faq": "常见问题",
"faq_q_1": "Auth 的安全性如何?",
"faq_a_1": "您通过 Auth 备份的所有代码均以端到端加密方式存储。这意味着只有您可以访问您的代码。我们的应用程序是开源的并且我们的加密技术已经过外部审计。",
"faq_q_2": "我可以在桌面上访问我的代码吗?",
"faq_a_2": "您可以在 web @auth.ente.io 上访问您的代码。",
"faq_q_2": "我可以在桌面设备上访问我的代码吗?",
"faq_a_2": "您可以在网页 auth.ente.io 上访问您的代码。",
"faq_q_3": "我如何删除代码?",
"faq_a_3": "您可以通过向左滑动该项目来删除该代码。",
"faq_q_4": "我该如何支持该项目?",
@ -240,9 +240,9 @@
"ackPasswordLostWarning": "我明白,如果我丢失密码,我可能会丢失我的数据,因为我的数据是 <underline>端到端加密的</underline>。",
"loginTerms": "点击登录后,我同意 <u-terms>服务条款</u-terms> 和 <u-policy>隐私政策</u-policy>",
"logInLabel": "登录",
"logout": "退出登录",
"areYouSureYouWantToLogout": "您确定要退出登录吗?",
"yesLogout": "是的,退出登陆",
"logout": "登出",
"areYouSureYouWantToLogout": "您确定要登出吗?",
"yesLogout": "是的,登出",
"exit": "退出",
"verifyingRecoveryKey": "正在验证恢复密钥...",
"recoveryKeyVerified": "恢复密钥已验证",
@ -299,7 +299,7 @@
"sorry": "抱歉",
"importFailureDesc": "无法解析选定的文件。\n如果您需要帮助请写入support@ente.io",
"pendingSyncs": "警告",
"pendingSyncsWarningBody": "您的一些代码尚未备份。\n\n请确保您在退出登录之前备份这些代码。",
"pendingSyncsWarningBody": "您的一些代码尚未备份。\n\n请确保您在登出之前备份这些代码。",
"checkInboxAndSpamFolder": "请检查您的收件箱 (或者是在您的“垃圾邮件”列表内) 以完成验证",
"tapToEnterCode": "点击以输入代码",
"resendEmail": "重新发送电子邮件",
@ -316,8 +316,8 @@
},
"activeSessions": "已登录的设备",
"somethingWentWrongPleaseTryAgain": "出了点问题,请重试",
"thisWillLogYouOutOfThisDevice": "这将使您在此设备上退出登录",
"thisWillLogYouOutOfTheFollowingDevice": "这将使您在以下设备中退出登录",
"thisWillLogYouOutOfThisDevice": "这将使您登出该设备",
"thisWillLogYouOutOfTheFollowingDevice": "这将使您登出以下设备",
"terminateSession": "是否终止会话?",
"terminate": "终止",
"thisDevice": "此设备",
@ -396,7 +396,7 @@
"@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": "生物识别身份验证已禁用。请锁定再解锁您的屏幕以启用它。",
"iOSLockOut": "生物识别身份验证已禁用。请锁定并解锁屏幕以启用该功能。",
"@iOSLockOut": {
"description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side."
},
@ -410,11 +410,11 @@
},
"noInternetConnection": "无互联网连接",
"pleaseCheckYourInternetConnectionAndTryAgain": "请检查您的互联网连接,然后重试。",
"signOutFromOtherDevices": "从其他设备退出登录",
"signOutOtherBody": "如果你认为有人可能知道你的密码,你可以强制所有使用你账户的其他设备退出登录。",
"signOutFromOtherDevices": "从其他设备登出",
"signOutOtherBody": "如果您认为有人可能知道您的密码,您可以强制所有其他使用您账户的设备登出。",
"signOutOtherDevices": "登出其他设备",
"doNotSignOut": "不要退登",
"hearUsWhereTitle": "您是如何知道Ente的 (可选的",
"doNotSignOut": "不要登",
"hearUsWhereTitle": "您是怎么知道 Ente 的?(可选",
"hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!",
"recoveryKeySaved": "恢复密钥已保存在下载文件夹中!",
"waitingForBrowserRequest": "正在等待浏览器请求...",

View File

@ -1,48 +1,56 @@
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/linear_progress_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class CodeTimerProgressCache {
static final Map<int, CodeTimerProgress> _cache = {};
static CodeTimerProgress getCachedWidget(int period) {
if (!_cache.containsKey(period)) {
_cache[period] = CodeTimerProgress(period: period);
}
return _cache[period]!;
}
}
class CodeTimerProgress extends StatefulWidget {
final int period;
CodeTimerProgress({
const CodeTimerProgress({
super.key,
required this.period,
});
@override
State createState() => _CodeTimerProgressState();
State<CodeTimerProgress> createState() => _CodeTimerProgressState();
}
class _CodeTimerProgressState extends State<CodeTimerProgress>
with SingleTickerProviderStateMixin {
late final Ticker _ticker;
double _progress = 0.0;
late final ValueNotifier<double> _progress;
late final int _microSecondsInPeriod;
@override
void initState() {
super.initState();
_microSecondsInPeriod = widget.period * 1000000;
_ticker = createTicker((elapsed) {
_updateTimeRemaining();
});
_progress = ValueNotifier<double>(0.0);
_ticker = createTicker(_updateTimeRemaining);
_ticker.start();
_updateTimeRemaining();
_updateTimeRemaining(Duration.zero);
}
void _updateTimeRemaining() {
int timeRemaining = (_microSecondsInPeriod) -
void _updateTimeRemaining(Duration elapsed) {
int timeRemaining = _microSecondsInPeriod -
(DateTime.now().microsecondsSinceEpoch % _microSecondsInPeriod);
setState(() {
_progress = (timeRemaining / _microSecondsInPeriod);
});
_progress.value = timeRemaining / _microSecondsInPeriod;
}
@override
void dispose() {
_ticker.dispose();
_progress.dispose();
super.dispose();
}
@ -50,12 +58,46 @@ class _CodeTimerProgressState extends State<CodeTimerProgress>
Widget build(BuildContext context) {
return SizedBox(
height: 3,
child: LinearProgressWidget(
color: _progress > 0.4
? getEnteColorScheme(context).primary700
: Colors.orange,
fractionOfStorage: _progress,
child: ValueListenableBuilder<double>(
valueListenable: _progress,
builder: (context, progress, _) {
return CustomPaint(
painter: _ProgressPainter(
progress: progress,
color: progress > 0.4
? getEnteColorScheme(context).primary700
: Colors.orange,
),
size: Size.infinite,
);
},
),
);
}
}
class _ProgressPainter extends CustomPainter {
final double progress;
final Color color;
_ProgressPainter({required this.progress, required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.fill;
final rect = RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width * progress, size.height),
const Radius.circular(2),
);
canvas.drawRRect(rect, paint);
}
@override
bool shouldRepaint(_ProgressPainter oldDelegate) {
return oldDelegate.progress != progress || oldDelegate.color != color;
}
}

View File

@ -49,6 +49,7 @@ class _CodeWidgetState extends State<CodeWidget> {
late bool _shouldShowLargeIcon;
late bool _hideCode;
bool isMaskingEnabled = false;
int _codeTimeStep = -1;
@override
void initState() {
@ -57,11 +58,22 @@ class _CodeWidgetState extends State<CodeWidget> {
_hideCode = isMaskingEnabled;
_everySecondTimer =
Timer.periodic(const Duration(milliseconds: 500), (Timer t) {
String newCode = _getCurrentOTP();
if (newCode != _currentCode.value) {
_currentCode.value = newCode;
if (widget.code.type.isTOTPCompatible) {
_nextCode.value = _getNextTotp();
int newStep = 0;
if (widget.code.type != Type.hotp) {
newStep = (((DateTime.now().millisecondsSinceEpoch ~/ 1000).round()) ~/
widget.code.period)
.floor();
} else {
newStep = widget.code.counter;
}
if (_codeTimeStep != newStep) {
_codeTimeStep = newStep;
String newCode = _getCurrentOTP();
if (newCode != _currentCode.value) {
_currentCode.value = newCode;
if (widget.code.type.isTOTPCompatible) {
_nextCode.value = _getNextTotp();
}
}
}
});
@ -111,8 +123,8 @@ class _CodeWidgetState extends State<CodeWidget> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (widget.code.type.isTOTPCompatible)
CodeTimerProgress(
period: widget.code.period,
CodeTimerProgressCache.getCachedWidget(
widget.code.period,
),
const SizedBox(height: 28),
Row(

View File

@ -55,6 +55,9 @@ class _HomePageState extends State<HomePage> {
final Logger _logger = Logger("HomePage");
final scaffoldKey = GlobalKey<ScaffoldState>();
// Used to request focus on the search box when clicked the search icon
late FocusNode searchBoxFocusNode;
final TextEditingController _textController = TextEditingController();
final bool _autoFocusSearch =
PreferenceService.instance.shouldAutoFocusOnSearchBar();
@ -89,6 +92,8 @@ class _HomePageState extends State<HomePage> {
setState(() {});
});
_showSearchBox = _autoFocusSearch;
searchBoxFocusNode = FocusNode();
}
void _loadCodes() {
@ -158,6 +163,9 @@ class _HomePageState extends State<HomePage> {
_triggerLogoutEvent?.cancel();
_iconsChangedEvent?.cancel();
_textController.removeListener(_applyFilteringAndRefresh);
searchBoxFocusNode.dispose();
super.dispose();
}
@ -241,6 +249,7 @@ class _HomePageState extends State<HomePage> {
border: InputBorder.none,
focusedBorder: InputBorder.none,
),
focusNode: searchBoxFocusNode,
),
centerTitle: true,
actions: <Widget>[
@ -258,6 +267,12 @@ class _HomePageState extends State<HomePage> {
_searchText = "";
} else {
_searchText = _textController.text;
// Request focus on the search box
// For Windows only for now. "Platform.isWindows" can be removed if other platforms has been tested.
if (Platform.isWindows) {
searchBoxFocusNode.requestFocus();
}
}
_applyFilteringAndRefresh();
},

View File

@ -4,6 +4,7 @@
- Improved date search, including support for day of week and hour of day.
- Fix video thumbnail generation and upload on Intel macOS.
- Club a photo and video into a live photo only if both are within 2 minutes.
- .
## v1.7.3

View File

@ -65,9 +65,9 @@ export const markClosableZip = (zipPath: string) => {
*/
export const clearOpenZipCache = () => {
if (_refCount.size > 0) {
const keys = JSON.stringify([..._refCount.keys()]);
const kvs = JSON.stringify([..._refCount.entries()]);
throw new Error(
`Attempting to clear zip file cache when some items are still in use: ${keys}`,
`Attempting to clear zip file cache when some items are still in use: ${kvs}`,
);
}
_cache.clear();

View File

@ -4,8 +4,7 @@
import { net, protocol } from "electron/main";
import { randomUUID } from "node:crypto";
import fs from "node:fs/promises";
import { Readable } from "node:stream";
import { ReadableStream } from "node:stream/web";
import { Writable } from "node:stream";
import { pathToFileURL } from "node:url";
import log from "./log";
import { ffmpegConvertToMP4 } from "./services/ffmpeg";
@ -120,20 +119,27 @@ const handleReadZip = async (zipPath: string, entryName: string) => {
return new Response("", { status: 404 });
}
// This returns an "old style" NodeJS.ReadableStream.
// zip.stream returns an "old style" NodeJS.ReadableStream. We then write it
// to the writable end of the web stream pipe, the readable end of which is
// relayed back to the renderer as the response.
const { writable, readable } = new TransformStream();
const stream = await zip.stream(entry);
// Convert it into a new style NodeJS.Readable.
const nodeReadable = new Readable({ emitClose: true }).wrap(stream);
// Then convert it into a Web stream.
const webReadableStreamAny = Readable.toWeb(nodeReadable);
// However, we get a ReadableStream<any> now. This doesn't go into the
// `BodyInit` expected by the Response constructor, which wants a
// ReadableStream<Uint8Array>. Force a cast.
const webReadableStream =
webReadableStreamAny as ReadableStream<Uint8Array>;
// Let go of the zip handle when the underlying stream closes.
nodeReadable.on("close", () => markClosableZip(zipPath));
const nodeWritable = Writable.fromWeb(writable);
stream.pipe(nodeWritable);
nodeWritable.on("error", (e: unknown) => {
// If the renderer process closes the network connection (say when it
// only needs the content-length and doesn't care about the body), we
// get an AbortError. Handle them here otherwise they litter the logs
// with unhandled exceptions.
if (e instanceof Error && e.name == "AbortError") return;
log.error("Error event for the writable end of zip stream", e);
});
nodeWritable.on("close", () => {
markClosableZip(zipPath);
});
// While it is documented that entry.time is the modification time,
// the units are not mentioned. By seeing the source code, we can
@ -142,8 +148,7 @@ const handleReadZip = async (zipPath: string, entryName: string) => {
// https://github.com/antelle/node-stream-zip/blob/master/node_stream_zip.js
const modifiedMs = entry.time;
// @ts-expect-error [Note: Node and web stream type mismatch]
return new Response(webReadableStream, {
return new Response(readable, {
headers: {
// We don't know the exact type, but it doesn't really matter, just
// set it to a generic binary content-type so that the browser

View File

@ -205,6 +205,15 @@ export const sidebar = [
},
],
},
{
text: "Troubleshooting",
items: [
{
text: "Windows login",
link: "/auth/troubleshooting/windows-login",
},
],
},
],
},
{

View File

@ -0,0 +1,43 @@
---
title: Unable to login on Windows Desktop
description:
Troubleshooting when you are not able to login or register on Ente Auth app on Windows
---
# Windows Login Error
### HandshakeException: Handshake error in client
This error usually happens when the Trusted Root certificates on your Windows machine are outdated.
To update the Trusted Root Certificates on Windows, you can use the `certutil` command. Here are the steps to do so:
1. **Open Command Prompt as Administrator**:
- Press `Windows + X` and select `Command Prompt (Admin)` or `Windows PowerShell (Admin)`.
2. **Run the following command to update the root certificates**:
```bash
certutil -generateSSTFromWU roots.sst
```
This command will generate a file named `roots.sst` that contains the latest root certificates from Windows Update.
3. **Install the new root certificates**:
```bash
certutil -addstore -f ROOT roots.sst
```
This command will add the certificates from the `roots.sst` file to the Trusted Root Certification Authorities store.
4. **Clean up**:
After the installation, you can delete the `roots.sst` file if you no longer need it:
```bash
del roots.sst
```
Make sure to restart your application after updating the certificates to ensure the changes take effect.
If the above steps don't resolve the issue, please follow [this guide](https://woshub.com/updating-trusted-root-certificates-in-windows-10/#h2_3) to update your trusted root certicates, and try again.

View File

@ -57,6 +57,26 @@ If you wish to collect photos from folks who are not Ente, you can do so with
our Links. Simply tick the box that says "Allow uploads", and anyone who has
access to the link will be able to add photos to your album.
## Organization
You can favorite items that have been shared with you, and organize them into
your own albums.
When you perform these operations, Ente will create a hard copy of these items,
that you fully own. This means, these copied items will count against your
storage space.
We understand there are use cases where this approach will consume extra space
(for eg. if you are organizing photos of a family member). We chose hard copies
as a first version to avoid complexities regarding the ownership of shared
items, in case the original owner were to delete it from their own library.
We plan to tackle these complexities in the future, by copying a reference to
the item that was shared, instead of the actual file, so that your storage will
only get consumed if the original owner deletes it from their library. If this
sounds useful to you, please participate in [this
discussion](https://github.com/ente-io/ente/discussions/790).
## Technical details
More details, including technical aspect about how the sharing features were

View File

@ -1,4 +1,4 @@
<resources>
<string name="app_name">ente debug</string>
<string name="app_name">Ente Debug</string>
<string name="backup">backup debug</string>
</resources>

View File

@ -1,4 +1,4 @@
<resources>
<string name="app_name">ente dev</string>
<string name="app_name">Ente Dev</string>
<string name="backup">backup dev</string>
</resources>

View File

@ -1,4 +1,4 @@
<resources>
<string name="app_name">ente face</string>
<string name="app_name">Ente Face</string>
<string name="backup">backup face</string>
</resources>

View File

@ -1,7 +1,8 @@
import Flutter
import UIKit
import AVFoundation
@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
@ -12,11 +13,36 @@ import UIKit
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
}
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let audioSessionChannel = FlutterMethodChannel(name: "io.ente.frame/audio_session",
binaryMessenger: controller.binaryMessenger)
audioSessionChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if call.method == "setAudioSessionCategory" {
self.setAudioSessionCategory(result: result)
} else {
result(FlutterMethodNotImplemented)
}
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func setAudioSessionCategory(result: @escaping FlutterResult) {
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers, .defaultToSpeaker])
try AVAudioSession.sharedInstance().setActive(true)
result(nil)
} catch {
result(FlutterError(code: "AUDIO_SESSION_ERROR",
message: "Failed to set audio session category",
details: error.localizedDescription))
}
}
override func applicationDidBecomeActive(_ application: UIApplication) {
signal(SIGPIPE, SIG_IGN)
}

View File

@ -0,0 +1,16 @@
import "package:flutter/services.dart";
import "package:logging/logging.dart";
class AudioSessionHandler {
static final _logger = Logger("AudioSessionHandler");
static const MethodChannel _channel =
MethodChannel('io.ente.frame/audio_session');
static Future<void> setAudioSessionCategory() async {
try {
await _channel.invokeMethod('setAudioSessionCategory');
} on PlatformException catch (e) {
_logger.warning("Failed to set audio session category: '${e.message}'.");
}
}
}

View File

@ -0,0 +1,10 @@
import "dart:collection";
import "package:photos/events/event.dart";
import "package:photos/models/backup/backup_item.dart";
class BackupUpdatedEvent extends Event {
final LinkedHashMap<String, BackupItem> items;
BackupUpdatedEvent(this.items);
}

View File

@ -390,6 +390,9 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Backup over mobile data"),
"backupSettings":
MessageLookupByLibrary.simpleMessage("Backup settings"),
"backupStatus": MessageLookupByLibrary.simpleMessage("Backup status"),
"backupStatusDescription": MessageLookupByLibrary.simpleMessage(
"Items that have been backed up will show up here"),
"backupVideos": MessageLookupByLibrary.simpleMessage("Backup videos"),
"blackFridaySale":
MessageLookupByLibrary.simpleMessage("Black Friday Sale"),

View File

@ -3172,6 +3172,26 @@ class S {
);
}
/// `Backup status`
String get backupStatus {
return Intl.message(
'Backup status',
name: 'backupStatus',
desc: '',
args: [],
);
}
/// `Items that have been backed up will show up here`
String get backupStatusDescription {
return Intl.message(
'Items that have been backed up will show up here',
name: 'backupStatusDescription',
desc: '',
args: [],
);
}
/// `Backup over mobile data`
String get backupOverMobileData {
return Intl.message(

View File

@ -12,7 +12,6 @@
"deleteAccountFeedbackPrompt": "Vi er kede af at du forlader os. Forklar venligst hvorfor, så vi kan forbedre os.",
"feedback": "Feedback",
"kindlyHelpUsWithThisInformation": "Hjælp os venligst med disse oplysninger",
"confirmDeletePrompt": "Ja, jeg ønsker at slette denne konto og alle dens data permanent.",
"confirmAccountDeletion": "Bekræft Sletning Af Konto",
"deleteAccountPermanentlyButton": "Slet konto permanent",
"yourAccountHasBeenDeleted": "Din konto er blevet slettet",

View File

@ -12,7 +12,7 @@
"deleteAccountFeedbackPrompt": "Wir bedauern sehr, dass du dein Konto löschen möchtest. Du würdest uns sehr helfen, wenn du uns kurz einige Gründe hierfür nennen könntest.",
"feedback": "Rückmeldung",
"kindlyHelpUsWithThisInformation": "Bitte gib diese Daten ein",
"confirmDeletePrompt": "Ja, ich möchte dieses Konto und alle enthaltenen Daten endgültig und unwiderruflich löschen.",
"confirmDeletePrompt": "Ja, ich möchte dieses Konto und alle enthaltenen Daten über alle Apps endgültig und unwiderruflich löschen.",
"confirmAccountDeletion": "Kontolöschung bestätigen",
"deleteAccountPermanentlyButton": "Konto unwiderruflich löschen",
"yourAccountHasBeenDeleted": "Dein Benutzerkonto wurde gelöscht",

View File

@ -453,6 +453,8 @@
"showMemories": "Show memories",
"yearsAgo": "{count, plural, one{{count} year ago} other{{count} years ago}}",
"backupSettings": "Backup settings",
"backupStatus": "Backup status",
"backupStatusDescription": "Items that have been backed up will show up here",
"backupOverMobileData": "Backup over mobile data",
"backupVideos": "Backup videos",
"disableAutoLock": "Disable auto lock",

View File

@ -12,7 +12,6 @@
"deleteAccountFeedbackPrompt": "Lamentamos que te vayas. Por favor, explícanos el motivo para ayudarnos a mejorar.",
"feedback": "Sugerencias",
"kindlyHelpUsWithThisInformation": "Por favor ayúdanos con esta información",
"confirmDeletePrompt": "Sí, quiero eliminar permanentemente esta cuenta y todos sus datos.",
"confirmAccountDeletion": "Confirmar borrado de cuenta",
"deleteAccountPermanentlyButton": "Eliminar cuenta permanentemente",
"yourAccountHasBeenDeleted": "Tu cuenta ha sido eliminada",

View File

@ -12,7 +12,6 @@
"deleteAccountFeedbackPrompt": "ما متاسفیم که می‌بینیم شما می‌روید. لطفا نظرات خود را برای کمک به بهبود ما به اشتراک بگذارید.",
"feedback": "بازخورد",
"kindlyHelpUsWithThisInformation": "لطفا با این اطلاعات به ما کمک کنید",
"confirmDeletePrompt": "بله، من می‌خواهم برای همیشه این حساب کاربری و تمام اطلاعات آن را حذف کنم.",
"confirmAccountDeletion": "تایید حذف حساب کاربری",
"deleteAccountPermanentlyButton": "حذف دائمی حساب کاربری",
"yourAccountHasBeenDeleted": "حساب کاربری شما حذف شده است",

View File

@ -12,7 +12,7 @@
"deleteAccountFeedbackPrompt": "Nous sommes désolés de vous voir partir. N'hésitez pas à partager vos commentaires pour nous aider à améliorer le service.",
"feedback": "Commentaires",
"kindlyHelpUsWithThisInformation": "Merci de nous aider avec cette information",
"confirmDeletePrompt": "Oui, je veux supprimer définitivement ce compte et toutes ses données.",
"confirmDeletePrompt": "Oui, je veux supprimer définitivement ce compte et ses données dans toutes les applications.",
"confirmAccountDeletion": "Confirmer la suppression du compte",
"deleteAccountPermanentlyButton": "Supprimer définitivement le compte",
"yourAccountHasBeenDeleted": "Votre compte a été supprimé",
@ -277,6 +277,7 @@
"change": "Modifier",
"unavailableReferralCode": "Désolé, ce code n'est pas disponible.",
"codeChangeLimitReached": "Désolé, vous avez atteint la limite de changements de code.",
"onlyFamilyAdminCanChangeCode": "Veuillez contacter {familyAdminEmail} pour modifier votre code.",
"storageInGB": "{storageAmountInGB} Go",
"claimed": "Réclamée",
"@claimed": {
@ -413,7 +414,13 @@
"photoGridSize": "Taille de la grille photo",
"manageDeviceStorage": "Gérer le stockage de l'appareil",
"machineLearning": "Apprentissage automatique",
"mlConsent": "Activer l'apprentissage automatique",
"mlConsentTitle": "Activer l'apprentissage automatique ?",
"mlConsentDescription": "Si vous activez l'apprentissage automatique, Ente extraira des informations comme la géométrie des visages, incluant les photos partagées avec vous. \nCela se fera sur votre appareil, avec un cryptage de bout-en-bout de toutes les données biométriques générées.",
"mlConsentPrivacy": "Veuillez cliquer ici pour plus de détails sur cette fonctionnalité dans notre politique de confidentialité",
"mlConsentConfirmation": "Je comprends, et souhaite activer l'apprentissage automatique",
"magicSearch": "Recherche magique",
"mlIndexingDescription": "Veuillez noter que l'apprentissage automatique entraînera une augmentation de l'utilisation de la bande passante et de la batterie, jusqu'à ce que tous les éléments soient indexés. \nEnvisagez d'utiliser l'application de bureau pour une indexation plus rapide, tous les résultats seront automatiquement synchronisés.",
"loadingModel": "Téléchargement des modèles...",
"waitingForWifi": "En attente de connexion Wi-Fi...",
"status": "État",
@ -489,6 +496,7 @@
"removeDuplicates": "Supprimer les doublons",
"removeDuplicatesDesc": "Examiner et supprimer les fichiers qui sont des doublons exacts.",
"viewLargeFiles": "Fichiers volumineux",
"viewLargeFilesDesc": "Afficher les fichiers qui consomment le plus de stockage.",
"noDuplicates": "✨ Aucun doublon",
"youveNoDuplicateFilesThatCanBeCleared": "Vous n'avez aucun fichier dédupliqué pouvant être nettoyé",
"success": "Succès",
@ -1145,6 +1153,7 @@
"successfullyHid": "Masquage réussi",
"successfullyUnhid": "Masquage réussi",
"crashReporting": "Rapports d'erreurs",
"resumableUploads": "Chargements à poursuivre",
"addToHiddenAlbum": "Ajouter à un album masqué",
"moveToHiddenAlbum": "Déplacer vers un album masqué",
"fileTypes": "Types de fichiers",
@ -1247,12 +1256,29 @@
"foundFaces": "Visages trouvés",
"clusteringProgress": "Progression du regroupement",
"indexingIsPaused": "L'indexation est en pause. Elle reprendra automatiquement lorsque l'appareil sera prêt.",
"trim": "Recadrer",
"crop": "Rogner",
"rotate": "Pivoter",
"left": "Gauche",
"right": "Droite",
"whatsNew": "Nouveautés",
"reviewSuggestions": "Consulter les suggestions",
"useAsCover": "Utiliser comme couverture",
"notPersonLabel": "Pas {name}?",
"@notPersonLabel": {
"description": "Label to indicate that the person in the photo is not the person whose name is mentioned",
"placeholders": {
"name": {
"content": "{name}",
"type": "String"
}
}
},
"enable": "Activer",
"enabled": "Activé",
"moreDetails": "Plus de détails",
"enableMLIndexingDesc": "Ente prend en charge l'apprentissage automatique sur l'appareil pour la reconnaissance faciale, la recherche magique et d'autres fonctionnalités de recherche avancée",
"magicSearchHint": "La recherche magique permet de rechercher des photos par leur contenu, par exemple 'fleur', 'voiture rouge', 'documents d'identité'",
"panorama": "Panorama",
"reenterPassword": "Ressaisir le mot de passe",
"reenterPin": "Ressaisir le code PIN",
@ -1278,6 +1304,8 @@
"pleaseSelectQuickLinksToRemove": "Veuillez sélectionner les liens rapides à supprimer",
"removePublicLinks": "Supprimer les liens publics",
"thisWillRemovePublicLinksOfAllSelectedQuickLinks": "Ceci supprimera les liens publics de tous les liens rapides sélectionnés.",
"guestView": "Vue invité",
"guestViewEnablePreSteps": "Pour activer la vue invité, veuillez configurer le code d'accès de l'appareil ou le verrouillage de l'écran dans les paramètres de votre système.",
"cl_guest_view_title": "Vue invité",
"cl_guest_view_description": "Montrer des photos à un ami en les transmettant sur votre téléphone ? Ne vous inquiétez pas si vous les faites glisser trop loin.\nLa vue \"invité\" les verrouillera dans les photos que vous avez sélectionnées.",
"cl_guest_view_call_to_action": "Sélectionnez les photos et fixez les en \"Vue Invité\".",
@ -1286,6 +1314,8 @@
"cl_video_player_title": "Lecteur vidéo",
"cl_video_player_description": "Intégration d'un nouveau lecteur vidéo, avec de meilleurs contrôles de lecture et la prise en charge des vidéos HDR.",
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Pour activer le verrouillage d'application, veuillez configurer le code d'accès de l'appareil ou le verrouillage de l'écran dans les paramètres de votre système.",
"appLockDescriptions": "Choisissez entre l'écran de verrouillage par défaut de votre appareil et un écran de verrouillage personnalisé avec un code PIN ou un mot de passe.",
"authToViewPasskey": "Veuillez vous authentifier pour afficher votre clé de récupération",
"loopVideoOn": "Loop video on",
"loopVideoOff": "Loop video off"
}

View File

@ -12,7 +12,6 @@
"deleteAccountFeedbackPrompt": "אנחנו מצטערים לראות שאתה עוזב. אנא תחלוק את המשוב שלך כדי לעזור לנו להשתפר.",
"feedback": "משוב",
"kindlyHelpUsWithThisInformation": "אנא עזור לנו עם המידע הזה",
"confirmDeletePrompt": "כן, אני רוצה למחוק לצמיתות את החשבון הזה וכל המידע שלו.",
"confirmAccountDeletion": "אשר את מחיקת החשבון",
"deleteAccountPermanentlyButton": "מחק את החשבון לצמיתות",
"yourAccountHasBeenDeleted": "החשבון שלך נמחק",

View File

@ -12,7 +12,6 @@
"deleteAccountFeedbackPrompt": "आपको जाता हुए देख कर हमें खेद है। कृपया हमें बेहतर बनने में सहायता के लिए अपनी प्रतिक्रिया साझा करें।",
"feedback": "प्रतिपुष्टि",
"kindlyHelpUsWithThisInformation": "कृपया हमें इस जानकारी के लिए सहायता करें",
"confirmDeletePrompt": "हां, मैं इस अकाउंट और इसके सभी डेटा को स्थायी रूप से हटाना चाहता/चाहती हूं।",
"confirmAccountDeletion": "अकाउंट डिलीट करने की पुष्टि करें",
"deleteAccountPermanentlyButton": "अकाउंट स्थायी रूप से डिलीट करें",
"yourAccountHasBeenDeleted": "आपका अकाउंट डिलीट कर दिया गया है",

View File

@ -12,7 +12,7 @@
"deleteAccountFeedbackPrompt": "Kami sedih kamu pergi. Silakan bagikan masukanmu agar kami bisa jadi lebih baik.",
"feedback": "Masukan",
"kindlyHelpUsWithThisInformation": "Harap bantu kami dengan informasi ini",
"confirmDeletePrompt": "Ya, saya ingin menghapus akun ini dan seluruh data yang terkait secara permanen.",
"confirmDeletePrompt": "Ya, saya ingin menghapus akun ini dan seluruh datanya secara permanen di semua aplikasi.",
"confirmAccountDeletion": "Konfirmasi Penghapusan Akun",
"deleteAccountPermanentlyButton": "Hapus Akun Secara Permanen",
"yourAccountHasBeenDeleted": "Akunmu telah dihapus",
@ -426,6 +426,7 @@
"status": "Status",
"indexedItems": "Item terindeks",
"pendingItems": "Item menunggu",
"clearIndexes": "Hapus indeks",
"selectFoldersForBackup": "Pilih folder yang perlu dicadangkan",
"selectedFoldersWillBeEncryptedAndBackedUp": "Folder yang terpilih akan dienkripsi dan dicadangkan",
"unselectAll": "Batalkan semua pilihan",
@ -437,11 +438,18 @@
"showMemories": "Lihat kenangan",
"yearsAgo": "{count, plural, other{{count} tahun lalu}}",
"backupSettings": "Pengaturan pencadangan",
"backupStatus": "Status pencadangan",
"backupStatusDescription": "Item yang sudah dicadangkan akan terlihat di sini",
"backupOverMobileData": "Cadangkan dengan data seluler",
"backupVideos": "Cadangkan video",
"disableAutoLock": "Nonaktifkan kunci otomatis",
"deviceLockExplanation": "Nonaktfikan kunci layar perangkat saat Ente berada di latar depan dan ada pencadangan yang sedang berlangsung. Hal ini biasanya tidak diperlukan, namun dapat membantu unggahan dan import awal berkas berkas besar selesai lebih cepat.",
"about": "Tentang",
"weAreOpenSource": "Kode kami sumber terbuka!",
"privacy": "Privasi",
"terms": "Ketentuan",
"checkForUpdates": "Periksa pembaruan",
"checkStatus": "Periksa status",
"checking": "Memeriksa...",
"youAreOnTheLatestVersion": "Kamu menggunakan versi terbaru",
"account": "Akun",
@ -458,10 +466,13 @@
"yesLogout": "Ya, keluar",
"aNewVersionOfEnteIsAvailable": "Versi baru dari Ente telah tersedia.",
"update": "Perbarui",
"installManually": "Instal secara manual",
"criticalUpdateAvailable": "Pembaruan penting tersedia",
"updateAvailable": "Pembaruan tersedia",
"ignoreUpdate": "Abaikan",
"downloading": "Mengunduh...",
"cannotDeleteSharedFiles": "Tidak dapat menghapus file berbagi",
"theDownloadCouldNotBeCompleted": "Unduhan tidak dapat diselesaikan",
"retry": "Coba lagi",
"backedUpFolders": "Folder yang dicadangkan",
"backup": "Pencadangan",
@ -472,8 +483,12 @@
"removeDuplicates": "Hapus duplikat",
"removeDuplicatesDesc": "Lihat dan hapus file yang sama persis.",
"viewLargeFiles": "File berukuran besar",
"viewLargeFilesDesc": "Tampilkan file yang banyak mengkonsumsi ruang penyimpanan.",
"noDuplicates": "✨ Tak ada file duplikat",
"youveNoDuplicateFilesThatCanBeCleared": "Kamu tidak memiliki file duplikat yang dapat di hapus",
"success": "Berhasil",
"rateUs": "Beri kami nilai",
"remindToEmptyDeviceTrash": "Kosongkan juga “Baru Saja Dihapus” dari “Pengaturan” -> “Penyimpanan” untuk mengklaim ruang yang baru dikosongkan",
"youHaveSuccessfullyFreedUp": "Kamu telah berhasil membersihkan {storageSaved}!",
"@youHaveSuccessfullyFreedUp": {
"description": "The text to display when the user has successfully freed up storage",
@ -485,6 +500,7 @@
}
}
},
"remindToEmptyEnteTrash": "Kosongkan juga \"Sampah\" untuk mendapatkan ruang yang baru dikosongkan",
"sparkleSuccess": "✨ Berhasil",
"duplicateFileCountWithStorageSaved": "Kamu telah menghapus {count, plural, other{{count} file duplikat}} dan membersihkan ({storageSaved}!)",
"@duplicateFileCountWithStorageSaved": {
@ -502,14 +518,18 @@
}
},
"familyPlans": "Paket keluarga",
"referrals": "Referensi",
"notifications": "Notifikasi",
"sharedPhotoNotifications": "Foto terbagi baru",
"sharedPhotoNotificationsExplanation": "Terima notifikasi apabila seseorang menambahkan foto ke album bersama yang kamu ikuti",
"advanced": "Lanjutan",
"general": "Umum",
"security": "Keamanan",
"authToViewYourRecoveryKey": "Harap autentikasi untuk melihat kunci pemulihan kamu",
"twofactor": "Autentikasi dua langkah",
"authToConfigureTwofactorAuthentication": "Harap autentikasi untuk mengatur autentikasi dua langkah",
"lockscreen": "Kunci layar",
"authToChangeLockscreenSetting": "Lakukan autentikasi untuk mengubah pengaturan kunci layar",
"viewActiveSessions": "Lihat sesi aktif",
"authToViewYourActiveSessions": "Harap autentikasi untuk melihat sesi aktif kamu",
"disableTwofactor": "Nonaktifkan autentikasi dua langkah",
@ -519,6 +539,7 @@
"social": "Sosial",
"rateUsOnStore": "Beri nilai di {storeName}",
"blog": "Blog",
"merchandise": "Barang Dagangan",
"twitter": "Twitter",
"mastodon": "Mastodon",
"matrix": "Matrix",
@ -549,6 +570,7 @@
"renewsOn": "Langganan akan diperpanjang pada {endDate}",
"freeTrialValidTill": "Percobaan gratis berlaku hingga {endDate}",
"validTill": "Berlaku hingga {endDate}",
"addOnValidTill": "Add-on {storageAmount} kamu berlaku sampai {endDate}",
"playStoreFreeTrialValidTill": "Percobaan gratis berlaku hingga {endDate}.\nKamu dapat memilih paket berbayar setelahnya.",
"subWillBeCancelledOn": "Langganan kamu akan dibatalkan pada {endDate}",
"subscription": "Langganan",
@ -576,18 +598,46 @@
},
"confirmPlanChange": "Konfirmasi perubahan paket",
"areYouSureYouWantToChangeYourPlan": "Apakah kamu yakin ingin mengubah paket kamu?",
"youCannotDowngradeToThisPlan": "Anda tidak dapat turun ke paket ini",
"cancelOtherSubscription": "Harap batalkan langganan kamu dari {paymentProvider} terlebih dahulu",
"@cancelOtherSubscription": {
"description": "The text to display when the user has an existing subscription from a different payment provider",
"type": "text",
"placeholders": {
"paymentProvider": {
"example": "Apple",
"type": "String"
}
}
},
"optionalAsShortAsYouLike": "Opsional, pendek pun tak apa...",
"send": "Kirim",
"askCancelReason": "Langganan kamu telah dibatalkan. Apakah kamu ingin membagikan alasannya?",
"thankYouForSubscribing": "Terima kasih telah berlangganan!",
"yourPurchaseWasSuccessful": "Pembelianmu berhasil",
"yourPlanWasSuccessfullyUpgraded": "Paket kamu berhasil ditingkatkan",
"yourPlanWasSuccessfullyDowngraded": "Paket kamu berhasil di turunkan",
"yourSubscriptionWasUpdatedSuccessfully": "Langgananmu telah berhasil diperbarui",
"googlePlayId": "ID Google Play",
"appleId": "ID Apple",
"playstoreSubscription": "Langganan PlayStore",
"appstoreSubscription": "Langganan AppStore",
"subAlreadyLinkedErrMessage": "{id} kamu telah terhubung dengan akun Ente lain.\nJika kamu ingin menggunakan {id} kamu untuk akun ini, silahkan hubungi tim bantuan kami",
"visitWebToManage": "Silakan buka web.ente.io untuk mengatur langgananmu",
"couldNotUpdateSubscription": "Tidak dapat memperbarui langganan",
"pleaseContactSupportAndWeWillBeHappyToHelp": "Silakan hubungi support@ente.io dan kami akan dengan senang hati membantu!",
"paymentFailed": "Pembayaran gagal",
"paymentFailedTalkToProvider": "Harap hubungi dukungan {providerName} jika kamu dikenai biaya",
"@paymentFailedTalkToProvider": {
"description": "The text to display when the payment failed",
"type": "text",
"placeholders": {
"providerName": {
"example": "AppStore|PlayStore",
"type": "String"
}
}
},
"continueOnFreeTrial": "Lanjut dengan percobaan gratis",
"areYouSureYouWantToExit": "Apakah kamu yakin ingin keluar?",
"thankYou": "Terima kasih",
@ -601,8 +651,10 @@
"leave": "Tinggalkan",
"rateTheApp": "Nilai app ini",
"startBackup": "Mulai pencadangan",
"noPhotosAreBeingBackedUpRightNow": "Tidak ada foto yang sedang dicadangkan sekarang",
"grantFullAccessPrompt": "Harap berikan akses ke semua foto di app Pengaturan",
"openSettings": "Buka Pengaturan",
"selectMorePhotos": "Pilih lebih banyak foto",
"existingUser": "Masuk",
"privateBackups": "Cadangan pribadi",
"forYourMemories": "untuk kenanganmu",
@ -614,6 +666,7 @@
"everywhere": "di mana saja",
"androidIosWebDesktop": "Android, iOS, Web, Desktop",
"mobileWebDesktop": "Seluler, Web, Desktop",
"newToEnte": "Baru di Ente",
"pleaseLoginAgain": "Silakan masuk akun lagi",
"autoLogoutMessage": "Akibat kesalahan teknis, kamu telah keluar dari akunmu. Kami mohon maaf atas ketidaknyamanannya.",
"yourSubscriptionHasExpired": "Langgananmu telah berakhir",
@ -848,6 +901,9 @@
"networkHostLookUpErr": "Tidak dapat terhubung dengan Ente, harap periksa pengaturan jaringan kamu dan hubungi dukungan jika masalah berlanjut.",
"networkConnectionRefusedErr": "Tidak dapat terhubung dengan Ente, silakan coba lagi setelah beberapa saat. Jika masalah berlanjut, harap hubungi dukungan.",
"cachedData": "Data cache",
"remoteThumbnails": "Thumbnail jarak jauh",
"pendingSync": "Sinkronisasi yang tertunda",
"localGallery": "Galeri lokal",
"todaysLogs": "Log hari ini",
"viewLogs": "Lihat log",
"preparingLogs": "Menyiapkan log...",

View File

@ -12,7 +12,7 @@
"deleteAccountFeedbackPrompt": "Ci dispiace vederti andare via. Facci sapere se hai bisogno di aiuto o se vuoi aiutarci a migliorare.",
"feedback": "Suggerimenti",
"kindlyHelpUsWithThisInformation": "Aiutaci con queste informazioni",
"confirmDeletePrompt": "Sì, voglio eliminare definitivamente questo account e tutti i suoi dati.",
"confirmDeletePrompt": "Sì, voglio eliminare definitivamente questo account e i dati associati a esso su tutte le applicazioni.",
"confirmAccountDeletion": "Conferma eliminazione account",
"deleteAccountPermanentlyButton": "Cancella definitivamente il tuo account",
"yourAccountHasBeenDeleted": "Il tuo account è stato eliminato",
@ -24,6 +24,7 @@
"sendEmail": "Invia email",
"deleteRequestSLAText": "La tua richiesta verrà elaborata entro 72 ore.",
"deleteEmailRequest": "Invia un'email a <warning>account-deletion@ente.io</warning> dal tuo indirizzo email registrato.",
"entePhotosPerm": "Ente <i>necessita del permesso per</i> preservare le tue foto",
"ok": "Ok",
"createAccount": "Crea account",
"createNewAccount": "Crea un nuovo account",
@ -225,14 +226,17 @@
},
"description": "Number of participants in an album, including the album owner."
},
"collabLinkSectionDescription": "Crea un link per consentire alle persone di aggiungere e visualizzare foto nel tuo album condiviso senza bisogno di un'applicazione o di un account Ente. Ottimo per raccogliere foto di un evento.",
"collectPhotos": "Raccogli le foto",
"collaborativeLink": "Link collaborativo",
"shareWithNonenteUsers": "Condividi con utenti che non hanno un account Ente",
"createPublicLink": "Crea link pubblico",
"sendLink": "Invia link",
"copyLink": "Copia link",
"linkHasExpired": "Il link è scaduto",
"publicLinkEnabled": "Link pubblico abilitato",
"shareALink": "Condividi un link",
"sharedAlbumSectionDescription": "Crea album condivisi e collaborativi con altri utenti di Ente, inclusi gli utenti con piani gratuiti.",
"shareWithPeopleSectionTitle": "{numberOfPeople, plural, =0 {Condividi con persone specifiche} =1 {Condividi con una persona} other {Condividi con {numberOfPeople} persone}}",
"@shareWithPeopleSectionTitle": {
"placeholders": {
@ -256,10 +260,12 @@
},
"verificationId": "ID di verifica",
"verifyEmailID": "Verifica {email}",
"emailNoEnteAccount": "{email} non ha un account Ente.\n\nInvia un invito per condividere foto.",
"shareMyVerificationID": "Ecco il mio ID di verifica: {verificationID} per ente.io.",
"shareTextConfirmOthersVerificationID": "Hey, puoi confermare che questo è il tuo ID di verifica: {verificationID} su ente.io",
"somethingWentWrong": "Qualcosa è andato storto",
"sendInvite": "Invita",
"shareTextRecommendUsingEnte": "Scarica Ente in modo da poter facilmente condividere foto e video in qualità originale\n\nhttps://ente.io",
"done": "Completato",
"applyCodeTitle": "Applica codice",
"enterCodeDescription": "Inserisci il codice fornito dal tuo amico per richiedere spazio gratuito per entrambi",
@ -267,6 +273,11 @@
"failedToApplyCode": "Impossibile applicare il codice",
"enterReferralCode": "Inserisci il codice di invito",
"codeAppliedPageTitle": "Codice applicato",
"changeYourReferralCode": "Cambia il tuo codice invito",
"change": "Cambia",
"unavailableReferralCode": "Siamo spiacenti, questo codice non è disponibile.",
"codeChangeLimitReached": "Siamo spiacenti, hai raggiunto il limite di modifiche del codice.",
"onlyFamilyAdminCanChangeCode": "Per favore contatta {familyAdminEmail} per cambiare il tuo codice.",
"storageInGB": "{storageAmountInGB} GB",
"claimed": "Riscattato",
"@claimed": {
@ -276,6 +287,7 @@
"claimMore": "Richiedine di più!",
"theyAlsoGetXGb": "Anche loro riceveranno {storageAmountInGB} GB",
"freeStorageOnReferralSuccess": "{storageAmountInGB} GB ogni volta che qualcuno si iscrive a un piano a pagamento e applica il tuo codice",
"shareTextReferralCode": "Codice invito Ente: {referralCode} \n\nInseriscilo in Impostazioni → Generali → Inviti per ottenere {referralStorageInGB} GB gratis dopo la sottoscrizione a un piano a pagamento\n\nhttps://ente.io",
"claimFreeStorage": "Richiedi spazio gratuito",
"inviteYourFriends": "Invita i tuoi amici",
"failedToFetchReferralDetails": "Impossibile recuperare i dettagli. Per favore, riprova più tardi.",
@ -298,6 +310,7 @@
}
},
"faq": "FAQ",
"help": "Aiuto",
"oopsSomethingWentWrong": "Oops! Qualcosa è andato storto",
"peopleUsingYourCode": "Persone che hanno usato il tuo codice",
"eligible": "idoneo",
@ -327,6 +340,7 @@
"removeParticipantBody": "{userEmail} verrà rimosso da questo album condiviso\n\nQualsiasi foto aggiunta dall'utente verrà rimossa dall'album",
"keepPhotos": "Mantieni foto",
"deletePhotos": "Elimina foto",
"inviteToEnte": "Invita su Ente",
"removePublicLink": "Rimuovi link pubblico",
"disableLinkMessage": "Questo rimuoverà il link pubblico per accedere a \"{albumName}\".",
"sharing": "Condivisione in corso...",
@ -342,7 +356,10 @@
"videoSmallCase": "video",
"photoSmallCase": "foto",
"singleFileDeleteHighlight": "Verrà eliminato da tutti gli album.",
"singleFileInBothLocalAndRemote": "Questo {fileType} è sia su Ente che sul tuo dispositivo.",
"singleFileInRemoteOnly": "Questo {fileType} verrà eliminato da Ente.",
"singleFileDeleteFromDevice": "Questo {fileType} verrà eliminato dal tuo dispositivo.",
"deleteFromEnte": "Elimina da Ente",
"yesDelete": "Sì, elimina",
"movedToTrash": "Spostato nel cestino",
"deleteFromDevice": "Elimina dal dispositivo",
@ -396,6 +413,10 @@
},
"photoGridSize": "Dimensione griglia foto",
"manageDeviceStorage": "Gestisci memoria dispositivo",
"mlConsentDescription": "Se abiliti il Machine Learning, Ente estrarrà informazioni come la geometria del volto dai file, inclusi quelli condivisi con te.\n\nQuesto accadrà sul tuo dispositivo, e qualsiasi informazione biometrica generata sarà crittografata end-to-end.",
"mlConsentPrivacy": "Clicca qui per maggiori dettagli su questa funzione nella nostra informativa sulla privacy",
"mlIndexingDescription": "Si prega di notare che l'attivazione dell'apprendimento automatico si tradurrà in un maggior utilizzo della connessione e della batteria fino a quando tutti gli elementi non saranno indicizzati. Valuta di utilizzare l'applicazione desktop per un'indicizzazione più veloce, tutti i risultati verranno sincronizzati automaticamente.",
"loadingModel": "Scaricamento modelli...",
"waitingForWifi": "In attesa del WiFi...",
"status": "Stato",
"indexedItems": "Elementi indicizzati",
@ -430,11 +451,13 @@
"backupOverMobileData": "Backup su dati mobili",
"backupVideos": "Backup dei video",
"disableAutoLock": "Disabilita blocco automatico",
"deviceLockExplanation": "Disabilita il blocco schermo del dispositivo quando Ente è in primo piano e c'è un backup in corso. Questo normalmente non è necessario ma può aiutare a completare più velocemente grossi caricamenti e l'importazione iniziale di grandi librerie.",
"about": "Info",
"weAreOpenSource": "Siamo open source!",
"privacy": "Privacy",
"terms": "Termini d'uso",
"checkForUpdates": "Controlla aggiornamenti",
"checkStatus": "Verifica stato",
"checking": "Controllo in corso...",
"youAreOnTheLatestVersion": "Stai utilizzando l'ultima versione",
"account": "Account",
@ -449,6 +472,7 @@
"authToInitiateAccountDeletion": "Autenticati per avviare l'eliminazione dell'account",
"areYouSureYouWantToLogout": "Sei sicuro di volerti disconnettere?",
"yesLogout": "Sì, disconnetti",
"aNewVersionOfEnteIsAvailable": "Una nuova versione di Ente è disponibile.",
"update": "Aggiorna",
"installManually": "Installa manualmente",
"criticalUpdateAvailable": "Un aggiornamento importante è disponibile",
@ -461,9 +485,13 @@
"backedUpFolders": "Cartelle salvate",
"backup": "Backup",
"freeUpDeviceSpace": "Libera spazio",
"freeUpDeviceSpaceDesc": "Risparmia spazio sul tuo dispositivo cancellando i file che sono già stati salvati online.",
"allClear": "✨ Tutto pulito",
"noDeviceThatCanBeDeleted": "Non hai file su questo dispositivo che possono essere eliminati",
"removeDuplicates": "Rimuovi i doppioni",
"removeDuplicatesDesc": "Verifica e rimuovi i file che sono esattamente duplicati.",
"viewLargeFiles": "File di grandi dimensioni",
"viewLargeFilesDesc": "Visualizza i file che stanno occupando la maggior parte dello spazio di archiviazione.",
"noDuplicates": "✨ Nessun doppione",
"youveNoDuplicateFilesThatCanBeCleared": "Non hai file duplicati che possono essere cancellati",
"success": "Operazione riuscita",
@ -536,6 +564,7 @@
"systemTheme": "Sistema",
"freeTrial": "Prova gratuita",
"selectYourPlan": "Seleziona un piano",
"enteSubscriptionPitch": "Ente conserva i tuoi ricordi in modo che siano sempre a disposizione, anche se perdi il tuo dispositivo.",
"enteSubscriptionShareWithFamily": "Aggiungi la tua famiglia al tuo piano.",
"currentUsageIs": "Spazio attualmente utilizzato ",
"@currentUsageIs": {
@ -549,6 +578,8 @@
"renewsOn": "Si rinnova il {endDate}",
"freeTrialValidTill": "La prova gratuita termina il {endDate}",
"validTill": "Valido fino al {endDate}",
"addOnValidTill": "Il tuo spazio aggiuntivo di {storageAmount} è valido fino al {endDate}",
"playStoreFreeTrialValidTill": "Prova gratuita valida fino al {endDate}.\nIn seguito potrai scegliere un piano a pagamento.",
"subWillBeCancelledOn": "L'abbonamento verrà cancellato il {endDate}",
"subscription": "Abbonamento",
"paymentDetails": "Dettagli di Pagamento",
@ -599,6 +630,7 @@
"appleId": "Apple ID",
"playstoreSubscription": "Abbonamento su PlayStore",
"appstoreSubscription": "abbonamento AppStore",
"subAlreadyLinkedErrMessage": "Il tuo {id} è già collegato a un altro account Ente.\nSe desideri utilizzare il tuo {id} con questo account, per favore contatta il nostro supporto''",
"visitWebToManage": "Visita web.ente.io per gestire il tuo abbonamento",
"couldNotUpdateSubscription": "Impossibile aggiornare l'abbonamento",
"pleaseContactSupportAndWeWillBeHappyToHelp": "Contatta support@ente.io e saremo felici di aiutarti!",
@ -619,6 +651,7 @@
"thankYou": "Grazie",
"failedToVerifyPaymentStatus": "Impossibile verificare lo stato del pagamento",
"pleaseWaitForSometimeBeforeRetrying": "Riprova tra qualche minuto",
"paymentFailedMessage": "Purtroppo il tuo pagamento non è riuscito. Contatta l'assistenza e ti aiuteremo!",
"youAreOnAFamilyPlan": "Sei un utente con piano famiglia!",
"contactFamilyAdmin": "Contatta <green>{familyAdminEmail}</green> per gestire il tuo abbonamento",
"leaveFamily": "Abbandona il piano famiglia",
@ -642,7 +675,9 @@
"everywhere": "ovunque",
"androidIosWebDesktop": "Android, iOS, Web, Desktop",
"mobileWebDesktop": "Mobile, Web, Desktop",
"newToEnte": "Prima volta con Ente",
"pleaseLoginAgain": "Effettua nuovamente l'accesso",
"autoLogoutMessage": "A causa di problemi tecnici, sei stato disconnesso. Ci scusiamo per l'inconveniente.",
"yourSubscriptionHasExpired": "Il tuo abbonamento è scaduto",
"storageLimitExceeded": "Limite d'archiviazione superato",
"upgrade": "Acquista altro spazio",
@ -653,10 +688,12 @@
},
"backupFailed": "Backup fallito",
"couldNotBackUpTryLater": "Impossibile eseguire il backup dei tuoi dati.\nRiproveremo più tardi.",
"enteCanEncryptAndPreserveFilesOnlyIfYouGrant": "Ente può criptare e conservare i file solo se gliene concedi l'accesso",
"pleaseGrantPermissions": "Concedi i permessi",
"grantPermission": "Concedi il permesso",
"privateSharing": "Condivisioni private",
"shareOnlyWithThePeopleYouWant": "Condividi solo con le persone che vuoi",
"usePublicLinksForPeopleNotOnEnte": "Usa link pubblici per persone non registrate su Ente",
"allowPeopleToAddPhotos": "Permetti alle persone di aggiungere foto",
"shareAnAlbumNow": "Condividi un album",
"collectEventPhotos": "Raccogli le foto di un evento",
@ -679,6 +716,21 @@
"deleteEmptyAlbumsWithQuestionMark": "Eliminare gli album vuoti?",
"deleteAlbumsDialogBody": "Questo eliminerà tutti gli album vuoti. È utile quando si desidera ridurre l'ingombro nella lista degli album.",
"deleteProgress": "Eliminazione di {currentlyDeleting} / {totalCount}",
"genericProgress": "Elaborazione {currentlyProcessing} / {totalCount}",
"@genericProgress": {
"description": "Generic progress text to display when processing multiple items",
"type": "text",
"placeholders": {
"currentlyProcessing": {
"example": "1",
"type": "int"
},
"totalCount": {
"example": "10",
"type": "int"
}
}
},
"permanentlyDelete": "Elimina definitivamente",
"canOnlyCreateLinkForFilesOwnedByYou": "Puoi creare solo link per i file di tua proprietà",
"publicLinkCreated": "Link pubblico creato",
@ -693,11 +745,13 @@
"unhide": "Mostra",
"unarchive": "Rimuovi dall'archivio",
"favorite": "Preferito",
"removeFromFavorite": "Rimuovi dai preferiti",
"shareLink": "Condividi link",
"createCollage": "Crea un collage",
"saveCollage": "Salva il collage",
"collageSaved": "Collage salvato nella galleria",
"collageLayout": "Disposizione",
"addToEnte": "Aggiungi a Ente",
"addToAlbum": "Aggiungi all'album",
"delete": "Cancella",
"hide": "Nascondi",
@ -762,7 +816,10 @@
"photosAddedByYouWillBeRemovedFromTheAlbum": "Le foto aggiunte da te verranno rimosse dall'album",
"youveNoFilesInThisAlbumThatCanBeDeleted": "Non hai file in questo album che possono essere eliminati",
"youDontHaveAnyArchivedItems": "Non hai nulla di archiviato.",
"ignoredFolderUploadReason": "Alcuni file in questo album vengono ignorati dal caricamento perché erano stati precedentemente eliminati da Ente.",
"resetIgnoredFiles": "Ripristina i file ignorati",
"deviceFilesAutoUploading": "I file aggiunti a questo album del dispositivo verranno automaticamente caricati su Ente.",
"turnOnBackupForAutoUpload": "Attiva il backup per caricare automaticamente i file aggiunti a questa cartella del dispositivo su Ente.",
"noHiddenPhotosOrVideos": "Nessuna foto o video nascosti",
"toHideAPhotoOrVideo": "Per nascondere una foto o un video",
"openTheItem": "• Apri la foto o il video",
@ -788,6 +845,7 @@
"close": "Chiudi",
"setAs": "Imposta come",
"fileSavedToGallery": "File salvato nella galleria",
"filesSavedToGallery": "File salvati nella galleria",
"fileFailedToSaveToGallery": "Impossibile salvare il file nella galleria",
"download": "Scarica",
"pressAndHoldToPlayVideo": "Tieni premuto per riprodurre il video",
@ -890,6 +948,7 @@
"renameFile": "Rinomina file",
"enterFileName": "Inserisci un nome per il file",
"filesDeleted": "File eliminati",
"selectedFilesAreNotOnEnte": "I file selezionati non sono su Ente",
"thisActionCannotBeUndone": "Questa azione non può essere annullata",
"emptyTrash": "Vuoi svuotare il cestino?",
"permDeleteWarning": "Tutti gli elementi nel cestino verranno eliminati definitivamente\n\nQuesta azione non può essere annullata",
@ -898,6 +957,7 @@
"permanentlyDeleteFromDevice": "Eliminare definitivamente dal dispositivo?",
"someOfTheFilesYouAreTryingToDeleteAre": "Alcuni dei file che si sta tentando di eliminare sono disponibili solo sul dispositivo e non possono essere recuperati se cancellati",
"theyWillBeDeletedFromAllAlbums": "Verranno eliminati da tutti gli album.",
"someItemsAreInBothEnteAndYourDevice": "Alcuni elementi sono sia su Ente che sul tuo dispositivo.",
"selectedItemsWillBeDeletedFromAllAlbumsAndMoved": "Gli elementi selezionati verranno eliminati da tutti gli album e spostati nel cestino.",
"theseItemsWillBeDeletedFromYourDevice": "Questi file verranno eliminati dal tuo dispositivo.",
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Sembra che qualcosa sia andato storto. Riprova tra un po'. Se l'errore persiste, contatta il nostro team di supporto.",
@ -933,11 +993,17 @@
"loadMessage7": "Le nostre app per smartphone vengono eseguite in background per crittografare e eseguire il backup di qualsiasi nuova foto o video",
"loadMessage8": "web.ente.io ha un uploader intuitivo",
"loadMessage9": "Usiamo Xchacha20Poly1305 per crittografare in modo sicuro i tuoi dati",
"photoDescriptions": "Descrizioni delle foto",
"fileTypesAndNames": "Tipi e nomi di file",
"location": "Luogo",
"moments": "Momenti",
"searchFaceEmptySection": "Le persone saranno mostrate qui una volta completata l'indicizzazione",
"searchDatesEmptySection": "Ricerca per data, mese o anno",
"searchLocationEmptySection": "Raggruppa foto scattate entro un certo raggio da una foto",
"searchPeopleEmptySection": "Invita persone e vedrai qui tutte le foto condivise da loro",
"searchAlbumsEmptySection": "Album",
"searchFileTypesAndNamesEmptySection": "Tipi e nomi di file",
"searchCaptionEmptySection": "Aggiungi descrizioni come \"#viaggio\" nelle informazioni delle foto per trovarle rapidamente qui",
"language": "Lingua",
"selectLanguage": "Seleziona una lingua",
"locationName": "Nome della località",
@ -986,6 +1052,7 @@
"@storageUsageInfo": {
"description": "Example: 1.2 GB of 2 GB used or 100 GB or 2TB used"
},
"availableStorageSpace": "{freeAmount} {storageUnit} liberi",
"appVersion": "Versione: {versionValue}",
"verifyIDLabel": "Verifica",
"fileInfoAddDescHint": "Aggiungi descrizione...",
@ -996,6 +1063,7 @@
},
"setRadius": "Imposta raggio",
"familyPlanPortalTitle": "Famiglia",
"familyPlanOverview": "Aggiungi 5 membri della famiglia al tuo piano esistente senza pagare extra.\n\nOgni membro ottiene il proprio spazio privato e non può vedere i file dell'altro a meno che non siano condivisi.\n\nI piani familiari sono disponibili per i clienti che hanno un abbonamento Ente a pagamento.\n\nIscriviti ora per iniziare!",
"androidBiometricHint": "Verifica l'identità",
"@androidBiometricHint": {
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
@ -1073,21 +1141,43 @@
"noAlbumsSharedByYouYet": "Ancora nessun album condiviso da te",
"sharedWithYou": "Condivise con te",
"sharedByYou": "Condivise da te",
"inviteYourFriendsToEnte": "Invita i tuoi amici a Ente",
"failedToDownloadVideo": "Download del video non riuscito",
"hiding": "Nascondendo...",
"unhiding": "Rimuovendo dal nascondiglio...",
"successfullyHid": "Nascosta con successo",
"successfullyUnhid": "Rimossa dal nascondiglio con successo",
"crashReporting": "Segnalazione di crash",
"resumableUploads": "Caricamenti riattivabili",
"addToHiddenAlbum": "Aggiungi ad album nascosto",
"moveToHiddenAlbum": "Sposta in album nascosto",
"fileTypes": "Tipi di file",
"hearUsWhereTitle": "Come hai sentito parlare di Ente? (opzionale)",
"hearUsExplanation": "Non teniamo traccia del numero di installazioni dell'app. Sarebbe utile se ci dicesse dove ci ha trovato!",
"viewAddOnButton": "Visualizza componenti aggiuntivi",
"addOns": "Componenti aggiuntivi",
"addOnPageSubtitle": "Dettagli dei componenti aggiuntivi",
"yourMap": "La tua mappa",
"modifyYourQueryOrTrySearchingFor": "Modifica la tua interrogazione o prova a cercare",
"blackFridaySale": "Offerta del Black Friday",
"photos": "Foto",
"videos": "Video",
"searchHint3": "Album, nomi di file e tipi",
"searchHint4": "Luogo",
"addYourPhotosNow": "Aggiungi le tue foto ora",
"searchResultCount": "{count, plural, one{{count} risultato trovato} other{{count} risultati trovati}}",
"@searchResultCount": {
"description": "Text to tell user how many results were found for their search query",
"placeholders": {
"count": {
"example": "1|2|3",
"type": "int"
}
}
},
"faces": "Volti",
"people": "Persone",
"contents": "Contenuti",
"addNew": "Aggiungi nuovo",
"@addNew": {
"description": "Text to add a new item (location tag, album, caption etc)"
@ -1104,6 +1194,29 @@
"selectALocationFirst": "Scegli prima una posizione",
"changeLocationOfSelectedItems": "Cambiare la posizione degli elementi selezionati?",
"editsToLocationWillOnlyBeSeenWithinEnte": "Le modifiche alla posizione saranno visibili solo all'interno di Ente",
"waitingForVerification": "In attesa di verifica...",
"passkey": "Passkey",
"passkeyAuthTitle": "Verifica della passkey",
"passKeyPendingVerification": "La verifica è ancora in corso",
"loginSessionExpired": "Sessione scaduta",
"loginSessionExpiredDetails": "La sessione è scaduta. Si prega di accedere nuovamente.",
"verifyPasskey": "Verifica passkey",
"playOnTv": "Riproduci album sulla TV",
"pair": "Abbina",
"deviceNotFound": "Dispositivo non trovato",
"castInstruction": "Visita cast.ente.io sul dispositivo che vuoi abbinare.\n\nInserisci il codice qui sotto per riprodurre l'album sulla tua TV.",
"deviceCodeHint": "Inserisci il codice",
"joinDiscord": "Unisciti a Discord",
"locations": "Luoghi",
"descriptions": "Descrizioni",
"addAName": "Aggiungi un nome",
"findPeopleByName": "Trova rapidamente le persone per nome",
"addViewers": "{count, plural, zero {Aggiungi visualizzatore} one {Aggiungi visualizzatore} other {Aggiungi visualizzatori}}",
"addCollaborators": "{count, plural, zero {Aggiungi collaboratore} one {Aggiungi collaboratore} other {Aggiungi collaboratori}}",
"developerSettings": "Impostazioni sviluppatore",
"serverEndpoint": "Endpoint del server",
"invalidEndpoint": "Endpoint invalido",
"invalidEndpointMessage": "Spiacenti, l'endpoint inserito non è valido. Inserisci un endpoint valido e riprova.",
"loopVideoOn": "Loop video on",
"loopVideoOff": "Loop video off"
}

View File

@ -12,7 +12,7 @@
"deleteAccountFeedbackPrompt": "We vinden het jammer je te zien gaan. Deel je feedback om ons te helpen verbeteren.",
"feedback": "Feedback",
"kindlyHelpUsWithThisInformation": "Help ons alsjeblieft met deze informatie",
"confirmDeletePrompt": "Ja, ik wil permanent mijn account inclusief alle gegevens verwijderen.",
"confirmDeletePrompt": "Ja, ik wil mijn account en de bijbehorende gegevens verspreid over alle apps permanent verwijderen.",
"confirmAccountDeletion": "Account verwijderen bevestigen",
"deleteAccountPermanentlyButton": "Account permanent verwijderen",
"yourAccountHasBeenDeleted": "Je account is verwijderd",
@ -453,6 +453,8 @@
"showMemories": "Toon herinneringen",
"yearsAgo": "{count, plural, one{{count} jaar geleden} other{{count} jaar geleden}}",
"backupSettings": "Back-up instellingen",
"backupStatus": "Back-upstatus",
"backupStatusDescription": "Items die zijn geback-upt, worden hier getoond",
"backupOverMobileData": "Back-up maken via mobiele data",
"backupVideos": "Back-up video's",
"disableAutoLock": "Automatisch vergrendelen uitschakelen",
@ -496,6 +498,7 @@
"removeDuplicates": "Duplicaten verwijderen",
"removeDuplicatesDesc": "Controleer en verwijder bestanden die exacte kopieën zijn.",
"viewLargeFiles": "Grote bestanden",
"viewLargeFilesDesc": "Bekijk bestanden die de meeste opslagruimte verbruiken.",
"noDuplicates": "✨ Geen duplicaten",
"youveNoDuplicateFilesThatCanBeCleared": "Je hebt geen dubbele bestanden die kunnen worden gewist",
"success": "Succes",
@ -1314,6 +1317,7 @@
"cl_video_player_description": "Een verfrissende nieuwe videospeler, met betere afspeelknoppen en ondersteuning voor HDR-video's.",
"appLockDescriptions": "Kies tussen het standaard vergrendelscherm van uw apparaat en een aangepast vergrendelscherm met een pincode of wachtwoord.",
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Om appvergrendeling in te schakelen, moet u een toegangscode of schermvergrendeling instellen in uw systeeminstellingen.",
"authToViewPasskey": "Verifieer uzelf om uw toegangssleutel te bekijken",
"loopVideoOn": "Loop video on",
"loopVideoOff": "Loop video off"
}

View File

@ -12,7 +12,6 @@
"deleteAccountFeedbackPrompt": "Vi er lei oss for at du forlater oss. Gi oss gjerne en tilbakemelding så vi kan forbedre oss.",
"feedback": "Tilbakemelding",
"kindlyHelpUsWithThisInformation": "Vær vennlig og hjelp oss med denne informasjonen",
"confirmDeletePrompt": "Ja, jeg ønsker å slette denne kontoen og all dataen dens permanent.",
"confirmAccountDeletion": "Bekreft sletting av konto",
"deleteAccountPermanentlyButton": "Slett bruker for altid",
"yourAccountHasBeenDeleted": "Brukeren din har blitt slettet",

View File

@ -12,7 +12,7 @@
"deleteAccountFeedbackPrompt": "Przykro nam, że odchodzisz. Wyjaśnij nam, dlaczego nas opuszczasz, aby pomóc ulepszać nasze usługi.",
"feedback": "Opinia",
"kindlyHelpUsWithThisInformation": "Pomóż nam z tą informacją",
"confirmDeletePrompt": "Tak, chcę trwale usunąć konto i wszystkie dane z nim powiązane.",
"confirmDeletePrompt": "Tak, chcę trwale usunąć to konto i jego dane ze wszystkich aplikacji.",
"confirmAccountDeletion": "Potwierdź usunięcie konta",
"deleteAccountPermanentlyButton": "Usuń konto na stałe",
"yourAccountHasBeenDeleted": "Twoje konto zostało usunięte",
@ -453,6 +453,8 @@
"showMemories": "Pokaż wspomnienia",
"yearsAgo": "{count, plural, one{{count} rok temu} few {{count} lata temu} many {{count} lat temu} other{{count} lata temu}}",
"backupSettings": "Ustawienia kopii zapasowej",
"backupStatus": "Status kopii zapasowej",
"backupStatusDescription": "Elementy, których kopia zapasowa została utworzona, zostaną wyświetlone w tym miejscu",
"backupOverMobileData": "Kopia zapasowa przez dane mobilne",
"backupVideos": "Utwórz kopię zapasową wideo",
"disableAutoLock": "Wyłącz automatyczną blokadę",

View File

@ -12,7 +12,7 @@
"deleteAccountFeedbackPrompt": "Lamentamos ver você partir. Por favor, compartilhe seus comentários para nos ajudar a melhorar.",
"feedback": "Comentários",
"kindlyHelpUsWithThisInformation": "Ajude-nos com esta informação",
"confirmDeletePrompt": "Sim, desejo excluir permanentemente esta conta e todos os seus dados.",
"confirmDeletePrompt": "Sim, eu quero excluir permanentemente esta conta e seus dados em todos os aplicativos.",
"confirmAccountDeletion": "Confirmar exclusão da conta",
"deleteAccountPermanentlyButton": "Excluir conta permanentemente",
"yourAccountHasBeenDeleted": "Sua conta foi excluída",
@ -453,6 +453,8 @@
"showMemories": "Mostrar memórias",
"yearsAgo": "{count, plural, one{{count} anos atrás} other{{count} anos atrás}}",
"backupSettings": "Configurações de backup",
"backupStatus": "Status do Backup",
"backupStatusDescription": "Os itens que foram salvos no backup aparecerão aqui",
"backupOverMobileData": "Backup usando dados móveis",
"backupVideos": "Backup de vídeos",
"disableAutoLock": "Desativar bloqueio automático",

View File

@ -12,7 +12,7 @@
"deleteAccountFeedbackPrompt": "Мы сожалеем, что вы уходите. Пожалуйста, объясните, почему вы уходите, чтобы помочь нам развиваться.",
"feedback": "Отзыв",
"kindlyHelpUsWithThisInformation": "Пожалуйста, помогите нам с этой информацией",
"confirmDeletePrompt": "Да, я хочу навсегда удалить эту учётную запись и все её данные.",
"confirmDeletePrompt": "Да, я хочу навсегда удалить эту учётную запись и все её данные во всех приложениях Ente.",
"confirmAccountDeletion": "Подтвердить удаление учётной записи",
"deleteAccountPermanentlyButton": "Удалить аккаунт навсегда",
"yourAccountHasBeenDeleted": "Ваша учетная запись была удалена",
@ -128,7 +128,7 @@
}
}
},
"twofactorSetup": "Установка двуфакторной аутентификации",
"twofactorSetup": "Вход с 2FA",
"enterCode": "Введите код",
"scanCode": "Сканировать код",
"codeCopiedToClipboard": "Код скопирован в буфер обмена",
@ -275,6 +275,7 @@
"codeAppliedPageTitle": "Код применён",
"changeYourReferralCode": "Изменить ваш реферальный код",
"change": "Изменить",
"unavailableReferralCode": "Извините, такого кода не существует.",
"storageInGB": "{storageAmountInGB} Гигабайт",
"claimed": "Получено",
"@claimed": {
@ -306,8 +307,8 @@
}
}
},
"faq": "ЧаВо",
"help": "помощь",
"faq": "Ответы на ваши вопросы",
"help": "Помощь",
"oopsSomethingWentWrong": "Ой! Что-то пошло не так",
"peopleUsingYourCode": "Люди использующие ваш код",
"eligible": "подходящий",
@ -411,7 +412,13 @@
"photoGridSize": "Размер сетки фотографий",
"manageDeviceStorage": "Управление хранилищем устройства",
"machineLearning": "Machine learning",
"mlConsent": "Включить машинное обучение",
"mlConsentTitle": "Включить машинное обучение?",
"mlConsentDescription": "Если вы включите машинное обучение, Ente будет извлекать информацию из файлов (например, геометрию лица), включая те, которыми с вами поделились.\n\nЭто будет происходить на вашем устройстве, и любая сгенерированная биометрическая информация будет зашифрована с использованием сквозного (End-to-End) шифрования между вашим устройством и сервером.",
"mlConsentPrivacy": "Пожалуйста, нажмите здесь, чтобы узнать больше об этой функции в нашей политике конфиденциальности",
"mlConsentConfirmation": "Я понимаю и хочу включить машинное обучение",
"magicSearch": "Волшебный поиск",
"mlIndexingDescription": "Обратите внимание, что машинное обучение приведёт к повышенному потреблению трафика и батареи, пока все элементы не будут проиндексированы. Рекомендуем использовать ПК версию для более быстрого индексирования. Полученные результаты будут синхронизированы автоматически между устройствами.",
"loadingModel": "Загрузка моделей...",
"waitingForWifi": "Ожидание WiFi...",
"status": "Статус",
@ -443,7 +450,7 @@
},
"showMemories": "Показать воспоминания",
"yearsAgo": "{count, plural, one{{count} год назад} other{{count} лет назад}}",
"backupSettings": "Резервная копия настроек",
"backupSettings": "Настройки резервного копирования",
"backupOverMobileData": "Резервное копирование через мобильную сеть",
"backupVideos": "Резервное копирование видео",
"disableAutoLock": "Отключить автоблокировку",
@ -461,8 +468,8 @@
"authToChangeYourEmail": "Пожалуйста, авторизуйтесь, чтобы изменить адрес электронной почты",
"changePassword": "Изменить пароль",
"authToChangeYourPassword": "Пожалуйста, авторизуйтесь, чтобы изменить пароль",
"emailVerificationToggle": "Подтверждение электронной почты",
"authToChangeEmailVerificationSetting": "Авторизуйтесь, чтобы изменить подтверждение электронной почты",
"emailVerificationToggle": "Вход с кодом на почту",
"authToChangeEmailVerificationSetting": "Пожалуйста, войдите, чтобы изменить настройку подтверждения электронной почты",
"exportYourData": "Экспорт данных",
"logout": "Выйти",
"authToInitiateAccountDeletion": "Пожалуйста, авторизуйтесь, чтобы начать удаление аккаунта",
@ -1143,6 +1150,7 @@
"successfullyHid": "Успешно скрыто",
"successfullyUnhid": "Успешно показано",
"crashReporting": "Отчеты об ошибках",
"resumableUploads": "Поддержка дозагрузки файл(а/ов) при разрыве связи",
"addToHiddenAlbum": "Добавить в скрытый альбом",
"moveToHiddenAlbum": "Переместить в скрытый альбом",
"fileTypes": "Типы файлов",
@ -1266,6 +1274,8 @@
"enable": "Включить",
"enabled": "Включено",
"moreDetails": "Подробнее",
"enableMLIndexingDesc": "Ente поддерживает машинное обучение на устройстве для распознавания лиц, умного поиска и других расширенных функций поиска",
"magicSearchHint": "Умный поиск позволяет искать фотографии по их содержимому, например, 'цветок', 'красная машина', 'паспорт', 'документы'",
"panorama": "Панорама",
"reenterPassword": "Подтвердите пароль",
"reenterPin": "Введите PIN-код ещё раз",

View File

@ -12,7 +12,6 @@
"deleteAccountFeedbackPrompt": "Vi är ledsna att se dig lämna oss. Vänligen dela dina synpunkter för att hjälpa oss att förbättra.",
"feedback": "Feedback",
"kindlyHelpUsWithThisInformation": "Vänligen hjälp oss med denna information",
"confirmDeletePrompt": "Ja, jag vill ta bort detta konto och all data permanent.",
"confirmAccountDeletion": "Bekräfta radering av konto",
"deleteAccountPermanentlyButton": "Radera kontot permanent",
"yourAccountHasBeenDeleted": "Ditt konto har raderats",
@ -281,6 +280,7 @@
"description": "Used to indicate storage claimed, like 10GB Claimed"
},
"inviteYourFriends": "Bjud in dina vänner",
"help": "Hjälp",
"subscribe": "Prenumerera",
"trash": "Papperskorg",
"photoSmallCase": "foto",

View File

@ -0,0 +1,19 @@
{
"@@locale ": "en",
"enterYourEmailAddress": "உங்கள் மின்னஞ்சல் முகவரியை உள்ளிடவும்",
"accountWelcomeBack": "மீண்டும் வருக!",
"email": "மின்னஞ்சல்",
"cancel": "ரத்து செய்",
"verify": "சரிபார்க்கவும்",
"invalidEmailAddress": "தவறான மின்னஞ்சல் முகவரி",
"enterValidEmail": "சரியான மின்னஞ்சல் முகவரியை உள்ளிடவும்.",
"deleteAccount": "கணக்கை நீக்கு",
"askDeleteReason": "உங்கள் கணக்கை நீக்குவதற்கான முக்கிய காரணம் என்ன?",
"deleteAccountFeedbackPrompt": "நீங்கள் வெளியேறுவதை கண்டு வருந்துகிறோம். எங்களை மேம்படுத்த உதவ உங்கள் கருத்தைப் பகிரவும்.",
"feedback": "பின்னூட்டம்",
"kindlyHelpUsWithThisInformation": "இந்த தகவலுடன் தயவுசெய்து எங்களுக்கு உதவுங்கள்",
"confirmDeletePrompt": "ஆம், எல்லா செயலிகளிலும் இந்தக் கணக்கையும் அதன் தரவையும் நிரந்தரமாக நீக்க விரும்புகிறேன்.",
"confirmAccountDeletion": "கணக்கு நீக்குதலை உறுதிப்படுத்தவும்",
"deleteAccountPermanentlyButton": "கணக்கை நிரந்தரமாக நீக்கவும்",
"deleteReason1": "எனக்கு தேவையான ஒரு முக்கிய அம்சம் இதில் இல்லை"
}

View File

@ -12,7 +12,6 @@
"deleteAccountFeedbackPrompt": "เราเสียใจที่เห็นคุณไป โปรดแบ่งปันความคิดเห็นของคุณเพื่อช่วยให้เราปรับปรุง",
"feedback": "ความคิดเห็น",
"kindlyHelpUsWithThisInformation": "กรุณาช่วยเราด้วยข้อมูลนี้",
"confirmDeletePrompt": "ใช่ ฉันต้องการลบบัญชีนี้และข้อมูลที่เกี่ยวข้องทั้งหมดแบบถาวร",
"confirmAccountDeletion": "ยืนยันการลบบัญชี",
"deleteAccountPermanentlyButton": "ลบบัญชีถาวร",
"yourAccountHasBeenDeleted": "บัญชีของคุณถูกลบแล้ว",

View File

@ -12,7 +12,6 @@
"deleteAccountFeedbackPrompt": "Aramızdan ayrıldığınız için üzgünüz. Lütfen kendimizi geliştirmemize yardımcı olun. Neden ayrıldığınızııklar mısınız.",
"feedback": "Geri Bildirim",
"kindlyHelpUsWithThisInformation": "Lütfen bu bilgilerle bize yardımcı olun",
"confirmDeletePrompt": "Evet, bu hesabı ve tüm verileri kalıcı olarak silmek istiyorum.",
"confirmAccountDeletion": "Hesap silme işlemini onayla",
"deleteAccountPermanentlyButton": "Hesabımı kalıcı olarak sil",
"yourAccountHasBeenDeleted": "Hesabınız silindi",

View File

@ -12,7 +12,7 @@
"deleteAccountFeedbackPrompt": "我们很抱歉看到您离开。请分享您的反馈以帮助我们改进。",
"feedback": "反馈",
"kindlyHelpUsWithThisInformation": "请帮助我们了解这个信息",
"confirmDeletePrompt": "是的,我想永久删除此账户及其相关数据.",
"confirmDeletePrompt": "是的,我想永久删除此账户及其所有关联的应用程序的数据。",
"confirmAccountDeletion": "确认删除账户",
"deleteAccountPermanentlyButton": "永久删除账户",
"yourAccountHasBeenDeleted": "您的账户已删除",
@ -453,6 +453,8 @@
"showMemories": "显示回忆",
"yearsAgo": "{count, plural, one{{count} 年前} other{{count} 年前}}",
"backupSettings": "备份设置",
"backupStatus": "备份状态",
"backupStatusDescription": "已备份的项目将显示在此处",
"backupOverMobileData": "通过移动数据备份",
"backupVideos": "备份视频",
"disableAutoLock": "禁用自动锁定",

View File

@ -14,6 +14,7 @@ import 'package:logging/logging.dart';
import "package:media_kit/media_kit.dart";
import 'package:path_provider/path_provider.dart';
import 'package:photos/app.dart';
import "package:photos/audio_session_handler.dart";
import 'package:photos/core/configuration.dart';
import 'package:photos/core/constants.dart';
import 'package:photos/core/error-reporting/super_logging.dart';
@ -73,6 +74,10 @@ const kFGTaskDeathTimeoutInMicroseconds = 5000000;
void main() async {
debugRepaintRainbowEnabled = false;
WidgetsFlutterBinding.ensureInitialized();
//For audio to work on vidoes in iOS when in silent mode.
if (Platform.isIOS) {
unawaited(AudioSessionHandler.setAudioSessionCategory());
}
MediaKit.ensureInitialized();
final savedThemeMode = await AdaptiveTheme.getThemeMode();

View File

@ -0,0 +1,60 @@
import "dart:async";
import "package:photos/models/backup/backup_item_status.dart";
import "package:photos/models/file/file.dart";
class BackupItem {
final BackupItemStatus status;
final EnteFile file;
final int collectionID;
final Completer<EnteFile> completer;
final Object? error;
BackupItem({
required this.status,
required this.file,
required this.collectionID,
required this.completer,
this.error,
});
BackupItem copyWith({
BackupItemStatus? status,
EnteFile? file,
int? collectionID,
Completer<EnteFile>? completer,
Object? error,
}) {
return BackupItem(
status: status ?? this.status,
file: file ?? this.file,
collectionID: collectionID ?? this.collectionID,
completer: completer ?? this.completer,
error: error ?? this.error,
);
}
@override
String toString() {
return 'BackupItem(status: $status, file: $file, collectionID: $collectionID, error: $error)';
}
@override
bool operator ==(covariant BackupItem other) {
if (identical(this, other)) return true;
return other.status == status &&
other.file == file &&
other.collectionID == collectionID &&
other.completer == completer &&
other.error == error;
}
@override
int get hashCode {
return status.hashCode ^
file.hashCode ^
collectionID.hashCode ^
completer.hashCode;
}
}

View File

@ -0,0 +1,7 @@
enum BackupItemStatus {
inBackground,
inQueue,
uploading,
completed,
retry,
}

View File

@ -16,6 +16,7 @@ import 'package:photos/ui/account/verify_recovery_page.dart';
import 'package:photos/ui/components/home_header_widget.dart';
import 'package:photos/ui/components/notification_widget.dart';
import 'package:photos/ui/home/header_error_widget.dart';
import "package:photos/ui/settings/backup/backup_status_screen.dart";
import 'package:photos/utils/navigation_util.dart';
const double kContainerHeight = 36;
@ -90,7 +91,16 @@ class _StatusBarWidgetState extends State<StatusBarWidget> {
centerWidget: _showStatus
? _showErrorBanner
? const Text("ente", style: brandStyleMedium)
: const SyncStatusWidget()
: GestureDetector(
onTap: () {
routeToPage(
context,
const BackupStatusScreen(),
forceCustomPageRoute: true,
).ignore();
},
child: const SyncStatusWidget(),
)
: const Text("ente", style: brandStyleMedium),
),
_showErrorBanner

View File

@ -0,0 +1,173 @@
import "dart:typed_data";
import 'package:flutter/material.dart';
import "package:photos/models/backup/backup_item.dart";
import "package:photos/models/backup/backup_item_status.dart";
import 'package:photos/theme/ente_theme.dart';
import "package:photos/utils/file_uploader.dart";
import "package:photos/utils/thumbnail_util.dart";
class BackupItemCard extends StatefulWidget {
const BackupItemCard({
super.key,
required this.item,
});
final BackupItem item;
@override
State<BackupItemCard> createState() => _BackupItemCardState();
}
class _BackupItemCardState extends State<BackupItemCard> {
Uint8List? thumbnail;
String? folderName;
@override
void initState() {
super.initState();
_getThumbnail();
_getFolderName();
}
@override
void dispose() {
super.dispose();
}
_getThumbnail() async {
thumbnail = await getThumbnail(widget.item.file);
setState(() {});
}
_getFolderName() async {
folderName = widget.item.file.deviceFolder ?? '';
setState(() {});
}
@override
Widget build(BuildContext context) {
final colorScheme = getEnteColorScheme(context);
return Container(
height: 60,
margin: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: Theme.of(context).brightness == Brightness.light
? const Color(0xFF000000).withOpacity(0.08)
: const Color(0xFFFFFFFF).withOpacity(0.08),
width: 1,
),
),
child: Row(
children: [
SizedBox(
width: 60,
height: 60,
child: ClipRRect(
borderRadius: BorderRadius.circular(4),
child: thumbnail != null
? Image.memory(
thumbnail!,
fit: BoxFit.cover,
)
: const SizedBox(),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.item.file.displayName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 16,
height: 20 / 16,
color: Theme.of(context).brightness == Brightness.light
? const Color(0xFF000000)
: const Color(0xFFFFFFFF),
),
),
const SizedBox(height: 4),
Text(
folderName ?? "",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
height: 17 / 14,
color: Theme.of(context).brightness == Brightness.light
? const Color.fromRGBO(0, 0, 0, 0.7)
: const Color.fromRGBO(255, 255, 255, 0.7),
),
),
],
),
),
const SizedBox(width: 12),
SizedBox(
height: 48,
width: 48,
child: Center(
child: switch (widget.item.status) {
BackupItemStatus.uploading => SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: colorScheme.primary700,
),
),
BackupItemStatus.completed => const SizedBox(
width: 24,
height: 24,
child: Icon(
Icons.check,
color: Color(0xFF00B33C),
),
),
BackupItemStatus.inQueue => SizedBox(
width: 24,
height: 24,
child: Icon(
Icons.history,
color: Theme.of(context).brightness == Brightness.light
? const Color.fromRGBO(0, 0, 0, .6)
: const Color.fromRGBO(255, 255, 255, .6),
),
),
BackupItemStatus.retry => IconButton(
icon: const Icon(
Icons.sync,
color: Color(0xFFFDB816),
),
onPressed: () async {
await FileUploader.instance.upload(
widget.item.file,
widget.item.collectionID,
);
},
),
BackupItemStatus.inBackground => SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: Theme.of(context).brightness == Brightness.light
? const Color.fromRGBO(0, 0, 0, .6)
: const Color.fromRGBO(255, 255, 255, .6),
),
),
},
),
),
],
),
);
}
}

View File

@ -0,0 +1,111 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import "dart:collection";
import 'package:flutter/material.dart';
import "package:photos/core/event_bus.dart";
import "package:photos/events/backup_updated_event.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/models/backup/backup_item.dart";
import 'package:photos/ui/components/title_bar_title_widget.dart';
import 'package:photos/ui/components/title_bar_widget.dart';
import "package:photos/ui/settings/backup/backup_item_card.dart";
import "package:photos/utils/file_uploader.dart";
class BackupStatusScreen extends StatefulWidget {
const BackupStatusScreen({super.key});
@override
State<BackupStatusScreen> createState() => _BackupStatusScreenState();
}
class _BackupStatusScreenState extends State<BackupStatusScreen> {
LinkedHashMap<String, BackupItem> items = FileUploader.instance.allBackups;
@override
void initState() {
super.initState();
checkBackupUpdatedEvent();
}
void checkBackupUpdatedEvent() {
Bus.instance.on<BackupUpdatedEvent>().listen((event) {
items = event.items;
setState(() {});
});
}
@override
Widget build(BuildContext context) {
final List<BackupItem> items = this.items.values.toList();
return Scaffold(
body: CustomScrollView(
primary: false,
slivers: <Widget>[
TitleBarWidget(
flexibleSpaceTitle: TitleBarTitleWidget(
title: S.of(context).backupStatus,
),
),
items.isEmpty
? SliverFillRemaining(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 60,
vertical: 12,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.cloud_upload_outlined,
color:
Theme.of(context).brightness == Brightness.light
? const Color.fromRGBO(0, 0, 0, 0.6)
: const Color.fromRGBO(255, 255, 255, 0.6),
),
const SizedBox(height: 16),
Text(
S.of(context).backupStatusDescription,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
height: 20 / 16,
color:
Theme.of(context).brightness == Brightness.light
? const Color(0xFF000000).withOpacity(0.7)
: const Color(0xFFFFFFFF).withOpacity(0.7),
),
),
const SizedBox(height: 48),
],
),
),
)
: SliverList(
delegate: SliverChildBuilderDelegate(
(delegateBuildContext, index) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 20,
horizontal: 16,
),
child: ListView.builder(
shrinkWrap: true,
primary: false,
itemBuilder: (context, index) {
return BackupItemCard(item: items[index]);
},
itemCount: items.length,
),
);
},
childCount: 1,
),
),
],
),
);
}
}

View File

@ -16,11 +16,14 @@ import 'package:photos/core/event_bus.dart';
import 'package:photos/core/network/network.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/db/upload_locks_db.dart';
import "package:photos/events/backup_updated_event.dart";
import "package:photos/events/file_uploaded_event.dart";
import 'package:photos/events/files_updated_event.dart';
import 'package:photos/events/local_photos_updated_event.dart';
import 'package:photos/events/subscription_purchased_event.dart';
import 'package:photos/main.dart';
import "package:photos/models/backup/backup_item.dart";
import "package:photos/models/backup/backup_item_status.dart";
import 'package:photos/models/encryption_result.dart';
import 'package:photos/models/file/file.dart';
import 'package:photos/models/file/file_type.dart';
@ -59,11 +62,15 @@ class FileUploader {
final _enteDio = NetworkClient.instance.enteDio;
final LinkedHashMap<String, FileUploadItem> _queue =
LinkedHashMap<String, FileUploadItem>();
final LinkedHashMap<String, BackupItem> _allBackups =
LinkedHashMap<String, BackupItem>();
final _uploadLocks = UploadLocksDB.instance;
final kSafeBufferForLockExpiry = const Duration(days: 1).inMicroseconds;
final kBGTaskDeathTimeout = const Duration(seconds: 5).inMicroseconds;
final _uploadURLs = Queue<UploadURL>();
LinkedHashMap<String, BackupItem> get allBackups => _allBackups;
// Maintains the count of files in the current upload session.
// Upload session is the period between the first entry into the _queue and last entry out of the _queue
int _totalCountInUploadSession = 0;
@ -160,6 +167,13 @@ class FileUploader {
if (!_queue.containsKey(localID)) {
final completer = Completer<EnteFile>();
_queue[localID] = FileUploadItem(file, collectionID, completer);
_allBackups[localID] = BackupItem(
status: BackupItemStatus.inQueue,
file: file,
collectionID: collectionID,
completer: completer,
);
Bus.instance.fire(BackupUpdatedEvent(_allBackups));
_pollQueue();
return completer.future;
}
@ -203,6 +217,11 @@ class FileUploader {
});
for (final id in uploadsToBeRemoved) {
_queue.remove(id)?.completer.completeError(reason);
_allBackups[id] = _allBackups[id]!.copyWith(
status: BackupItemStatus.retry,
error: reason,
);
Bus.instance.fire(BackupUpdatedEvent(_allBackups));
}
_totalCountInUploadSession = 0;
}
@ -225,6 +244,9 @@ class FileUploader {
});
for (final id in uploadsToBeRemoved) {
_queue.remove(id)?.completer.completeError(reason);
_allBackups[id] = _allBackups[id]!
.copyWith(status: BackupItemStatus.retry, error: reason);
Bus.instance.fire(BackupUpdatedEvent(_allBackups));
}
_logger.info(
'number of enteries removed from queue ${uploadsToBeRemoved.length}',
@ -262,6 +284,10 @@ class FileUploader {
}
if (pendingEntry != null) {
pendingEntry.status = UploadStatus.inProgress;
_allBackups[pendingEntry.file.localID!] =
_allBackups[pendingEntry.file.localID]!
.copyWith(status: BackupItemStatus.uploading);
Bus.instance.fire(BackupUpdatedEvent(_allBackups));
_encryptAndUploadFileToCollection(
pendingEntry.file,
pendingEntry.collectionID,
@ -291,13 +317,22 @@ class FileUploader {
},
);
_queue.remove(localID)!.completer.complete(uploadedFile);
_allBackups[localID] =
_allBackups[localID]!.copyWith(status: BackupItemStatus.completed);
Bus.instance.fire(BackupUpdatedEvent(_allBackups));
return uploadedFile;
} catch (e) {
if (e is LockAlreadyAcquiredError) {
_queue[localID]!.status = UploadStatus.inBackground;
_allBackups[localID] = _allBackups[localID]!
.copyWith(status: BackupItemStatus.inBackground);
Bus.instance.fire(BackupUpdatedEvent(_allBackups));
return _queue[localID]!.completer.future;
} else {
_queue.remove(localID)!.completer.completeError(e);
_allBackups[localID] = _allBackups[localID]!
.copyWith(status: BackupItemStatus.retry, error: e);
Bus.instance.fire(BackupUpdatedEvent(_allBackups));
return null;
}
} finally {
@ -406,7 +441,26 @@ class FileUploader {
Future<EnteFile> forceUpload(EnteFile file, int collectionID) async {
_hasInitiatedForceUpload = true;
return _tryToUpload(file, collectionID, true);
final isInQueue = _allBackups[file.localID!] != null;
try {
final result = await _tryToUpload(file, collectionID, true);
if (isInQueue) {
_allBackups[file.localID!] = _allBackups[file.localID]!.copyWith(
status: BackupItemStatus.completed,
);
Bus.instance.fire(BackupUpdatedEvent(_allBackups));
}
return result;
} catch (error) {
if (isInQueue) {
_allBackups[file.localID!] = _allBackups[file.localID]!.copyWith(
status: BackupItemStatus.retry,
error: error,
);
Bus.instance.fire(BackupUpdatedEvent(_allBackups));
}
rethrow;
}
}
Future<EnteFile> _tryToUpload(
@ -426,6 +480,14 @@ class FileUploader {
return fileOnDisk;
}
}
if (_allBackups[file.localID!] != null &&
_allBackups[file.localID]!.status != BackupItemStatus.uploading) {
_allBackups[file.localID!] = _allBackups[file.localID]!.copyWith(
status: BackupItemStatus.uploading,
);
Bus.instance.fire(BackupUpdatedEvent(_allBackups));
}
if ((file.localID ?? '') == '') {
_logger.severe('Trying to upload file with missing localID');
return file;
@ -442,7 +504,7 @@ class FileUploader {
}
final String lockKey = file.localID!;
bool _isMultipartUpload = false;
bool isMultipartUpload = false;
try {
await _uploadLocks.acquireLock(
@ -589,7 +651,7 @@ class FileUploader {
final fileUploadURL = await _getUploadURL();
fileObjectKey = await _putFile(fileUploadURL, encryptedFile);
} else {
_isMultipartUpload = true;
isMultipartUpload = true;
_logger.finest(
"Init multipartUpload $multipartEntryExists, isUpdate $isUpdatedFile",
);
@ -757,7 +819,7 @@ class FileUploader {
encryptedFilePath,
encryptedThumbnailPath,
lockKey: lockKey,
isMultiPartUpload: _isMultipartUpload,
isMultiPartUpload: isMultipartUpload,
);
}
}
@ -1280,10 +1342,17 @@ class FileUploader {
if (dbFile?.uploadedFileID != null) {
_logger.info("Background upload success detected");
completer?.complete(dbFile);
_allBackups[upload.key] = _allBackups[upload.key]!
.copyWith(status: BackupItemStatus.completed);
} else {
_logger.info("Background upload failure detected");
completer?.completeError(SilentlyCancelUploadsError());
_allBackups[upload.key] = _allBackups[upload.key]!.copyWith(
status: BackupItemStatus.retry,
error: SilentlyCancelUploadsError(),
);
}
Bus.instance.fire(BackupUpdatedEvent(_allBackups));
}
}
Future.delayed(kBlockedUploadsPollFrequency, () async {

View File

@ -1297,18 +1297,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev"
source: hosted
version: "10.0.5"
version: "10.0.4"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
version: "3.0.3"
leak_tracker_testing:
dependency: transitive
description:
@ -1441,10 +1441,10 @@ packages:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev"
source: hosted
version: "0.11.1"
version: "0.8.0"
media_extension:
dependency: "direct main"
description:
@ -1529,10 +1529,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev"
source: hosted
version: "1.15.0"
version: "1.12.0"
mgrs_dart:
dependency: transitive
description:
@ -1901,10 +1901,10 @@ packages:
dependency: transitive
description:
name: platform
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
source: hosted
version: "3.1.5"
version: "3.1.4"
plugin_platform_interface:
dependency: transitive
description:
@ -2410,26 +2410,26 @@ packages:
dependency: "direct dev"
description:
name: test
sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e"
sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073"
url: "https://pub.dev"
source: hosted
version: "1.25.7"
version: "1.25.2"
test_api:
dependency: transitive
description:
name: test_api
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
version: "0.7.0"
test_core:
dependency: transitive
description:
name: test_core
sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696"
sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4"
url: "https://pub.dev"
source: hosted
version: "0.6.4"
version: "0.6.0"
timezone:
dependency: transitive
description:
@ -2708,10 +2708,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev"
source: hosted
version: "14.2.5"
version: "14.2.1"
volume_controller:
dependency: transitive
description:

View File

@ -12,7 +12,7 @@ description: ente photos application
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.9.30+930
version: 0.9.32+932
publish_to: none
environment:

View File

@ -5,6 +5,7 @@ import (
"database/sql"
b64 "encoding/base64"
"fmt"
"github.com/ente-io/museum/ente/base"
"github.com/ente-io/museum/pkg/controller/file_copy"
"github.com/ente-io/museum/pkg/controller/filedata"
"net/http"
@ -361,7 +362,14 @@ func main() {
server.Use(p.HandlerFunc())
// note: the recover middleware must be in the last
server.Use(requestid.New(), middleware.Logger(urlSanitizer), cors(), gzip.Gzip(gzip.DefaultCompression), middleware.PanicRecover())
server.Use(requestid.New(
requestid.Config{
Generator: func() string {
return base.ServerReqID()
},
}),
middleware.Logger(urlSanitizer), cors(), gzip.Gzip(gzip.DefaultCompression), middleware.PanicRecover())
publicAPI := server.Group("/")
publicAPI.Use(rateLimiter.GlobalRateLimiter(), rateLimiter.APIRateLimitMiddleware(urlSanitizer))

View File

@ -3,6 +3,7 @@ package base
import (
"errors"
"fmt"
"github.com/google/uuid"
"github.com/matoous/go-nanoid/v2"
)
@ -28,3 +29,12 @@ func NewID(prefix string) (*string, error) {
result := fmt.Sprintf("%s_%s", prefix, id)
return &result, nil
}
func ServerReqID() string {
// Generate a nanoid with a custom alphabet and length of 22
id, err := NewID("ser")
if err != nil {
return "ser_" + uuid.New().String()
}
return *id
}

View File

@ -23,9 +23,7 @@ export default function AddCollectionButton({ showNextModal }: Iprops) {
onClick={() => showNextModal()}
coverFile={null}
>
<AllCollectionTileText>
{t("CREATE_COLLECTION")}
</AllCollectionTileText>
<AllCollectionTileText>{t("create_albums")}</AllCollectionTileText>
<ImageContainer>
<CenteredFlex>+</CenteredFlex>
</ImageContainer>

View File

@ -4,10 +4,10 @@ import {
faceCrop,
wipClusterDebugPageContents,
type ClusterDebugPageContents,
type FaceFileNeighbour,
type FaceFileNeighbours,
} from "@/new/photos/services/ml";
import type { Face } from "@/new/photos/services/ml/face";
import { type ClusteringOpts } from "@/new/photos/services/ml/cluster";
import { faceDirection, type Face } from "@/new/photos/services/ml/face";
import type { EnteFile } from "@/new/photos/types/file";
import {
FlexWrapper,
FluidContainer,
@ -15,56 +15,52 @@ import {
} from "@ente/shared/components/Container";
import EnteSpinner from "@ente/shared/components/EnteSpinner";
import BackButton from "@mui/icons-material/ArrowBackOutlined";
import { Box, IconButton, styled, Typography } from "@mui/material";
import {
Box,
Button,
IconButton,
MenuItem,
Stack,
styled,
TextField,
Typography,
} from "@mui/material";
import { useFormik } from "formik";
import { useRouter } from "next/router";
import { AppContext } from "pages/_app";
import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
import AutoSizer from "react-virtualized-auto-sizer";
import { VariableSizeList } from "react-window";
import {
areEqual,
VariableSizeList,
type ListChildComponentProps,
} from "react-window";
// TODO-Cluster Temporary component for debugging
export default function ClusterDebug() {
const { startLoading, finishLoading, showNavBar } = useContext(AppContext);
const [clusterRes, setClusterRes] = useState<
ClusterDebugPageContents | undefined
>();
useEffect(() => {
showNavBar(true);
cluster();
}, []);
const cluster = async () => {
const cluster = async (opts: ClusteringOpts) => {
setClusterRes(undefined);
startLoading();
setClusterRes(await wipClusterDebugPageContents());
setClusterRes(await wipClusterDebugPageContents(opts));
finishLoading();
};
if (!clusterRes) {
return (
<VerticallyCentered>
<EnteSpinner />
</VerticallyCentered>
);
}
useEffect(() => showNavBar(true), []);
return (
<>
<Typography variant="small">
{`${clusterRes.clusters.length} clusters`}
</Typography>
<Typography variant="small" color="text.muted">
Showing only upto first 30 faces (and only upto 30 nearest
neighbours of each).
</Typography>
<hr />
<Container>
<AutoSizer>
{({ height, width }) => (
<ClusterPhotoList
width={width}
height={height}
clusterRes={clusterRes}
/>
<ClusterList {...{ width, height, clusterRes }}>
<OptionsForm onCluster={cluster} />
</ClusterList>
)}
</AutoSizer>
</Container>
@ -84,7 +80,7 @@ const Options: React.FC = () => {
<IconButton onClick={close}>
<BackButton />
</IconButton>
<Box sx={{ marginInline: "auto" }}>{pt("Faces")}</Box>
<Box sx={{ marginInline: "auto" }}>{pt("Face Clusters")}</Box>
</FluidContainer>
</SelectionBar>
);
@ -101,19 +97,122 @@ const Container = styled("div")`
}
`;
interface ClusterPhotoListProps {
height: number;
width: number;
clusterRes: ClusterDebugPageContents;
interface OptionsFormProps {
onCluster: (opts: ClusteringOpts) => Promise<void>;
}
const ClusterPhotoList: React.FC<ClusterPhotoListProps> = ({
height,
const OptionsForm: React.FC<OptionsFormProps> = ({ onCluster }) => {
// Formik converts nums to a string on edit.
const toFloat = (n: number | string) =>
typeof n == "string" ? parseFloat(n) : n;
const { values, handleSubmit, handleChange, isSubmitting } =
useFormik<ClusteringOpts>({
initialValues: {
method: "linear",
minBlur: 10,
minScore: 0.8,
minClusterSize: 2,
joinThreshold: 0.7,
batchSize: 12500,
},
onSubmit: (values) =>
onCluster({
method: values.method,
minBlur: toFloat(values.minBlur),
minScore: toFloat(values.minScore),
minClusterSize: toFloat(values.minClusterSize),
joinThreshold: toFloat(values.joinThreshold),
batchSize: toFloat(values.batchSize),
}),
});
return (
<form onSubmit={handleSubmit}>
<Stack>
<Typography paddingInline={1}>Parameters</Typography>
<Stack
direction="row"
gap={1}
sx={{ ".MuiFormControl-root": { flex: "1" } }}
>
<TextField
name="method"
label="method"
value={values.method}
select
size="small"
onChange={handleChange}
>
{["hdbscan", "linear"].map((v) => (
<MenuItem key={v} value={v}>
{v}
</MenuItem>
))}
</TextField>
<TextField
name="minBlur"
label="minBlur"
value={values.minBlur}
size="small"
onChange={handleChange}
/>
<TextField
name="minScore"
label="minScore"
value={values.minScore}
size="small"
onChange={handleChange}
/>
<TextField
name="minClusterSize"
label="minClusterSize"
value={values.minClusterSize}
size="small"
onChange={handleChange}
/>
<TextField
name="joinThreshold"
label="joinThreshold"
value={values.joinThreshold}
size="small"
onChange={handleChange}
/>
<TextField
name="batchSize"
label="batchSize"
value={values.batchSize}
size="small"
onChange={handleChange}
/>
</Stack>
<Box marginInlineStart={"auto"} p={1}>
<Button
color="secondary"
type="submit"
disabled={isSubmitting}
>
Cluster
</Button>
</Box>
{isSubmitting && <Loader />}
</Stack>
</form>
);
};
type ClusterListProps = ClusterResHeaderProps & {
height: number;
width: number;
};
const ClusterList: React.FC<React.PropsWithChildren<ClusterListProps>> = ({
width,
height,
clusterRes,
children,
}) => {
const { faceFNs, clusterIDForFaceID } = clusterRes;
const [itemList, setItemList] = useState<ItemListItem[]>([]);
const [items, setItems] = useState<Item[]>([]);
const listRef = useRef(null);
const columns = useMemo(
@ -125,78 +224,67 @@ const ClusterPhotoList: React.FC<ClusterPhotoListProps> = ({
const listItemHeight = 120 * shrinkRatio + 24 + 4;
useEffect(() => {
setItemList(itemListFromFaceFNs(faceFNs, columns));
}, [columns, faceFNs]);
setItems(clusterRes ? itemsFromClusterRes(clusterRes, columns) : []);
}, [columns, clusterRes]);
useEffect(() => {
listRef.current?.resetAfterIndex(0);
}, [itemList]);
}, [items]);
const getItemSize = (i: number) =>
Array.isArray(itemList[i]) ? listItemHeight : 36;
const generateKey = (i: number) =>
Array.isArray(itemList[i])
? `${itemList[i][0].enteFile.id}/${itemList[i][0].face.faceID}-${itemList[i].slice(-1)[0].enteFile.id}/${itemList[i].slice(-1)[0].face.faceID}-${i}`
: `${itemList[i].faceID}-${i}`;
const itemSize = (index: number) =>
index === 0
? 140
: index === 1
? 130
: Array.isArray(items[index - 2])
? listItemHeight
: 36;
return (
<VariableSizeList
itemData={{ itemList, columns, shrinkRatio }}
ref={listRef}
itemSize={getItemSize}
height={height}
width={width}
itemCount={itemList.length}
itemKey={generateKey}
ref={listRef}
itemData={{ items, clusterRes, columns, shrinkRatio, children }}
itemCount={2 + items.length}
itemSize={itemSize}
overscanCount={3}
useIsScrolling
>
{({ index, style, data }) => {
const { itemList, columns, shrinkRatio } = data;
const item = itemList[index];
return (
<ListItem style={style}>
<ListContainer
columns={columns}
shrinkRatio={shrinkRatio}
>
{!Array.isArray(item) ? (
<LabelContainer span={columns}>
{`score ${item.score.toFixed(2)} blur ${item.blur.toFixed(0)}`}
</LabelContainer>
) : (
item.map((faceFN, i) => (
<FaceItem
key={i.toString()}
{...{ faceFN, clusterIDForFaceID }}
/>
))
)}
</ListContainer>
</ListItem>
);
}}
{ClusterListItemRenderer}
</VariableSizeList>
);
};
type ItemListItem = Face | FaceFileNeighbour[];
type Item = string | FaceWithFile[];
const itemListFromFaceFNs = (
faceFNs: FaceFileNeighbours[],
const itemsFromClusterRes = (
clusterRes: ClusterDebugPageContents,
columns: number,
) => {
const result: ItemListItem[] = [];
for (let index = 0; index < faceFNs.length; index++) {
const { face, neighbours } = faceFNs[index];
result.push(face);
const { clusterPreviewsWithFile, unclusteredFacesWithFile } = clusterRes;
const result: Item[] = [];
for (let index = 0; index < clusterPreviewsWithFile.length; index++) {
const { clusterSize, faces } = clusterPreviewsWithFile[index];
result.push(`cluster size ${clusterSize.toFixed(2)}`);
let lastIndex = 0;
while (lastIndex < neighbours.length) {
result.push(neighbours.slice(lastIndex, lastIndex + columns));
while (lastIndex < faces.length) {
result.push(faces.slice(lastIndex, lastIndex + columns));
lastIndex += columns;
}
}
if (unclusteredFacesWithFile.length) {
result.push(`•• unclustered faces ${unclusteredFacesWithFile.length}`);
let lastIndex = 0;
while (lastIndex < unclusteredFacesWithFile.length) {
result.push(
unclusteredFacesWithFile.slice(lastIndex, lastIndex + columns),
);
lastIndex += columns;
}
}
return result;
};
@ -209,67 +297,91 @@ const getShrinkRatio = (width: number, columns: number) =>
(width - 2 * getGapFromScreenEdge(width) - (columns - 1) * 4) /
(columns * 120);
interface FaceItemProps {
faceFN: FaceFileNeighbour;
clusterIDForFaceID: Map<string, string>;
// It in necessary to define the item renderer otherwise it gets recreated every
// time the parent rerenders, causing the form to lose its submitting state.
const ClusterListItemRenderer = React.memo<ListChildComponentProps>(
({ index, style, data }) => {
const { clusterRes, columns, shrinkRatio, items, children } = data;
if (index == 0) return <div style={style}>{children}</div>;
if (index == 1)
return (
<div style={style}>
<ClusterResHeader clusterRes={clusterRes} />
</div>
);
const item = items[index - 2];
return (
<ListItem style={style}>
<ListContainer columns={columns} shrinkRatio={shrinkRatio}>
{!Array.isArray(item) ? (
<LabelContainer span={columns}>{item}</LabelContainer>
) : (
item.map((f, i) => (
<FaceItem key={i.toString()} faceWithFile={f} />
))
)}
</ListContainer>
</ListItem>
);
},
areEqual,
);
interface ClusterResHeaderProps {
clusterRes: ClusterDebugPageContents | undefined;
}
const FaceItem: React.FC<FaceItemProps> = ({ faceFN, clusterIDForFaceID }) => {
const { face, enteFile, cosineSimilarity } = faceFN;
const { faceID } = face;
const ClusterResHeader: React.FC<ClusterResHeaderProps> = ({ clusterRes }) => {
if (!clusterRes) return null;
const [objectURL, setObjectURL] = useState<string | undefined>();
useEffect(() => {
let didCancel = false;
let thisObjectURL: string | undefined;
void faceCrop(faceID, enteFile).then((blob) => {
if (blob && !didCancel)
setObjectURL((thisObjectURL = URL.createObjectURL(blob)));
});
return () => {
didCancel = true;
if (thisObjectURL) URL.revokeObjectURL(thisObjectURL);
};
}, [faceID, enteFile]);
const {
totalFaceCount,
filteredFaceCount,
clusteredFaceCount,
unclusteredFaceCount,
timeTakenMs,
clusters,
} = clusterRes;
return (
<FaceChip
style={{
outline: outlineForCluster(clusterIDForFaceID.get(faceID)),
outlineOffset: "2px",
}}
>
{objectURL && (
<img
style={{
objectFit: "cover",
width: "100%",
height: "100%",
}}
src={objectURL}
/>
)}
<Typography variant="small" color="text.muted" textAlign="right">
{cosineSimilarity.toFixed(2)}
<Stack m={1}>
<Typography mb={1} variant="small">
{`${clusters.length} clusters in ${(timeTakenMs / 1000).toFixed(0)} seconds • ${totalFaceCount} faces ${filteredFaceCount} filtered ${clusteredFaceCount} clustered ${unclusteredFaceCount} unclustered`}
</Typography>
</FaceChip>
<Typography variant="small" color="text.muted">
Showing only top 30 clusters, bottom 30 clusters, and
unclustered faces.
</Typography>
<Typography variant="small" color="text.muted">
For each cluster showing only up to 50 faces, sorted by cosine
similarity to highest scoring face in the cluster.
</Typography>
<Typography variant="small" color="text.muted">
Below each face is its{" "}
<b>blur - score - cosineSimilarity - direction</b>.
</Typography>
<Typography variant="small" color="text.muted">
Faces added to the cluster as a result of next batch merging are
outlined.
</Typography>
</Stack>
);
};
const FaceChip = styled(Box)`
width: 120px;
height: 120px;
const Loader = () => (
<VerticallyCentered mt={4}>
<EnteSpinner />
</VerticallyCentered>
);
const ListItem = styled("div")`
display: flex;
justify-content: center;
`;
const outlineForCluster = (clusterID: string | undefined) =>
clusterID ? `1px solid oklch(0.8 0.2 ${hForID(clusterID)})` : undefined;
const hForID = (id: string) =>
([...id].reduce((s, c) => s + c.charCodeAt(0), 0) % 10) * 36;
const ListContainer = styled(Box, {
shouldForwardProp: (propName) => propName != "shrinkRatio",
})<{
@ -293,7 +405,78 @@ const LabelContainer = styled(ListItemContainer)`
height: 32px;
`;
const ListItem = styled("div")`
display: flex;
justify-content: center;
interface FaceItemProps {
faceWithFile: FaceWithFile;
}
interface FaceWithFile {
face: Face;
enteFile: EnteFile;
cosineSimilarity?: number;
wasMerged?: boolean;
}
const FaceItem: React.FC<FaceItemProps> = ({ faceWithFile }) => {
const { face, enteFile, cosineSimilarity, wasMerged } = faceWithFile;
const { faceID } = face;
const [objectURL, setObjectURL] = useState<string | undefined>();
useEffect(() => {
let didCancel = false;
let thisObjectURL: string | undefined;
void faceCrop(faceID, enteFile).then((blob) => {
if (blob && !didCancel)
setObjectURL((thisObjectURL = URL.createObjectURL(blob)));
});
return () => {
didCancel = true;
if (thisObjectURL) URL.revokeObjectURL(thisObjectURL);
};
}, [faceID, enteFile]);
const fd = faceDirection(face.detection);
const d = fd == "straight" ? "•" : fd == "left" ? "←" : "→";
return (
<FaceChip
style={{
outline: wasMerged ? `1px solid gray` : undefined,
outlineOffset: "2px",
}}
>
{objectURL && (
<img
style={{
objectFit: "cover",
width: "100%",
height: "100%",
}}
src={objectURL}
/>
)}
<Stack direction="row" justifyContent="space-between">
<Typography variant="small" color="text.muted">
{`b${face.blur.toFixed(0)} `}
</Typography>
<Typography variant="small" color="text.muted">
{`s${face.score.toFixed(1)}`}
</Typography>
{cosineSimilarity && (
<Typography variant="small" color="text.muted">
{`c${cosineSimilarity.toFixed(1)}`}
</Typography>
)}
<Typography variant="small" color="text.muted">
{`d${d}`}
</Typography>
</Stack>
</FaceChip>
);
};
const FaceChip = styled(Box)`
width: 120px;
height: 120px;
`;

View File

@ -6,10 +6,7 @@ import {
getLocalFiles,
getLocalTrashedFiles,
} from "@/new/photos/services/files";
import {
wipClusterEnable,
wipHasSwitchedOnceCmpAndSet,
} from "@/new/photos/services/ml";
import { wipHasSwitchedOnceCmpAndSet } from "@/new/photos/services/ml";
import { EnteFile } from "@/new/photos/types/file";
import { mergeMetadata } from "@/new/photos/utils/file";
import { CenteredFlex } from "@ente/shared/components/Container";
@ -677,11 +674,8 @@ export default function Gallery() {
// TODO-Cluster
if (process.env.NEXT_PUBLIC_ENTE_WIP_CL_AUTO) {
setTimeout(() => {
if (!wipHasSwitchedOnceCmpAndSet()) {
void wipClusterEnable().then(
(y) => y && router.push("cluster-debug"),
);
}
if (!wipHasSwitchedOnceCmpAndSet())
router.push("cluster-debug");
}, 2000);
}
}, []);

View File

@ -33,14 +33,15 @@
"ENTER_ENC_PASSPHRASE": "الرجاء إدخال كلمة المرور التي يمكننا استخدامها لتشفير بياناتك",
"PASSPHRASE_DISCLAIMER": "نحن لا نخزن كلمة مرورك، لذا إذا نسيتها، <strong>لن نتمكن من مساعدتك </strong>في استرداد بياناتك دون مفتاح الاسترداد.",
"WELCOME_TO_ENTE_HEADING": "مرحبا بك في <a/>",
"WELCOME_TO_ENTE_SUBHEADING": "",
"WHERE_YOUR_BEST_PHOTOS_LIVE": "",
"WELCOME_TO_ENTE_SUBHEADING": "تخزين الصور ومشاركتها بشكل مشفر من طرف إلى طرف",
"WHERE_YOUR_BEST_PHOTOS_LIVE": "أين تعيش أفضل صورك",
"KEY_GENERATION_IN_PROGRESS_MESSAGE": "جار توليد مفاتيح التشفير...",
"PASSPHRASE_HINT": "كلمة المرور",
"CONFIRM_PASSPHRASE": "تأكيد كلمة المرور",
"REFERRAL_CODE_HINT": "كيف سمعت عن Ente؟ (اختياري)",
"REFERRAL_INFO": "",
"REFERRAL_INFO": "نحن لا نتتبع عمليات تثبيت التطبيق، سيكون من المفيد لنا أن تخبرنا أين وجدتنا!",
"PASSPHRASE_MATCH_ERROR": "كلمات المرور غير متطابقة",
"create_albums": "",
"CREATE_COLLECTION": "ألبوم جديد",
"ENTER_ALBUM_NAME": "اسم الألبوم",
"CLOSE_OPTION": "إغلاق (Esc)",
@ -58,10 +59,10 @@
"FILE_UPLOAD": "تحميل الملف",
"UPLOAD_STAGE_MESSAGE": {
"0": "الإعداد للتحميل",
"1": "",
"2": "",
"3": "",
"4": "",
"1": "قراءة ملفات بيانات تعريف جوجل",
"2": "{{uploadCounter.finished, number}} / {{uploadCounter.total, number}} البيانات الملفات الوصفية المستخرجة",
"3": "{{uploadCounter.finished, number}} / {{uploadCounter.total, number}} ملفات معالجة",
"4": "إلغاء التحميلات المتبقية",
"5": "اكتمل النسخ الاحتياطي"
},
"FILE_NOT_UPLOADED_LIST": "لم يتم تحميل الملفات التالية",
@ -71,227 +72,227 @@
"ACCOUNT_EXISTS": "لديك حساب بالفعل",
"CREATE": "إنشاء",
"DOWNLOAD": "تنزيل",
"DOWNLOAD_OPTION": "",
"DOWNLOAD_FAVORITES": "",
"DOWNLOAD_UNCATEGORIZED": "",
"DOWNLOAD_HIDDEN_ITEMS": "",
"COPY_OPTION": "",
"TOGGLE_FULLSCREEN": "",
"ZOOM_IN_OUT": "",
"PREVIOUS": "",
"NEXT": "",
"title_photos": "",
"title_auth": "",
"title_accounts": "",
"UPLOAD_FIRST_PHOTO": "",
"IMPORT_YOUR_FOLDERS": "",
"UPLOAD_DROPZONE_MESSAGE": "",
"DOWNLOAD_OPTION": "تنزيل (D)",
"DOWNLOAD_FAVORITES": "تنزيل المفضلات",
"DOWNLOAD_UNCATEGORIZED": "التنزيل غير المصنف",
"DOWNLOAD_HIDDEN_ITEMS": "تنزيل العناصر المخفية",
"COPY_OPTION": "نسخ كـ PNG (Ctrl/Cmd - C)",
"TOGGLE_FULLSCREEN": "تبديل ملء الشاشة (F)",
"ZOOM_IN_OUT": "تكبير/تصغير",
"PREVIOUS": "السابق (←)",
"NEXT": "التالي (→)",
"title_photos": "صور Ente",
"title_auth": "مصادقة Ente",
"title_accounts": "حسابات Ente",
"UPLOAD_FIRST_PHOTO": "تحميل صورتك الأولى",
"IMPORT_YOUR_FOLDERS": "استيراد مجلداتك",
"UPLOAD_DROPZONE_MESSAGE": "إسقاط للنسخ الاحتياطي للملفاتك",
"WATCH_FOLDER_DROPZONE_MESSAGE": "",
"TRASH_FILES_TITLE": "",
"TRASH_FILE_TITLE": "",
"DELETE_FILES_TITLE": "",
"DELETE_FILES_MESSAGE": "",
"DELETE": "",
"DELETE_OPTION": "",
"FAVORITE_OPTION": "",
"UNFAVORITE_OPTION": "",
"MULTI_FOLDER_UPLOAD": "",
"UPLOAD_STRATEGY_CHOICE": "",
"UPLOAD_STRATEGY_SINGLE_COLLECTION": "",
"OR": "",
"UPLOAD_STRATEGY_COLLECTION_PER_FOLDER": "",
"SESSION_EXPIRED_MESSAGE": "",
"SESSION_EXPIRED": "",
"PASSWORD_GENERATION_FAILED": "",
"CHANGE_PASSWORD": "",
"password_changed_elsewhere": "",
"password_changed_elsewhere_message": "",
"GO_BACK": "",
"RECOVERY_KEY": "",
"SAVE_LATER": "",
"SAVE": "",
"TRASH_FILES_TITLE": "حذف الملفات؟",
"TRASH_FILE_TITLE": "حذف الملف؟",
"DELETE_FILES_TITLE": "حذف فورا؟",
"DELETE_FILES_MESSAGE": "سيتم حذف الملفات المحددة نهائيا من حساب Ente الخاص بك.",
"DELETE": "حذف",
"DELETE_OPTION": "حذف (DEL)",
"FAVORITE_OPTION": "مفضلة (L)",
"UNFAVORITE_OPTION": "غير مفضلة (L)",
"MULTI_FOLDER_UPLOAD": "تم اكتشاف مجلدات متعددة",
"UPLOAD_STRATEGY_CHOICE": "هل ترغب في تحميلهم إلى",
"UPLOAD_STRATEGY_SINGLE_COLLECTION": "ألبوم واحد",
"OR": "أو",
"UPLOAD_STRATEGY_COLLECTION_PER_FOLDER": "ألبومات منفصلة",
"SESSION_EXPIRED_MESSAGE": "لقد انتهت صلاحية جلستك، يرجى تسجيل الدخول مرة أخرى للمتابعة",
"SESSION_EXPIRED": "انتهت صلاحية الجلسة",
"PASSWORD_GENERATION_FAILED": "لم يتمكن متصفحك من إنشاء مفتاح قوي يفي بمعايير تشفير Ente، يرجى المحاولة باستخدام تطبيق الهاتف المحمول أو متصفح آخر",
"CHANGE_PASSWORD": "تغيير كلمة المرور",
"password_changed_elsewhere": "تم تغيير كلمة المرور في مكان آخر",
"password_changed_elsewhere_message": "يرجى تسجيل الدخول مرة أخرى على هذا الجهاز لاستخدام كلمة المرور الجديدة للمصادقة.",
"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": "",
"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": "",
"CONTACT_SUPPORT": "الاتصال بالدعم",
"REQUEST_FEATURE": "طلب ميزة",
"SUPPORT": "الدعم",
"CONFIRM": "تأكيد",
"cancel": "إلغاء",
"LOGOUT": "تسجيل الخروج",
"delete_account": "حذف الحساب",
"delete_account_manually_message": "",
"LOGOUT_MESSAGE": "",
"CHANGE_EMAIL": "",
"OK": "",
"SUCCESS": "",
"ERROR": "",
"MESSAGE": "",
"LOGOUT_MESSAGE": "هل أنت متأكد من أنك تريد تسجيل الخروج؟",
"CHANGE_EMAIL": "تغيير البريد الإلكتروني",
"OK": "حسنا",
"SUCCESS": "تم بنجاح",
"ERROR": "خطأ",
"MESSAGE": "رسالة",
"OFFLINE_MSG": "",
"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": "",
"CURRENT_USAGE": "",
"TWO_MONTHS_FREE": "",
"POPULAR": "",
"free_plan_option": "",
"free_plan_description": "",
"active": "",
"subscription_info_free": "",
"DOWNLOAD_APP": "تنزيل تطبيق سطح المكتب",
"EXPORT": "تصدير البيانات",
"SUBSCRIPTION": "اشتراك",
"SUBSCRIBE": "اشترك",
"MANAGEMENT_PORTAL": "إدارة طريقة الدفع",
"MANAGE_FAMILY_PORTAL": "إدارة العائلة",
"LEAVE_FAMILY_PLAN": "مغادرة خطة العائلة",
"LEAVE": "مغادرة",
"LEAVE_FAMILY_CONFIRM": "هل أنت متأكد من أنك تريد مغادرة الخطة العائلية؟",
"CHOOSE_PLAN": "اختر خطتك",
"MANAGE_PLAN": "إدارة اشتراكك",
"CURRENT_USAGE": "الاستخدام الحالي هو <strong>{{usage}}</strong>",
"TWO_MONTHS_FREE": "احصل على شهرين مجانا في الخطط السنوية",
"POPULAR": "رائج",
"free_plan_option": "المتابعة مع الخطة المجانية",
"free_plan_description": "{{storage}} مجاني للأبد",
"active": "نشط",
"subscription_info_free": "أنت في الخطة المجانية",
"subscription_info_family": "",
"subscription_info_expired": "",
"subscription_info_renewal_cancelled": "",
"subscription_info_storage_quota_exceeded": "",
"subscription_status_renewal_active": "",
"subscription_status_renewal_cancelled": "",
"subscription_status_renewal_cancelled": "ينتهي في {{date, date}}",
"add_on_valid_till": "",
"subscription_expired": "",
"storage_quota_exceeded": "",
"SUBSCRIPTION_PURCHASE_SUCCESS": "",
"subscription_expired": "إنتهت صلاحية الاشتراك",
"storage_quota_exceeded": "تم تجاوز حد التخزين",
"SUBSCRIPTION_PURCHASE_SUCCESS": "<p>لقد تلقينا دفعتك</p><p>اشتراكك صالح حتى <strong>{{date, date}}</strong></p>",
"SUBSCRIPTION_PURCHASE_CANCELLED": "",
"SUBSCRIPTION_PURCHASE_FAILED": "",
"SUBSCRIPTION_UPDATE_FAILED": "",
"UPDATE_PAYMENT_METHOD_MESSAGE": "",
"STRIPE_AUTHENTICATION_FAILED": "",
"UPDATE_PAYMENT_METHOD": "",
"MONTHLY": "",
"YEARLY": "",
"MONTH_SHORT": "",
"YEAR": "",
"update_subscription_title": "",
"UPDATE_SUBSCRIPTION_MESSAGE": "",
"UPDATE_SUBSCRIPTION": "",
"CANCEL_SUBSCRIPTION": "",
"MONTHLY": "شهريا",
"YEARLY": "سنويا",
"MONTH_SHORT": "شهر",
"YEAR": "سنة",
"update_subscription_title": "تأكيد تغيير الخطة",
"UPDATE_SUBSCRIPTION_MESSAGE": "هل أنت متأكد من أنك تريد تغيير خطتك؟",
"UPDATE_SUBSCRIPTION": "تغيير الخطة",
"CANCEL_SUBSCRIPTION": "إلغاء الاشتراك",
"CANCEL_SUBSCRIPTION_MESSAGE": "",
"CANCEL_SUBSCRIPTION_WITH_ADDON_MESSAGE": "",
"SUBSCRIPTION_CANCEL_FAILED": "",
"SUBSCRIPTION_CANCEL_SUCCESS": "",
"REACTIVATE_SUBSCRIPTION": "",
"SUBSCRIPTION_CANCEL_FAILED": "فشل في إلغاء الاشتراك",
"SUBSCRIPTION_CANCEL_SUCCESS": "تم إلغاء الاشتراك بنجاح",
"REACTIVATE_SUBSCRIPTION": "إعادة تنشيط الاشتراك",
"REACTIVATE_SUBSCRIPTION_MESSAGE": "",
"SUBSCRIPTION_ACTIVATE_SUCCESS": "",
"SUBSCRIPTION_ACTIVATE_FAILED": "",
"SUBSCRIPTION_PURCHASE_SUCCESS_TITLE": "",
"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": "",
"RENAME": "اعادة تسمية",
"RENAME_FILE": "إعادة تسمية ملف",
"RENAME_COLLECTION": "إعادة تسمية ألبوم",
"DELETE_COLLECTION_TITLE": "حذف ألبوم؟",
"DELETE_COLLECTION": "حذف ألبوم",
"DELETE_COLLECTION_MESSAGE": "",
"DELETE_PHOTOS": "",
"KEEP_PHOTOS": "",
"SHARE_COLLECTION": "",
"SHARE_WITH_SELF": "",
"DELETE_PHOTOS": "حذف الصور",
"KEEP_PHOTOS": "الاحتفاظ بالصور",
"SHARE_COLLECTION": "مشاركة الألبوم",
"SHARE_WITH_SELF": "عفوا، لا يمكنك المشاركة مع نفسك",
"ALREADY_SHARED": "",
"SHARING_BAD_REQUEST_ERROR": "",
"SHARING_DISABLED_FOR_FREE_ACCOUNTS": "",
"DOWNLOAD_COLLECTION": "",
"SHARING_BAD_REQUEST_ERROR": "لا يسمح بمشاركة الألبوم",
"SHARING_DISABLED_FOR_FREE_ACCOUNTS": "المشاركة معطلة للحسابات المجانية",
"DOWNLOAD_COLLECTION": "تنزيل الألبوم",
"CREATE_ALBUM_FAILED": "",
"SEARCH": "",
"SEARCH_RESULTS": "",
"NO_RESULTS": "",
"SEARCH_HINT": "",
"SEARCH": "بحث",
"SEARCH_RESULTS": "نتائج البحث",
"NO_RESULTS": "لا توجد نتائج",
"SEARCH_HINT": "البحث عن الألبومات، التواريخ، والأوصاف...",
"SEARCH_TYPE": {
"COLLECTION": "",
"LOCATION": "",
"CITY": "",
"DATE": "",
"FILE_NAME": "",
"THING": "",
"FILE_CAPTION": "",
"FILE_TYPE": "",
"CLIP": ""
"COLLECTION": "ألبوم",
"LOCATION": "الموقع",
"CITY": "الموقع",
"DATE": "تاريخ",
"FILE_NAME": "إسم الملف",
"THING": "المحتوى",
"FILE_CAPTION": "وصف",
"FILE_TYPE": "نوع الملف",
"CLIP": "سحر"
},
"photos_count_zero": "",
"photos_count_one": "",
"photos_count": "",
"TERMS_AND_CONDITIONS": "",
"ADD_TO_COLLECTION": "",
"SELECTED": "",
"photos_count_zero": "لا توجد ذكريات",
"photos_count_one": "ذكرى واحدة",
"photos_count": "{{count, number}} ذكريات",
"TERMS_AND_CONDITIONS": "أوافق على <a>شروط الخدمة</a> و<b>سياسة الخصوصية</b>",
"ADD_TO_COLLECTION": "إضافة إلى الألبوم",
"SELECTED": "محدد",
"PEOPLE": "",
"indexing_scheduled": "",
"indexing_scheduled": "الفهرسة مجدولة...",
"indexing_photos": "",
"indexing_fetching": "",
"indexing_people": "",
"indexing_done": "",
"UNIDENTIFIED_FACES": "",
"UNIDENTIFIED_FACES": "وجوه غير محددة",
"OBJECTS": "",
"TEXT": "",
"INFO": "",
"INFO_OPTION": "",
"FILE_NAME": "",
"CAPTION_PLACEHOLDER": "",
"LOCATION": "",
"SHOW_ON_MAP": "",
"MAP": "",
"MAP_SETTINGS": "",
"ENABLE_MAPS": "",
"ENABLE_MAP": "",
"DISABLE_MAPS": "",
"TEXT": "نص",
"INFO": "معلومات ",
"INFO_OPTION": "معلومات (I)",
"FILE_NAME": "إسم الملف",
"CAPTION_PLACEHOLDER": "إضافة وصف",
"LOCATION": "الموقع",
"SHOW_ON_MAP": "عرض على OpenStreetMap",
"MAP": "خريطة",
"MAP_SETTINGS": "إعدادات الخريطة",
"ENABLE_MAPS": "تمكين الخرائط ؟",
"ENABLE_MAP": "تمكين الخريطة",
"DISABLE_MAPS": "تعطيل الخرائط؟",
"ENABLE_MAP_DESCRIPTION": "",
"DISABLE_MAP_DESCRIPTION": "",
"DISABLE_MAP": "",
"DETAILS": "",
"view_exif": "",
"no_exif": "",
"exif": "",
"ISO": "",
"DISABLE_MAP": "تعطيل الخريطة",
"DETAILS": "تفاصيل",
"view_exif": "عرض جميع بيانات Exif",
"no_exif": "لا توجد بيانات Exif",
"exif": "Exif",
"ISO": "ISO",
"TWO_FACTOR": "",
"TWO_FACTOR_AUTHENTICATION": "",
"TWO_FACTOR_AUTHENTICATION": "المصادقة الثنائية",
"TWO_FACTOR_QR_INSTRUCTION": "",
"ENTER_CODE_MANUALLY": "",
"ENTER_CODE_MANUALLY": "أدخل الرمز يدويا",
"TWO_FACTOR_MANUAL_CODE_INSTRUCTION": "",
"SCAN_QR_CODE": "",
"SCAN_QR_CODE": "مسح رمز QR بدلاً من ذلك",
"ENABLE_TWO_FACTOR": "",
"enable": "",
"enabled": "",
"enable": "تفعيل",
"enabled": "مفعل",
"LOST_DEVICE": "",
"INCORRECT_CODE": "",
"INCORRECT_CODE": "رمز غير صحيح",
"TWO_FACTOR_INFO": "",
"DISABLE_TWO_FACTOR_LABEL": "",
"UPDATE_TWO_FACTOR_LABEL": "",
"disable": "",
"reconfigure": "",
"disable": "تعطيل",
"reconfigure": "إعادة التهيئة",
"UPDATE_TWO_FACTOR": "",
"UPDATE_TWO_FACTOR_MESSAGE": "",
"UPDATE": "",
"UPDATE": "تحديث",
"DISABLE_TWO_FACTOR": "",
"DISABLE_TWO_FACTOR_MESSAGE": "",
"TWO_FACTOR_DISABLE_FAILED": "",
"EXPORT_DATA": "",
"EXPORT_DATA": "تصدير البيانات",
"select_folder": "",
"select_zips": "",
"faq": "",
"faq": "الأسئلة الشائعة",
"takeout_hint": "",
"DESTINATION": "",
"START": "",
"LAST_EXPORT_TIME": "",
"DESTINATION": "الوجهة",
"START": "بدء",
"LAST_EXPORT_TIME": "آخر وقت تصدير",
"EXPORT_AGAIN": "",
"LOCAL_STORAGE_NOT_ACCESSIBLE": "",
"LOCAL_STORAGE_NOT_ACCESSIBLE": "التخزين المحلي غير قابل للوصول",
"LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE": "",
"SEND_OTT": "",
"EMAIl_ALREADY_OWNED": "",
"EMAIl_ALREADY_OWNED": "البريد الإلكتروني مأخوذ بالفعل",
"ETAGS_BLOCKED": "",
"LIVE_PHOTOS_DETECTED": "",
"RETRY_FAILED": "",
@ -321,22 +322,22 @@
"MOVE_TO_COLLECTION": "",
"UNARCHIVE": "",
"UNARCHIVE_COLLECTION": "",
"HIDE_COLLECTION": "",
"UNHIDE_COLLECTION": "",
"MOVE": "",
"ADD": "",
"REMOVE": "",
"YES_REMOVE": "",
"HIDE_COLLECTION": "إخفاء الألبوم",
"UNHIDE_COLLECTION": "إلغاء إخفاء الألبوم",
"MOVE": "نقل",
"ADD": "إضافة",
"REMOVE": "ازالة",
"YES_REMOVE": "نعم، إزالة",
"REMOVE_FROM_COLLECTION": "",
"TRASH": "",
"MOVE_TO_TRASH": "",
"TRASH": "سلة المهملات",
"MOVE_TO_TRASH": "نقل إلى سلة المهملات",
"TRASH_FILES_MESSAGE": "",
"TRASH_FILE_MESSAGE": "",
"DELETE_PERMANENTLY": "",
"RESTORE": "",
"DELETE_PERMANENTLY": "حذف بشكل دائم",
"RESTORE": "استعادة",
"RESTORE_TO_COLLECTION": "",
"EMPTY_TRASH": "",
"EMPTY_TRASH_TITLE": "",
"EMPTY_TRASH": "إفراغ سلة المهملات",
"EMPTY_TRASH_TITLE": "إفراغ سلة المهملات؟",
"EMPTY_TRASH_MESSAGE": "",
"LEAVE_SHARED_ALBUM": "",
"LEAVE_ALBUM": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "Wie hast du von Ente erfahren? (optional)",
"REFERRAL_INFO": "Wir tracken keine App-Installationen. Es würde uns jedoch helfen, wenn du uns mitteilst, wie du von uns erfahren hast!",
"PASSPHRASE_MATCH_ERROR": "Die Passwörter stimmen nicht überein",
"create_albums": "",
"CREATE_COLLECTION": "Neues Album",
"ENTER_ALBUM_NAME": "Albumname",
"CLOSE_OPTION": "Schließen (Esc)",
@ -301,7 +302,7 @@
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "Das Vorschaubild konnte nicht erzeugt werden",
"UNSUPPORTED_FILES": "Nicht unterstützte Dateien",
"SUCCESSFUL_UPLOADS": "Erfolgreiche Uploads",
"SKIPPED_INFO": "Diese wurden übersprungen, da es Dateien mit gleichen Namen im selben Album gibt",
"SKIPPED_INFO": "",
"UNSUPPORTED_INFO": "Ente unterstützt diese Dateiformate noch nicht",
"BLOCKED_UPLOADS": "Blockierte Uploads",
"INPROGRESS_METADATA_EXTRACTION": "In Bearbeitung",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "Πώς ακούσατε για το Ente; (προαιρετικό)",
"REFERRAL_INFO": "Δεν παρακολουθούμε τις εγκαταστάσεις εφαρμογών. Θα μας βοηθούσε αν μας λέγατε που μας βρήκατε!",
"PASSPHRASE_MATCH_ERROR": "Οι κωδικοί πρόσβασης δεν ταιριάζουν",
"create_albums": "",
"CREATE_COLLECTION": "Νέο άλμπουμ",
"ENTER_ALBUM_NAME": "Όνομα άλμπουμ",
"CLOSE_OPTION": "Κλείσιμο (Esc)",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "How did you hear about Ente? (optional)",
"REFERRAL_INFO": "We don't track app installs, It'd help us if you told us where you found us!",
"PASSPHRASE_MATCH_ERROR": "Passwords don't match",
"create_albums": "Create albums",
"CREATE_COLLECTION": "New album",
"ENTER_ALBUM_NAME": "Album name",
"CLOSE_OPTION": "Close (Esc)",
@ -301,7 +302,7 @@
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "Thumbnail generation failed",
"UNSUPPORTED_FILES": "Unsupported files",
"SUCCESSFUL_UPLOADS": "Successful uploads",
"SKIPPED_INFO": "Skipped these as there are files with matching names in the same album",
"SKIPPED_INFO": "Skipped these as there are files with matching name and content in the same album",
"UNSUPPORTED_INFO": "Ente does not support these file formats yet",
"BLOCKED_UPLOADS": "Blocked uploads",
"INPROGRESS_METADATA_EXTRACTION": "In progress",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "¿Cómo escuchaste acerca de Ente? (opcional)",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "Las contraseñas no coinciden",
"create_albums": "",
"CREATE_COLLECTION": "Nuevo álbum",
"ENTER_ALBUM_NAME": "Nombre del álbum",
"CLOSE_OPTION": "Cerrar (Esc)",
@ -301,7 +302,7 @@
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "Generación de miniaturas fallida",
"UNSUPPORTED_FILES": "Archivos no soportados",
"SUCCESSFUL_UPLOADS": "Subidas exitosas",
"SKIPPED_INFO": "Se han omitido ya que hay archivos con nombres coincidentes en el mismo álbum",
"SKIPPED_INFO": "",
"UNSUPPORTED_INFO": "ente no soporta estos formatos de archivo aún",
"BLOCKED_UPLOADS": "Subidas bloqueadas",
"INPROGRESS_METADATA_EXTRACTION": "En proceso",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "Miten kuulit Entestä? (valinnainen)",
"REFERRAL_INFO": "Emme seuraa sovelluksen asennuksia. Se auttaisi meitä, jos kertoisit mistä löysit meidät!",
"PASSPHRASE_MATCH_ERROR": "Salasanat eivät täsmää",
"create_albums": "",
"CREATE_COLLECTION": "Uusi albumi",
"ENTER_ALBUM_NAME": "Albumin nimi",
"CLOSE_OPTION": "Sulje (Esc)",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "Comment avez-vous entendu parler de Ente? (facultatif)",
"REFERRAL_INFO": "Nous ne suivons pas les installations d'applications. Il serait utile que vous nous disiez comment vous nous avez trouvés !",
"PASSPHRASE_MATCH_ERROR": "Les mots de passe ne correspondent pas",
"create_albums": "",
"CREATE_COLLECTION": "Nouvel album",
"ENTER_ALBUM_NAME": "Nom de l'album",
"CLOSE_OPTION": "Fermer (Échap)",
@ -301,7 +302,7 @@
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "Échec de création d'une miniature",
"UNSUPPORTED_FILES": "Fichiers non supportés",
"SUCCESSFUL_UPLOADS": "Chargements réussis",
"SKIPPED_INFO": "Ignorés car il y a des fichiers avec des noms identiques dans le même album",
"SKIPPED_INFO": "",
"UNSUPPORTED_INFO": "Ente ne supporte pas encore ces formats de fichiers",
"BLOCKED_UPLOADS": "Chargements bloqués",
"INPROGRESS_METADATA_EXTRACTION": "En cours",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "Dari mana Anda menemukan Ente? (opsional)",
"REFERRAL_INFO": "Kami tidak melacak pemasangan aplikasi, Ini akan membantu kami jika Anda memberi tahu kami di mana Anda menemukan kami!",
"PASSPHRASE_MATCH_ERROR": "Kata sandi tidak cocok",
"create_albums": "",
"CREATE_COLLECTION": "Album baru",
"ENTER_ALBUM_NAME": "Nama album",
"CLOSE_OPTION": "Tutup (Esc)",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "Come hai conosciuto Ente? (opzionale)",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "Le password non corrispondono",
"create_albums": "",
"CREATE_COLLECTION": "Nuovo album",
"ENTER_ALBUM_NAME": "Nome album",
"CLOSE_OPTION": "Chiudi (Esc)",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "어떻게 Ente에 대해 들으셨나요? (선택사항)",
"REFERRAL_INFO": "우리는 앱 설치를 추적하지 않습니다. 우리를 알게 된 곳을 남겨주시면 우리에게 도움이 될꺼에요!",
"PASSPHRASE_MATCH_ERROR": "비밀번호가 일치하지 않습니다",
"create_albums": "",
"CREATE_COLLECTION": "새 앨범",
"ENTER_ALBUM_NAME": "앨범 이름",
"CLOSE_OPTION": "닫기 (Esc)",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "Hoe hoorde je over Ente? (optioneel)",
"REFERRAL_INFO": "Wij gebruiken geen tracking. Het zou helpen als je ons vertelt waar je ons gevonden hebt!",
"PASSPHRASE_MATCH_ERROR": "Wachtwoorden komen niet overeen",
"create_albums": "",
"CREATE_COLLECTION": "Nieuw album",
"ENTER_ALBUM_NAME": "Albumnaam",
"CLOSE_OPTION": "Sluiten (Esc)",
@ -301,7 +302,7 @@
"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",
"SKIPPED_INFO": "",
"UNSUPPORTED_INFO": "Ente ondersteunt deze bestandsformaten nog niet",
"BLOCKED_UPLOADS": "Geblokkeerde uploads",
"INPROGRESS_METADATA_EXTRACTION": "In behandeling",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "Jak usłyszałeś/aś o Ente? (opcjonalnie)",
"REFERRAL_INFO": "Nie śledzimy instalacji aplikacji. Pomogłyby nam, gdybyś powiedział/a nam, gdzie nas znalazłeś/aś!",
"PASSPHRASE_MATCH_ERROR": "Hasła nie pasują do siebie",
"create_albums": "Utwórz albumy",
"CREATE_COLLECTION": "Nowy album",
"ENTER_ALBUM_NAME": "Nazwa albumu",
"CLOSE_OPTION": "Zamknij (Esc)",
@ -301,7 +302,7 @@
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "Generowanie miniatur nie powiodło się",
"UNSUPPORTED_FILES": "Nieobsługiwane pliki",
"SUCCESSFUL_UPLOADS": "Pomyślne przesłania",
"SKIPPED_INFO": "Pominięto te pliki, ponieważ są pliki z pasującymi nazwami w tym samym albumie",
"SKIPPED_INFO": "Pominięto te pliki, ponieważ są pliki z pasującymi nazwami i zawartością w tym samym albumie",
"UNSUPPORTED_INFO": "Ente nie obsługuje jeszcze tych formatów plików",
"BLOCKED_UPLOADS": "Zablokowane przesłania",
"INPROGRESS_METADATA_EXTRACTION": "W toku",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "Como você ouviu sobre o Ente? (opcional)",
"REFERRAL_INFO": "Não rastreamos instalações do aplicativo. Seria útil se você nos contasse onde nos encontrou!",
"PASSPHRASE_MATCH_ERROR": "As senhas não coincidem",
"create_albums": "Criar álbuns",
"CREATE_COLLECTION": "Novo álbum",
"ENTER_ALBUM_NAME": "Nome do álbum",
"CLOSE_OPTION": "Fechar (Esc)",
@ -301,7 +302,7 @@
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "Falha ao gerar miniaturas",
"UNSUPPORTED_FILES": "Arquivos não suportados",
"SUCCESSFUL_UPLOADS": "Envios bem sucedidos",
"SKIPPED_INFO": "Ignorar estes como existem arquivos com nomes correspondentes no mesmo álbum",
"SKIPPED_INFO": "Estes foram pulados, pois há arquivos com nome e conteúdo correspondentes no mesmo álbum",
"UNSUPPORTED_INFO": "ente ainda não suporta estes formatos de arquivo",
"BLOCKED_UPLOADS": "Envios bloqueados",
"INPROGRESS_METADATA_EXTRACTION": "Em andamento",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "Novo álbum",
"ENTER_ALBUM_NAME": "Nome do álbum",
"CLOSE_OPTION": "Fechar (Esc)",

View File

@ -1,11 +1,11 @@
{
"HERO_SLIDE_1_TITLE": "<div>Личные резервные копии</div><div>для твоих воспоминаний</div>",
"HERO_SLIDE_1_TITLE": "<div>Приватные резервные копии</div><div>для ваших воспоминаний</div>",
"HERO_SLIDE_1": "Сквозное шифрование по умолчанию",
"HERO_SLIDE_2_TITLE": "<div>Надежно хранится</div><div>в убежище от радиоактивных осадков</div>",
"HERO_SLIDE_2": "Созданный для того, чтобы пережить",
"HERO_SLIDE_3_TITLE": "<div>Доступно</div><div> везде</div>",
"HERO_SLIDE_3": "Android, iOS, Веб, ПК",
"LOGIN": "Авторизоваться",
"LOGIN": "Войти",
"SIGN_UP": "Регистрация",
"NEW_USER": "Новенький в Ente",
"EXISTING_USER": "Существующий пользователь",
@ -19,7 +19,7 @@
"ENTER_OTT": "Проверочный код",
"RESEND_MAIL": "Отправить код еще раз",
"VERIFY": "Подтвердить",
"UNKNOWN_ERROR": "Что-то пошло не так, Попробуйте еще раз",
"UNKNOWN_ERROR": "Что-то пошло не так, попробуйте еще раз",
"INVALID_CODE": "Неверный код подтверждения",
"EXPIRED_CODE": "Срок действия вашего проверочного кода истек",
"SENDING": "Отправка...",
@ -28,7 +28,7 @@
"link_password_description": "Введите пароль, чтобы разблокировать альбом",
"unlock": "Разблокировать",
"SET_PASSPHRASE": "Установить пароль",
"VERIFY_PASSPHRASE": "Войти",
"VERIFY_PASSPHRASE": "Зарегистрироваться",
"INCORRECT_PASSPHRASE": "Неверный пароль",
"ENTER_ENC_PASSPHRASE": "Пожалуйста, введите пароль, который мы можем использовать для шифрования ваших данных",
"PASSPHRASE_DISCLAIMER": "Мы не храним ваш пароль, поэтому, если вы его забудете,\n<strong>мы ничем не сможем вам помочь\n</strong>восстановите ваши данные без ключа восстановления.",
@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "Как вы узнали о Ente? (необязательно)",
"REFERRAL_INFO": "Будет полезно, если вы укажете, где нашли нас, так как мы не отслеживаем установки приложения!",
"PASSPHRASE_MATCH_ERROR": "Пароли не совпадают",
"create_albums": "Создать альбомы",
"CREATE_COLLECTION": "Новый альбом",
"ENTER_ALBUM_NAME": "Название альбома",
"CLOSE_OPTION": "Закрыть (Esc)",
@ -175,7 +176,7 @@
"UPDATE_PAYMENT_METHOD": "Обновить платёжную информацию",
"MONTHLY": "Ежемесячно",
"YEARLY": "Ежегодно",
"MONTH_SHORT": о",
"MONTH_SHORT": ес",
"YEAR": "год",
"update_subscription_title": "Подтвердить изменение плана",
"UPDATE_SUBSCRIPTION_MESSAGE": "Хотите сменить текущий план?",
@ -301,7 +302,7 @@
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "Не удалось создать миниатюру",
"UNSUPPORTED_FILES": "Неподдерживаемые файлы",
"SUCCESSFUL_UPLOADS": "Успешные загрузки",
"SKIPPED_INFO": "Пропустил их, так как в одном альбоме есть файлы с одинаковыми названиями",
"SKIPPED_INFO": "Пропущено, так как в альбоме есть файлы с совпадающими именем и содержимым",
"UNSUPPORTED_INFO": "Ente пока не поддерживает эти форматы файлов",
"BLOCKED_UPLOADS": "Заблокированные загрузки",
"INPROGRESS_METADATA_EXTRACTION": "В процессе",
@ -335,8 +336,8 @@
"DELETE_PERMANENTLY": "Удалить навсегда",
"RESTORE": "Восстанавливать",
"RESTORE_TO_COLLECTION": "Восстановить в альбом",
"EMPTY_TRASH": "Пустой мусор",
"EMPTY_TRASH_TITLE": "Пустой мусор?",
"EMPTY_TRASH": "Очистить корзину",
"EMPTY_TRASH_TITLE": "Очистить корзину?",
"EMPTY_TRASH_MESSAGE": "Эти файлы будут безвозвратно удалены из вашей учетной записи Ente.",
"LEAVE_SHARED_ALBUM": "Да, уходи",
"LEAVE_ALBUM": "Оставить альбом",
@ -485,15 +486,15 @@
"indexing": "Индексирование",
"processed": "Обработано",
"indexing_status_running": "Выполняется",
"indexing_status_fetching": "",
"indexing_status_fetching": "Получение",
"indexing_status_scheduled": "Запланировано",
"indexing_status_done": "Готово",
"ml_search_disable": "Отключить машинное обучение",
"ml_search_disable_confirm": "Вы хотите отключить машинное обучение на всех ваших устройствах?",
"ml_consent": "",
"ml_consent_title": "",
"ml_consent_description": "",
"ml_consent_confirmation": "",
"ml_consent": "Включить машинное обучение",
"ml_consent_title": "Включить машинное обучение?",
"ml_consent_description": "<p>Если вы включите машинное обучение, Ente будет извлекать информацию из файлов (например, геометрию лица), включая те, которыми с вами поделились.</p><p>Это будет происходить на вашем устройстве, и любая сгенерированная биометрическая информация будет зашифрована с использованием сквозного (End-to-End) шифрования</p><p>Пожалуйста нажмите здесь для получения дополнительной информации об этой функции в нашей политике конфиденциальности<a></a></p>",
"ml_consent_confirmation": "Я понимаю, и хочу разрешить машинное обучение",
"labs": "Лаборатории",
"YOURS": "твой",
"passphrase_strength_weak": "Надежность пароля: слабая",
@ -532,8 +533,8 @@
"EXPORT_PROGRESS": "<a>{{progress.success, number}} / {{progress.total, number}}</a> синхронизированные элементы",
"MIGRATING_EXPORT": "Подготовка...",
"RENAMING_COLLECTION_FOLDERS": "Переименование папок альбомов...",
"TRASHING_DELETED_FILES": "Удаление удаленных файлов...",
"TRASHING_DELETED_COLLECTIONS": "Удаление удаленных альбомов...",
"TRASHING_DELETED_FILES": "Очистка удаленных файлов...",
"TRASHING_DELETED_COLLECTIONS": "Очистка удаленных альбомов...",
"CONTINUOUS_EXPORT": "Непрерывная синхронизация",
"PENDING_ITEMS": "Отложенные пункты",
"EXPORT_STARTING": "Запуск экспорта...",

View File

@ -7,7 +7,7 @@
"HERO_SLIDE_3": "Android, iOS, webb, skrivbord",
"LOGIN": "Logga in",
"SIGN_UP": "Registrera",
"NEW_USER": "",
"NEW_USER": "Ny hos Ente",
"EXISTING_USER": "Befintlig användare",
"ENTER_NAME": "Ange namn",
"PUBLIC_UPLOADER_NAME_MESSAGE": "Lägg till ett namn så att dina vänner vet vem de ska tacka för dessa fantastiska bilder!",
@ -26,7 +26,7 @@
"SENT": "Skickat!",
"password": "Lösenord",
"link_password_description": "Ange lösenord för att låsa upp albumet",
"unlock": "",
"unlock": "Lås upp",
"SET_PASSPHRASE": "Välj lösenord",
"VERIFY_PASSPHRASE": "Logga in",
"INCORRECT_PASSPHRASE": "Felaktigt lösenord",
@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "Hur hörde du talas om Ente? (valfritt)",
"REFERRAL_INFO": "Vi spårar inte appinstallationer, Det skulle hjälpa oss om du berättade var du hittade oss!",
"PASSPHRASE_MATCH_ERROR": "Lösenorden matchar inte",
"create_albums": "Skapa album",
"CREATE_COLLECTION": "Nytt album",
"ENTER_ALBUM_NAME": "Albumnamn",
"CLOSE_OPTION": "Stäng (Esc)",
@ -60,11 +61,11 @@
"0": "Förbereder att ladda upp",
"1": "Läser Google metadatafiler",
"2": "Metadata för {{uploadCounter.finished, number}} / {{uploadCounter.total, number}} filer extraherat",
"3": "",
"4": "",
"5": ""
"3": "{{uploadCounter.finished, number}} / {{uploadCounter.total, number}} filer behandlade",
"4": "Avbryter återstående uppladdningar",
"5": "Säkerhetskopiering slutförd"
},
"FILE_NOT_UPLOADED_LIST": "",
"FILE_NOT_UPLOADED_LIST": "Följande filer laddades ej upp",
"INITIAL_LOAD_DELAY_WARNING": "",
"USER_DOES_NOT_EXIST": "",
"NO_ACCOUNT": "",
@ -650,7 +651,7 @@
"redirect_close_instructions": "",
"redirect_again": "",
"autogenerated_first_album_name": "",
"autogenerated_default_album_name": "",
"autogenerated_default_album_name": "Nytt album",
"developer_settings": "Utvecklarinställningar",
"server_endpoint": "",
"more_information": "",

View File

@ -0,0 +1,659 @@
{
"HERO_SLIDE_1_TITLE": "",
"HERO_SLIDE_1": "",
"HERO_SLIDE_2_TITLE": "",
"HERO_SLIDE_2": "",
"HERO_SLIDE_3_TITLE": "",
"HERO_SLIDE_3": "",
"LOGIN": "",
"SIGN_UP": "",
"NEW_USER": "",
"EXISTING_USER": "",
"ENTER_NAME": "",
"PUBLIC_UPLOADER_NAME_MESSAGE": "",
"ENTER_EMAIL": "",
"EMAIL_ERROR": "",
"REQUIRED": "",
"EMAIL_SENT": "",
"CHECK_INBOX": "",
"ENTER_OTT": "",
"RESEND_MAIL": "",
"VERIFY": "",
"UNKNOWN_ERROR": "",
"INVALID_CODE": "",
"EXPIRED_CODE": "",
"SENDING": "",
"SENT": "",
"password": "",
"link_password_description": "",
"unlock": "",
"SET_PASSPHRASE": "",
"VERIFY_PASSPHRASE": "",
"INCORRECT_PASSPHRASE": "",
"ENTER_ENC_PASSPHRASE": "",
"PASSPHRASE_DISCLAIMER": "",
"WELCOME_TO_ENTE_HEADING": "",
"WELCOME_TO_ENTE_SUBHEADING": "",
"WHERE_YOUR_BEST_PHOTOS_LIVE": "",
"KEY_GENERATION_IN_PROGRESS_MESSAGE": "",
"PASSPHRASE_HINT": "",
"CONFIRM_PASSPHRASE": "",
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",
"ENTER_FILE_NAME": "",
"CLOSE": "",
"NO": "",
"NOTHING_HERE": "",
"upload": "",
"import": "",
"ADD_PHOTOS": "",
"ADD_MORE_PHOTOS": "",
"add_photos_count_one": "",
"add_photos_count": "",
"select_photos": "",
"FILE_UPLOAD": "",
"UPLOAD_STAGE_MESSAGE": {
"0": "",
"1": "",
"2": "",
"3": "",
"4": "",
"5": ""
},
"FILE_NOT_UPLOADED_LIST": "",
"INITIAL_LOAD_DELAY_WARNING": "",
"USER_DOES_NOT_EXIST": "",
"NO_ACCOUNT": "",
"ACCOUNT_EXISTS": "",
"CREATE": "",
"DOWNLOAD": "",
"DOWNLOAD_OPTION": "",
"DOWNLOAD_FAVORITES": "",
"DOWNLOAD_UNCATEGORIZED": "",
"DOWNLOAD_HIDDEN_ITEMS": "",
"COPY_OPTION": "",
"TOGGLE_FULLSCREEN": "",
"ZOOM_IN_OUT": "",
"PREVIOUS": "",
"NEXT": "",
"title_photos": "",
"title_auth": "",
"title_accounts": "",
"UPLOAD_FIRST_PHOTO": "",
"IMPORT_YOUR_FOLDERS": "",
"UPLOAD_DROPZONE_MESSAGE": "",
"WATCH_FOLDER_DROPZONE_MESSAGE": "",
"TRASH_FILES_TITLE": "",
"TRASH_FILE_TITLE": "",
"DELETE_FILES_TITLE": "",
"DELETE_FILES_MESSAGE": "",
"DELETE": "",
"DELETE_OPTION": "",
"FAVORITE_OPTION": "",
"UNFAVORITE_OPTION": "",
"MULTI_FOLDER_UPLOAD": "",
"UPLOAD_STRATEGY_CHOICE": "",
"UPLOAD_STRATEGY_SINGLE_COLLECTION": "",
"OR": "",
"UPLOAD_STRATEGY_COLLECTION_PER_FOLDER": "",
"SESSION_EXPIRED_MESSAGE": "",
"SESSION_EXPIRED": "",
"PASSWORD_GENERATION_FAILED": "",
"CHANGE_PASSWORD": "",
"password_changed_elsewhere": "",
"password_changed_elsewhere_message": "",
"GO_BACK": "",
"RECOVERY_KEY": "",
"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_manually_message": "",
"LOGOUT_MESSAGE": "",
"CHANGE_EMAIL": "",
"OK": "",
"SUCCESS": "",
"ERROR": "",
"MESSAGE": "",
"OFFLINE_MSG": "",
"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": "",
"CURRENT_USAGE": "",
"TWO_MONTHS_FREE": "",
"POPULAR": "",
"free_plan_option": "",
"free_plan_description": "",
"active": "",
"subscription_info_free": "",
"subscription_info_family": "",
"subscription_info_expired": "",
"subscription_info_renewal_cancelled": "",
"subscription_info_storage_quota_exceeded": "",
"subscription_status_renewal_active": "",
"subscription_status_renewal_cancelled": "",
"add_on_valid_till": "",
"subscription_expired": "",
"storage_quota_exceeded": "",
"SUBSCRIPTION_PURCHASE_SUCCESS": "",
"SUBSCRIPTION_PURCHASE_CANCELLED": "",
"SUBSCRIPTION_PURCHASE_FAILED": "",
"SUBSCRIPTION_UPDATE_FAILED": "",
"UPDATE_PAYMENT_METHOD_MESSAGE": "",
"STRIPE_AUTHENTICATION_FAILED": "",
"UPDATE_PAYMENT_METHOD": "",
"MONTHLY": "",
"YEARLY": "",
"MONTH_SHORT": "",
"YEAR": "",
"update_subscription_title": "",
"UPDATE_SUBSCRIPTION_MESSAGE": "",
"UPDATE_SUBSCRIPTION": "",
"CANCEL_SUBSCRIPTION": "",
"CANCEL_SUBSCRIPTION_MESSAGE": "",
"CANCEL_SUBSCRIPTION_WITH_ADDON_MESSAGE": "",
"SUBSCRIPTION_CANCEL_FAILED": "",
"SUBSCRIPTION_CANCEL_SUCCESS": "",
"REACTIVATE_SUBSCRIPTION": "",
"REACTIVATE_SUBSCRIPTION_MESSAGE": "",
"SUBSCRIPTION_ACTIVATE_SUCCESS": "",
"SUBSCRIPTION_ACTIVATE_FAILED": "",
"SUBSCRIPTION_PURCHASE_SUCCESS_TITLE": "",
"CANCEL_SUBSCRIPTION_ON_MOBILE": "",
"CANCEL_SUBSCRIPTION_ON_MOBILE_MESSAGE": "",
"MAIL_TO_MANAGE_SUBSCRIPTION": "",
"RENAME": "",
"RENAME_FILE": "",
"RENAME_COLLECTION": "",
"DELETE_COLLECTION_TITLE": "",
"DELETE_COLLECTION": "",
"DELETE_COLLECTION_MESSAGE": "",
"DELETE_PHOTOS": "",
"KEEP_PHOTOS": "",
"SHARE_COLLECTION": "",
"SHARE_WITH_SELF": "",
"ALREADY_SHARED": "",
"SHARING_BAD_REQUEST_ERROR": "",
"SHARING_DISABLED_FOR_FREE_ACCOUNTS": "",
"DOWNLOAD_COLLECTION": "",
"CREATE_ALBUM_FAILED": "",
"SEARCH": "",
"SEARCH_RESULTS": "",
"NO_RESULTS": "",
"SEARCH_HINT": "",
"SEARCH_TYPE": {
"COLLECTION": "",
"LOCATION": "",
"CITY": "",
"DATE": "",
"FILE_NAME": "",
"THING": "",
"FILE_CAPTION": "",
"FILE_TYPE": "",
"CLIP": ""
},
"photos_count_zero": "",
"photos_count_one": "",
"photos_count": "",
"TERMS_AND_CONDITIONS": "",
"ADD_TO_COLLECTION": "",
"SELECTED": "",
"PEOPLE": "",
"indexing_scheduled": "",
"indexing_photos": "",
"indexing_fetching": "",
"indexing_people": "",
"indexing_done": "",
"UNIDENTIFIED_FACES": "",
"OBJECTS": "",
"TEXT": "",
"INFO": "",
"INFO_OPTION": "",
"FILE_NAME": "",
"CAPTION_PLACEHOLDER": "",
"LOCATION": "",
"SHOW_ON_MAP": "",
"MAP": "",
"MAP_SETTINGS": "",
"ENABLE_MAPS": "",
"ENABLE_MAP": "",
"DISABLE_MAPS": "",
"ENABLE_MAP_DESCRIPTION": "",
"DISABLE_MAP_DESCRIPTION": "",
"DISABLE_MAP": "",
"DETAILS": "",
"view_exif": "",
"no_exif": "",
"exif": "",
"ISO": "",
"TWO_FACTOR": "",
"TWO_FACTOR_AUTHENTICATION": "",
"TWO_FACTOR_QR_INSTRUCTION": "",
"ENTER_CODE_MANUALLY": "",
"TWO_FACTOR_MANUAL_CODE_INSTRUCTION": "",
"SCAN_QR_CODE": "",
"ENABLE_TWO_FACTOR": "",
"enable": "",
"enabled": "",
"LOST_DEVICE": "",
"INCORRECT_CODE": "",
"TWO_FACTOR_INFO": "",
"DISABLE_TWO_FACTOR_LABEL": "",
"UPDATE_TWO_FACTOR_LABEL": "",
"disable": "",
"reconfigure": "",
"UPDATE_TWO_FACTOR": "",
"UPDATE_TWO_FACTOR_MESSAGE": "",
"UPDATE": "",
"DISABLE_TWO_FACTOR": "",
"DISABLE_TWO_FACTOR_MESSAGE": "",
"TWO_FACTOR_DISABLE_FAILED": "",
"EXPORT_DATA": "",
"select_folder": "",
"select_zips": "",
"faq": "",
"takeout_hint": "",
"DESTINATION": "",
"START": "",
"LAST_EXPORT_TIME": "",
"EXPORT_AGAIN": "",
"LOCAL_STORAGE_NOT_ACCESSIBLE": "",
"LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE": "",
"SEND_OTT": "",
"EMAIl_ALREADY_OWNED": "",
"ETAGS_BLOCKED": "",
"LIVE_PHOTOS_DETECTED": "",
"RETRY_FAILED": "",
"FAILED_UPLOADS": "",
"failed_uploads_hint": "",
"SKIPPED_FILES": "",
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "",
"UNSUPPORTED_FILES": "",
"SUCCESSFUL_UPLOADS": "",
"SKIPPED_INFO": "",
"UNSUPPORTED_INFO": "",
"BLOCKED_UPLOADS": "",
"INPROGRESS_METADATA_EXTRACTION": "",
"INPROGRESS_UPLOADS": "",
"TOO_LARGE_UPLOADS": "",
"LARGER_THAN_AVAILABLE_STORAGE_UPLOADS": "",
"LARGER_THAN_AVAILABLE_STORAGE_INFO": "",
"TOO_LARGE_INFO": "",
"THUMBNAIL_GENERATION_FAILED_INFO": "",
"UPLOAD_TO_COLLECTION": "",
"UNCATEGORIZED": "",
"ARCHIVE": "",
"FAVORITES": "",
"ARCHIVE_COLLECTION": "",
"ARCHIVE_SECTION_NAME": "",
"ALL_SECTION_NAME": "",
"MOVE_TO_COLLECTION": "",
"UNARCHIVE": "",
"UNARCHIVE_COLLECTION": "",
"HIDE_COLLECTION": "",
"UNHIDE_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": "",
"FIX_CREATION_TIME": "",
"FIX_CREATION_TIME_IN_PROGRESS": "",
"CREATION_TIME_UPDATED": "",
"UPDATE_CREATION_TIME_NOT_STARTED": "",
"UPDATE_CREATION_TIME_COMPLETED": "",
"UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR": "",
"CAPTION_CHARACTER_LIMIT": "",
"DATE_TIME_ORIGINAL": "",
"DATE_TIME_DIGITIZED": "",
"METADATA_DATE": "",
"CUSTOM_TIME": "",
"REOPEN_PLAN_SELECTOR_MODAL": "",
"OPEN_PLAN_SELECTOR_MODAL_FAILED": "",
"INSTALL": "",
"SHARING_DETAILS": "",
"MODIFY_SHARING": "",
"ADD_COLLABORATORS": "",
"ADD_NEW_EMAIL": "",
"shared_with_people_count_zero": "",
"shared_with_people_count_one": "",
"shared_with_people_count": "",
"participants_count_zero": "",
"participants_count_one": "",
"participants_count": "",
"ADD_VIEWERS": "",
"CHANGE_PERMISSIONS_TO_VIEWER": "",
"CHANGE_PERMISSIONS_TO_COLLABORATOR": "",
"CONVERT_TO_VIEWER": "",
"CONVERT_TO_COLLABORATOR": "",
"CHANGE_PERMISSION": "",
"REMOVE_PARTICIPANT": "",
"CONFIRM_REMOVE": "",
"MANAGE": "",
"ADDED_AS": "",
"COLLABORATOR_RIGHTS": "",
"REMOVE_PARTICIPANT_HEAD": "",
"OWNER": "",
"COLLABORATORS": "",
"ADD_MORE": "",
"VIEWERS": "",
"OR_ADD_EXISTING": "",
"REMOVE_PARTICIPANT_MESSAGE": "",
"NOT_FOUND": "",
"LINK_EXPIRED": "",
"LINK_EXPIRED_MESSAGE": "",
"MANAGE_LINK": "",
"LINK_TOO_MANY_REQUESTS": "",
"FILE_DOWNLOAD": "",
"link_password_lock": "",
"PUBLIC_COLLECT": "",
"LINK_DEVICE_LIMIT": "",
"NO_DEVICE_LIMIT": "",
"LINK_EXPIRY": "",
"NEVER": "",
"DISABLE_FILE_DOWNLOAD": "",
"DISABLE_FILE_DOWNLOAD_MESSAGE": "",
"SHARED_USING": "",
"SHARING_REFERRAL_CODE": "",
"LIVE": "",
"DISABLE_PASSWORD": "",
"DISABLE_PASSWORD_MESSAGE": "",
"PASSWORD_LOCK": "",
"LOCK": "",
"DOWNLOAD_UPLOAD_LOGS": "",
"file": "",
"folder": "",
"google_takeout": "",
"DEDUPLICATE_FILES": "",
"NO_DUPLICATES_FOUND": "",
"FILES": "",
"EACH": "",
"DEDUPLICATE_BASED_ON_SIZE": "",
"STOP_ALL_UPLOADS_MESSAGE": "",
"STOP_UPLOADS_HEADER": "",
"YES_STOP_UPLOADS": "",
"STOP_DOWNLOADS_HEADER": "",
"YES_STOP_DOWNLOADS": "",
"STOP_ALL_DOWNLOADS_MESSAGE": "",
"albums_count_one": "",
"albums_count": "",
"ALL_ALBUMS": "",
"ALBUMS": "",
"ALL_HIDDEN_ALBUMS": "",
"HIDDEN_ALBUMS": "",
"HIDDEN_ITEMS": "",
"ENTER_TWO_FACTOR_OTP": "",
"CREATE_ACCOUNT": "",
"COPIED": "",
"WATCH_FOLDERS": "",
"upgrade_now": "",
"renew_now": "",
"STORAGE": "",
"USED": "",
"YOU": "",
"FAMILY": "",
"FREE": "",
"OF": "",
"WATCHED_FOLDERS": "",
"NO_FOLDERS_ADDED": "",
"FOLDERS_AUTOMATICALLY_MONITORED": "",
"UPLOAD_NEW_FILES_TO_ENTE": "",
"REMOVE_DELETED_FILES_FROM_ENTE": "",
"ADD_FOLDER": "",
"STOP_WATCHING": "",
"STOP_WATCHING_FOLDER": "",
"STOP_WATCHING_DIALOG_MESSAGE": "",
"YES_STOP": "",
"CHANGE_FOLDER": "",
"FAMILY_PLAN": "",
"DOWNLOAD_LOGS": "",
"DOWNLOAD_LOGS_MESSAGE": "",
"WEAK_DEVICE": "",
"drag_and_drop_hint": "",
"AUTHENTICATE": "",
"UPLOADED_TO_SINGLE_COLLECTION": "",
"UPLOADED_TO_SEPARATE_COLLECTIONS": "",
"NEVERMIND": "",
"UPDATE_AVAILABLE": "",
"UPDATE_INSTALLABLE_MESSAGE": "",
"INSTALL_NOW": "",
"INSTALL_ON_NEXT_LAUNCH": "",
"UPDATE_AVAILABLE_MESSAGE": "",
"DOWNLOAD_AND_INSTALL": "",
"IGNORE_THIS_VERSION": "",
"TODAY": "",
"YESTERDAY": "",
"NAME_PLACEHOLDER": "",
"ROOT_LEVEL_FILE_WITH_FOLDER_NOT_ALLOWED": "",
"ROOT_LEVEL_FILE_WITH_FOLDER_NOT_ALLOWED_MESSAGE": "",
"CHOSE_THEME": "",
"more_details": "",
"ml_search": "",
"ml_search_description": "",
"ml_search_footnote": "",
"indexing": "",
"processed": "",
"indexing_status_running": "",
"indexing_status_fetching": "",
"indexing_status_scheduled": "",
"indexing_status_done": "",
"ml_search_disable": "",
"ml_search_disable_confirm": "",
"ml_consent": "",
"ml_consent_title": "",
"ml_consent_description": "",
"ml_consent_confirmation": "",
"labs": "",
"YOURS": "",
"passphrase_strength_weak": "",
"passphrase_strength_moderate": "",
"passphrase_strength_strong": "",
"preferences": "",
"language": "",
"advanced": "",
"EXPORT_DIRECTORY_DOES_NOT_EXIST": "",
"EXPORT_DIRECTORY_DOES_NOT_EXIST_MESSAGE": "",
"SUBSCRIPTION_VERIFICATION_ERROR": "",
"storage_unit": {
"b": "",
"kb": "",
"mb": "",
"gb": "",
"tb": ""
},
"AFTER_TIME": {
"HOUR": "",
"DAY": "",
"WEEK": "",
"MONTH": "",
"YEAR": ""
},
"COPY_LINK": "",
"DONE": "",
"LINK_SHARE_TITLE": "",
"REMOVE_LINK": "",
"CREATE_PUBLIC_SHARING": "",
"PUBLIC_LINK_CREATED": "",
"PUBLIC_LINK_ENABLED": "",
"COLLECT_PHOTOS": "",
"PUBLIC_COLLECT_SUBTEXT": "",
"STOP_EXPORT": "",
"EXPORT_PROGRESS": "",
"MIGRATING_EXPORT": "",
"RENAMING_COLLECTION_FOLDERS": "",
"TRASHING_DELETED_FILES": "",
"TRASHING_DELETED_COLLECTIONS": "",
"CONTINUOUS_EXPORT": "",
"PENDING_ITEMS": "",
"EXPORT_STARTING": "",
"delete_account_reason_label": "",
"delete_account_reason_placeholder": "",
"delete_reason": {
"missing_feature": "",
"behaviour": "",
"found_another_service": "",
"not_listed": ""
},
"delete_account_feedback_label": "",
"delete_account_feedback_placeholder": "",
"delete_account_confirm_checkbox_label": "",
"delete_account_confirm": "",
"delete_account_confirm_message": "",
"feedback_required": "",
"feedback_required_found_another_service": "",
"RECOVER_TWO_FACTOR": "",
"at": "",
"AUTH_NEXT": "",
"AUTH_DOWNLOAD_MOBILE_APP": "",
"HIDDEN": "",
"HIDE": "",
"UNHIDE": "",
"UNHIDE_TO_COLLECTION": "",
"SORT_BY": "",
"NEWEST_FIRST": "",
"OLDEST_FIRST": "",
"CONVERSION_FAILED_NOTIFICATION_MESSAGE": "",
"SELECT_COLLECTION": "",
"PIN_ALBUM": "",
"UNPIN_ALBUM": "",
"DOWNLOAD_COMPLETE": "",
"DOWNLOADING_COLLECTION": "",
"DOWNLOAD_FAILED": "",
"DOWNLOAD_PROGRESS": "",
"CHRISTMAS": "",
"CHRISTMAS_EVE": "",
"NEW_YEAR": "",
"NEW_YEAR_EVE": "",
"IMAGE": "",
"VIDEO": "",
"LIVE_PHOTO": "",
"editor": {
"crop": ""
},
"CONVERT": "",
"CONFIRM_EDITOR_CLOSE_MESSAGE": "",
"CONFIRM_EDITOR_CLOSE_DESCRIPTION": "",
"BRIGHTNESS": "",
"CONTRAST": "",
"SATURATION": "",
"BLUR": "",
"INVERT_COLORS": "",
"ASPECT_RATIO": "",
"SQUARE": "",
"ROTATE_LEFT": "",
"ROTATE_RIGHT": "",
"FLIP_VERTICALLY": "",
"FLIP_HORIZONTALLY": "",
"DOWNLOAD_EDITED": "",
"SAVE_A_COPY_TO_ENTE": "",
"RESTORE_ORIGINAL": "",
"TRANSFORM": "",
"COLORS": "",
"FLIP": "",
"ROTATION": "",
"RESET": "",
"PHOTO_EDITOR": "",
"FASTER_UPLOAD": "",
"FASTER_UPLOAD_DESCRIPTION": "",
"CAST_ALBUM_TO_TV": "",
"ENTER_CAST_PIN_CODE": "",
"PAIR_DEVICE_TO_TV": "",
"TV_NOT_FOUND": "",
"AUTO_CAST_PAIR": "",
"AUTO_CAST_PAIR_DESC": "",
"PAIR_WITH_PIN": "",
"CHOOSE_DEVICE_FROM_BROWSER": "",
"PAIR_WITH_PIN_DESC": "",
"VISIT_CAST_ENTE_IO": "",
"CAST_AUTO_PAIR_FAILED": "",
"FREEHAND": "",
"APPLY_CROP": "",
"PHOTO_EDIT_REQUIRED_TO_SAVE": "",
"passkeys": "",
"passkey_fetch_failed": "",
"manage_passkey": "",
"delete_passkey": "",
"delete_passkey_confirmation": "",
"rename_passkey": "",
"add_passkey": "",
"enter_passkey_name": "",
"passkeys_description": "",
"CREATED_AT": "",
"passkey_add_failed": "",
"passkey_login_failed": "",
"passkey_login_invalid_url": "",
"passkey_login_already_claimed_session": "",
"passkey_login_generic_error": "",
"passkey_login_credential_hint": "",
"passkeys_not_supported": "",
"try_again": "",
"check_status": "",
"passkey_login_instructions": "",
"passkey_login": "",
"passkey": "",
"passkey_verify_description": "",
"waiting_for_verification": "",
"verification_still_pending": "",
"passkey_verified": "",
"redirecting_back_to_app": "",
"redirect_close_instructions": "",
"redirect_again": "",
"autogenerated_first_album_name": "",
"autogenerated_default_album_name": "",
"developer_settings": "",
"server_endpoint": "",
"more_information": "",
"save": ""
}

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "",
"REFERRAL_INFO": "",
"PASSPHRASE_MATCH_ERROR": "",
"create_albums": "",
"CREATE_COLLECTION": "",
"ENTER_ALBUM_NAME": "",
"CLOSE_OPTION": "",

View File

@ -41,6 +41,7 @@
"REFERRAL_CODE_HINT": "您是如何知道Ente的 (可选的)",
"REFERRAL_INFO": "我们不跟踪应用程序安装情况,如果您告诉我们您是在哪里找到我们的,将会有所帮助!",
"PASSPHRASE_MATCH_ERROR": "两次输入的密码不一致",
"create_albums": "创建相册",
"CREATE_COLLECTION": "新建相册",
"ENTER_ALBUM_NAME": "相册名称",
"CLOSE_OPTION": "关闭 (或按Esc键)",
@ -98,7 +99,7 @@
"MULTI_FOLDER_UPLOAD": "检测到多个文件夹",
"UPLOAD_STRATEGY_CHOICE": "你想要上传他们到",
"UPLOAD_STRATEGY_SINGLE_COLLECTION": "单个相册",
"OR": "或者",
"OR": "还是",
"UPLOAD_STRATEGY_COLLECTION_PER_FOLDER": "独立相册",
"SESSION_EXPIRED_MESSAGE": "您的会话已过期,请重新登录以继续",
"SESSION_EXPIRED": "会话已过期",
@ -301,7 +302,7 @@
"THUMBNAIL_GENERATION_FAILED_UPLOADS": "缩略图生成失败",
"UNSUPPORTED_FILES": "不支持的文件",
"SUCCESSFUL_UPLOADS": "上传成功",
"SKIPPED_INFO": "跳过这些,因为在同一相册中有具有匹配名称的文件",
"SKIPPED_INFO": "跳过这些文件,因为同一相册中有名称和内容相匹配的文件",
"UNSUPPORTED_INFO": "Ente 尚不支持这些文件格式",
"BLOCKED_UPLOADS": "已阻止上传",
"INPROGRESS_METADATA_EXTRACTION": "进行中",

View File

@ -8,7 +8,6 @@ import {
enableML,
mlStatusSnapshot,
mlStatusSubscribe,
wipCluster,
wipClusterEnable,
type MLStatus,
} from "@/new/photos/services/ml";
@ -341,8 +340,7 @@ const ManageML: React.FC<ManageMLProps> = ({
// TODO-Cluster
const router = useRouter();
const wipClusterNow = () => wipCluster();
const wipClusterShowNow = () => router.push("/cluster-debug");
const wipClusterDebug = () => router.push("/cluster-debug");
return (
<Stack px={"16px"} py={"20px"} gap={4}>
@ -391,28 +389,15 @@ const ManageML: React.FC<ManageMLProps> = ({
<Box>
<MenuItemGroup>
<EnteMenuItem
label={ut("Create clusters • internal only option")}
onClick={wipClusterNow}
label={ut(
"Create clusters • internal only option",
)}
onClick={wipClusterDebug}
/>
</MenuItemGroup>
<MenuSectionTitle
title={ut(
"Create in-memory clusters from arbitrary 2k photos. Nothing will be saved or synced to remote. You can view the results in search dropdown.",
)}
/>
</Box>
)}
{showClusterOpt && (
<Box>
<MenuItemGroup>
<EnteMenuItem
label={ut("View clusters • internal only option")}
onClick={wipClusterShowNow}
/>
</MenuItemGroup>
<MenuSectionTitle
title={ut(
"Variant of above that opens a page with debug info.",
"Create and show in-memory clusters (not saved or synced). You can also view them in the search dropdown later.",
)}
/>
</Box>

View File

@ -0,0 +1,35 @@
import { Hdbscan, type DebugInfo } from "hdbscan";
/**
* Each "cluster" is a list of indexes of the embeddings belonging to that
* particular cluster.
*/
export type EmbeddingCluster = number[];
export interface ClusterHdbscanResult {
clusters: EmbeddingCluster[];
noise: number[];
debugInfo?: DebugInfo;
}
/**
* Cluster the given {@link embeddings} using hdbscan.
*/
export const clusterHdbscan = (
embeddings: number[][],
): ClusterHdbscanResult => {
const hdbscan = new Hdbscan({
input: embeddings,
minClusterSize: 3,
minSamples: 5,
clusterSelectionEpsilon: 0.6,
clusterSelectionMethod: "leaf",
debug: false,
});
return {
clusters: hdbscan.getClusters(),
noise: hdbscan.getNoise(),
debugInfo: hdbscan.getDebugInfo(),
};
};

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