Compare commits

...

24 Commits

Author SHA1 Message Date
Greyson Parrelli 1aa33d5f8d Bump version to 7.28.0 2024-12-12 12:14:17 -05:00
Greyson Parrelli 558673dbdc Update translations and other static files. 2024-12-12 12:10:32 -05:00
Greyson Parrelli a8c0747434 Fix delete options in note to self. 2024-12-12 11:35:58 -05:00
Michelle Tang 3425e34c08 Fix duplicate contacts during story selection. 2024-12-12 11:35:03 -05:00
Greyson Parrelli 0e4b0a3bd5 Inline some paypal remote configs. 2024-12-12 11:27:11 -05:00
Greyson Parrelli 041883f472 Rotate link+sync flag. 2024-12-12 11:16:40 -05:00
Jameson Williams b7690471e6 Fix instrumentation tests.
Resolves #13836
2024-12-12 10:52:13 -05:00
Jameson Williams d45146e483 Migrate all remaining mockito tests to mockk.
Resolves #13835
2024-12-12 10:30:28 -05:00
Jameson Williams 53d0bed77e Convert a batch of tests from mockito -> mockk.
Resolves #13832
2024-12-12 10:26:32 -05:00
Greyson Parrelli 71c59af415 Remove some badly-formatted e164's. 2024-12-12 10:16:34 -05:00
Michelle Tang 562e2b6c3c Update story strings and button. 2024-12-11 11:48:21 -05:00
Cody Henthorne d36b61119f Add learn more urls for regv3 flows. 2024-12-11 09:35:53 -05:00
Cody Henthorne 9221ec5f59 Add additional error handling in regv3 flows. 2024-12-10 17:09:28 -05:00
Cody Henthorne 6cb14a04ce Rotate quick restore QR code and web socket. 2024-12-10 16:32:00 -05:00
Michelle Tang 8a584204e4 Limit wallpaper uploading to media tier. 2024-12-10 14:39:03 -05:00
Michelle Tang d5f8ee3b7c Close selection bar when nothing is selected. 2024-12-10 11:32:56 -05:00
Michelle Tang 1150c12da3 Update restore banners with progress indicators and redirection. 2024-12-10 11:21:50 -05:00
Alex Hart 23962cf16f Fix bad one-time-payment receipt creation for cancelled iDEAL. 2024-12-10 11:34:17 -04:00
Greyson Parrelli 6eaa93f46f fixup! Fix dangling quote authors. 2024-12-10 10:04:17 -05:00
Greyson Parrelli f61ea6d486 Fix export issue with bad edit messages from during development. 2024-12-09 23:48:20 -05:00
Greyson Parrelli 8b6597ada1 Fix dangling quote authors. 2024-12-09 23:23:25 -05:00
Greyson Parrelli 225173cead Update the backup playground to be more friendly. 2024-12-09 15:56:20 -05:00
Cody Henthorne d55467fd65 fixup! Add notification profile and chat folder backupv2 proto support. 2024-12-09 11:13:29 -05:00
Cody Henthorne b5857d4169 Add notification profile and chat folder backupv2 proto support. 2024-12-09 11:04:32 -05:00
178 changed files with 5716 additions and 2994 deletions
+4 -2
View File
@@ -21,8 +21,8 @@ plugins {
apply(from = "static-ips.gradle.kts")
val canonicalVersionCode = 1491
val canonicalVersionName = "7.27.1"
val canonicalVersionCode = 1492
val canonicalVersionName = "7.28.0"
val currentHotfixVersion = 0
val maxHotfixVersions = 100
@@ -228,6 +228,7 @@ android {
buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"unset\"")
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"unset\"")
buildConfigField("String", "BADGE_STATIC_ROOT", "\"https://updates2.signal.org/static/badges/\"")
buildConfigField("String", "STRIPE_BASE_URL", "\"https://api.stripe.com/v1\"")
buildConfigField("String", "STRIPE_PUBLISHABLE_KEY", "\"pk_live_6cmGZopuTsV8novGgJJW9JpC00vLIgtQ1D\"")
buildConfigField("boolean", "TRACING_ENABLED", "false")
buildConfigField("boolean", "MESSAGE_BACKUP_RESTORE_ENABLED", "false")
@@ -301,6 +302,7 @@ android {
applicationIdSuffix = ".instrumentation"
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Instrumentation\"")
buildConfigField("String", "STRIPE_BASE_URL", "\"http://127.0.0.1:8080/stripe\"")
}
create("spinner") {
@@ -66,6 +66,11 @@ class ArchiveImportExportTests {
runTests { it.startsWith("chat_") && !it.contains("_item") }
}
// @Test
fun chatFolders() {
runTests { it.startsWith("chat_folder_") }
}
// @Test
fun chatItemContactMessage() {
runTests { it.startsWith("chat_item_contact_message_") }
@@ -191,7 +196,12 @@ class ArchiveImportExportTests {
runTests { it.startsWith("chat_item_view_once_") }
}
// @Test
// @Test
fun notificationProfiles() {
runTests { it.startsWith("notification_profile_") }
}
// @Test
fun recipientCallLink() {
runTests { it.startsWith("recipient_call_link_") }
}
@@ -290,20 +300,6 @@ class ArchiveImportExportTests {
)
}
private fun assertPassesValidator(testName: String, generatedBackupData: ByteArray): TestResult.Failure? {
try {
BackupRepository.validate(
length = generatedBackupData.size.toLong(),
inputStreamFactory = { ByteArrayInputStream(generatedBackupData) },
selfData = BackupRepository.SelfData(SELF_ACI, SELF_PNI, SELF_E164, ProfileKey(SELF_PROFILE_KEY))
)
} catch (e: Exception) {
return TestResult.Failure(testName, "Generated backup failed validation: ${e.message}")
}
return null
}
private fun checkEquivalent(testName: String, import: ByteArray, export: ByteArray): TestResult.Failure? {
val importComparable = try {
ComparableBackup.readUnencrypted(MessageBackup.Purpose.REMOTE_BACKUP, import.inputStream(), import.size.toLong())
@@ -5,12 +5,16 @@
package org.thoughtcrime.securesms.conversation.v2.items
import android.content.Context
import android.net.Uri
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.Observer
import androidx.test.core.app.ApplicationProvider
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestManager
import io.mockk.mockk
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
@@ -204,13 +208,15 @@ class V2ConversationItemShapeTest {
private val colorizer = Colorizer()
override val lifecycleOwner: LifecycleOwner = mockk(relaxed = true)
override val lifecycleOwner: LifecycleOwner = object : LifecycleOwner {
override val lifecycle: Lifecycle = LifecycleRegistry(this)
}
override val displayMode: ConversationItemDisplayMode = ConversationItemDisplayMode.Standard
override val clickListener: ConversationAdapter.ItemClickListener = FakeConversationItemClickListener
override val selectedItems: Set<MultiselectPart> = emptySet()
override val isMessageRequestAccepted: Boolean = true
override val searchQuery: String? = null
override val requestManager: RequestManager = mockk()
override val requestManager: RequestManager = Glide.with(ApplicationProvider.getApplicationContext() as Context)
override val isParentInScroll: Boolean = false
override fun getChatColorsData(): ChatColorsDrawable.ChatColorsData = ChatColorsDrawable.ChatColorsData(null, null)
@@ -1026,7 +1026,7 @@ class RecipientTableTest_getAndPossiblyMerge {
}
private fun notificationProfile(name: String): NotificationProfile {
return (SignalDatabase.notificationProfiles.createProfile(name = name, emoji = "", color = AvatarColor.A210, System.currentTimeMillis()) as NotificationProfileDatabase.NotificationProfileChangeResult.Success).notificationProfile
return (SignalDatabase.notificationProfiles.createProfile(name = name, emoji = "", color = AvatarColor.A210, System.currentTimeMillis()) as NotificationProfileTables.NotificationProfileChangeResult.Success).notificationProfile
}
private fun getMention(messageId: Long): MentionModel {
@@ -0,0 +1,109 @@
package org.thoughtcrime.securesms.jobs
import androidx.test.ext.junit.runners.AndroidJUnit4
import okhttp3.mockwebserver.MockResponse
import org.hamcrest.Matchers
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.deleteAll
import org.signal.core.util.money.FiatMoney
import org.signal.donations.InAppPaymentType
import org.signal.donations.json.StripeIntentStatus
import org.signal.donations.json.StripePaymentIntent
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationSerializationHelper.toFiatValue
import org.thoughtcrime.securesms.database.DonationReceiptTable
import org.thoughtcrime.securesms.database.InAppPaymentTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider
import org.thoughtcrime.securesms.testing.Get
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assert
import org.thoughtcrime.securesms.testing.success
import org.thoughtcrime.securesms.util.TestStripePaths
import java.math.BigDecimal
import java.util.Currency
@RunWith(AndroidJUnit4::class)
class InAppPaymentAuthCheckJobTest {
companion object {
private const val TEST_INTENT_ID = "test-intent-id"
private const val TEST_CLIENT_SECRET = "test-client-secret"
}
@get:Rule
val harness = SignalActivityRule()
@Before
fun setUp() {
SignalDatabase.inAppPayments.writableDatabase.deleteAll(InAppPaymentTable.TABLE_NAME)
SignalDatabase.donationReceipts.writableDatabase.deleteAll(DonationReceiptTable.TABLE_NAME)
}
@Test
fun givenCanceledOneTimeAuthRequiredPayment_whenICheck_thenIDoNotExpectAReceipt() {
initializeMockGetPaymentIntent(status = StripeIntentStatus.CANCELED)
SignalDatabase.inAppPayments.insert(
type = InAppPaymentType.ONE_TIME_DONATION,
state = InAppPaymentTable.State.WAITING_FOR_AUTHORIZATION,
subscriberId = null,
endOfPeriod = null,
inAppPaymentData = InAppPaymentData(
amount = FiatMoney(BigDecimal.ONE, Currency.getInstance("USD")).toFiatValue(),
waitForAuth = InAppPaymentData.WaitingForAuthorizationState(
stripeIntentId = TEST_INTENT_ID,
stripeClientSecret = TEST_CLIENT_SECRET
)
)
)
InAppPaymentAuthCheckJob().run()
val receipts = SignalDatabase.donationReceipts.getReceipts(InAppPaymentReceiptRecord.Type.ONE_TIME_DONATION)
receipts assert Matchers.empty()
}
@Test
fun givenSuccessfulOneTimeAuthRequiredPayment_whenICheck_thenIExpectAReceipt() {
initializeMockGetPaymentIntent(status = StripeIntentStatus.SUCCEEDED)
SignalDatabase.inAppPayments.insert(
type = InAppPaymentType.ONE_TIME_DONATION,
state = InAppPaymentTable.State.WAITING_FOR_AUTHORIZATION,
subscriberId = null,
endOfPeriod = null,
inAppPaymentData = InAppPaymentData(
amount = FiatMoney(BigDecimal.ONE, Currency.getInstance("USD")).toFiatValue(),
waitForAuth = InAppPaymentData.WaitingForAuthorizationState(
stripeIntentId = TEST_INTENT_ID,
stripeClientSecret = TEST_CLIENT_SECRET
)
)
)
InAppPaymentAuthCheckJob().run()
val receipts = SignalDatabase.donationReceipts.getReceipts(InAppPaymentReceiptRecord.Type.ONE_TIME_DONATION)
receipts assert Matchers.hasSize(1)
}
private fun initializeMockGetPaymentIntent(status: StripeIntentStatus) {
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
Get(TestStripePaths.getPaymentIntentPath(TEST_INTENT_ID, TEST_CLIENT_SECRET)) {
MockResponse().success(
StripePaymentIntent(
id = TEST_INTENT_ID,
clientSecret = TEST_CLIENT_SECRET,
status = status,
paymentMethod = null
)
)
}
)
}
}
@@ -0,0 +1,58 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.util
import org.signal.donations.StripePaths
/**
* Stripe paths should be prefixed with 'stripe/' in order to access the proper namespacing in
* the mock server. This object serves as a convenience delegate to StripePaths.
*/
object TestStripePaths {
/**
* @see StripePaths.getPaymentIntentPath
*/
fun getPaymentIntentPath(paymentIntentId: String, clientSecret: String): String {
return withNamespace(StripePaths.getPaymentIntentPath(paymentIntentId, clientSecret))
}
/**
* @see StripePaths.getPaymentIntentConfirmationPath
*/
fun getPaymentIntentConfirmationPath(paymentIntentId: String): String {
return withNamespace(StripePaths.getPaymentIntentConfirmationPath(paymentIntentId))
}
/**
* @see StripePaths.getSetupIntentPath
*/
fun getSetupIntentPath(setupIntentId: String, clientSecret: String): String {
return withNamespace(StripePaths.getSetupIntentPath(setupIntentId, clientSecret))
}
/**
* @see StripePaths.getSetupIntentConfirmationPath
*/
fun getSetupIntentConfirmationPath(setupIntentId: String): String {
return withNamespace(StripePaths.getSetupIntentConfirmationPath(setupIntentId))
}
/**
* @see StripePaths.getPaymentIntentPath
*/
fun getPaymentMethodsPath(): String {
return withNamespace(StripePaths.getPaymentMethodsPath())
}
/**
* @see StripePaths.getTokensPath
*/
fun getTokensPath(): String {
return withNamespace(StripePaths.getTokensPath())
}
private fun withNamespace(path: String) = "stripe/$path"
}
@@ -156,6 +156,14 @@ object ArchiveUploadProgress {
updatePhase(ArchiveUploadProgressState.BackupPhase.Sticker)
}
override fun onNotificationProfile() {
updatePhase(ArchiveUploadProgressState.BackupPhase.NotificationProfile)
}
override fun onChatFolder() {
updatePhase(ArchiveUploadProgressState.BackupPhase.ChatFolder)
}
override fun onMessage(currentProgress: Long, approximateCount: Long) {
updatePhase(ArchiveUploadProgressState.BackupPhase.Message, currentProgress, approximateCount)
}
@@ -33,9 +33,6 @@ import org.signal.core.util.requireNonNullString
import org.signal.core.util.stream.NonClosingOutputStream
import org.signal.core.util.urlEncode
import org.signal.core.util.withinTransaction
import org.signal.libsignal.messagebackup.MessageBackup
import org.signal.libsignal.messagebackup.MessageBackup.ValidationResult
import org.signal.libsignal.protocol.ServiceId.Aci
import org.signal.libsignal.zkgroup.backups.BackupLevel
import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.thoughtcrime.securesms.attachments.Attachment
@@ -46,7 +43,9 @@ import org.thoughtcrime.securesms.backup.v2.importer.ChatItemArchiveImporter
import org.thoughtcrime.securesms.backup.v2.processor.AccountDataArchiveProcessor
import org.thoughtcrime.securesms.backup.v2.processor.AdHocCallArchiveProcessor
import org.thoughtcrime.securesms.backup.v2.processor.ChatArchiveProcessor
import org.thoughtcrime.securesms.backup.v2.processor.ChatFolderProcessor
import org.thoughtcrime.securesms.backup.v2.processor.ChatItemArchiveProcessor
import org.thoughtcrime.securesms.backup.v2.processor.NotificationProfileProcessor
import org.thoughtcrime.securesms.backup.v2.processor.RecipientArchiveProcessor
import org.thoughtcrime.securesms.backup.v2.processor.StickerArchiveProcessor
import org.thoughtcrime.securesms.backup.v2.proto.BackupInfo
@@ -107,7 +106,6 @@ import java.util.concurrent.atomic.AtomicLong
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.milliseconds
import org.signal.libsignal.messagebackup.MessageBackupKey as LibSignalMessageBackupKey
object BackupRepository {
@@ -576,6 +574,28 @@ object BackupRepository {
return@export
}
progressEmitter?.onNotificationProfile()
NotificationProfileProcessor.export(dbSnapshot, exportState) { frame ->
writer.write(frame)
eventTimer.emit("notification-profile")
frameCount++
}
if (cancellationSignal()) {
Log.w(TAG, "[export] Cancelled! Stopping")
return@export
}
progressEmitter?.onChatFolder()
ChatFolderProcessor.export(dbSnapshot, exportState) { frame ->
writer.write(frame)
eventTimer.emit("chat-folder")
frameCount++
}
if (cancellationSignal()) {
Log.w(TAG, "[export] Cancelled! Stopping")
return@export
}
val approximateMessageCount = dbSnapshot.messageTable.getApproximateExportableMessageCount(exportState.threadIds)
val frameCountStart = frameCount
progressEmitter?.onMessage(0, approximateMessageCount)
@@ -727,9 +747,6 @@ object BackupRepository {
SignalDatabase.recipients.setProfileKey(selfId, selfData.profileKey)
SignalDatabase.recipients.setProfileSharing(selfId, true)
// Add back default All Chats chat folder after clearing data
SignalDatabase.chatFolders.insertAllChatFolder()
val importState = ImportState(messageBackupKey, mediaRootBackupKey)
val chatItemInserter: ChatItemArchiveImporter = ChatItemArchiveProcessor.beginImport(importState)
@@ -768,6 +785,18 @@ object BackupRepository {
frameCount++
}
frame.notificationProfile != null -> {
NotificationProfileProcessor.import(frame.notificationProfile, importState)
eventTimer.emit("notification-profile")
frameCount++
}
frame.chatFolder != null -> {
ChatFolderProcessor.import(frame.chatFolder, importState)
eventTimer.emit("chat-folder")
frameCount++
}
frame.chatItem != null -> {
chatItemInserter.import(frame.chatItem)
eventTimer.emit("chatItem")
@@ -791,6 +820,11 @@ object BackupRepository {
eventTimer.emit("chatItem")
}
if (!importState.importedChatFolders) {
// Add back default All Chats chat folder after clearing data if missing
SignalDatabase.chatFolders.insertAllChatFolder()
}
stopwatch.split("frames")
Log.d(TAG, "[import] Rebuilding FTS index...")
@@ -856,13 +890,6 @@ object BackupRepository {
return ImportResult.Success(backupTime = header.backupTimeMs)
}
fun validate(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData): ValidationResult {
val accountEntropyPool = SignalStore.account.accountEntropyPool.value
val key = LibSignalMessageBackupKey(accountEntropyPool, Aci.parseFromBinary(selfData.aci.toByteArray()))
return MessageBackup.validate(key, MessageBackup.Purpose.REMOTE_BACKUP, inputStreamFactory, length)
}
fun listRemoteMediaObjects(limit: Int, cursor: String? = null): NetworkResult<ArchiveGetMediaItemsResponse> {
return initBackupAndFetchAuth()
.then { credential ->
@@ -1449,6 +1476,8 @@ object BackupRepository {
fun onThread()
fun onCall()
fun onSticker()
fun onNotificationProfile()
fun onChatFolder()
fun onMessage(currentProgress: Long, approximateCount: Long)
fun onAttachment(currentProgress: Long, totalCount: Long)
}
@@ -1477,10 +1506,20 @@ class ImportState(val messageBackupKey: MessageBackupKey, val mediaRootBackupKey
val chatIdToLocalRecipientId: MutableMap<Long, RecipientId> = hashMapOf()
val chatIdToBackupRecipientId: MutableMap<Long, Long> = hashMapOf()
val remoteToLocalColorId: MutableMap<Long, Long> = hashMapOf()
val recipientIdToLocalThreadId: MutableMap<RecipientId, Long> = hashMapOf()
val recipientIdToIsGroup: MutableMap<RecipientId, Boolean> = hashMapOf()
private var chatFolderPosition: Int = 0
val importedChatFolders: Boolean
get() = chatFolderPosition > 0
fun requireLocalRecipientId(remoteId: Long): RecipientId {
return remoteToLocalRecipientId[remoteId] ?: throw IllegalArgumentException("There is no local recipientId for remote recipientId $remoteId!")
}
fun getNextChatFolderPosition(): Int {
return chatFolderPosition++
}
}
class BackupMetadata(
@@ -12,6 +12,8 @@ class LocalBackupV2Event(val type: Type, val count: Long = 0, val estimatedTotal
PROGRESS_THREAD,
PROGRESS_CALL,
PROGRESS_STICKER,
NOTIFICATION_PROFILE,
CHAT_FOLDER,
PROGRESS_MESSAGE,
PROGRESS_ATTACHMENT,
PROGRESS_VERIFYING,
@@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadTable
fun ThreadTable.getThreadsForBackup(db: SignalDatabase): ChatArchiveExporter {
fun ThreadTable.getThreadsForBackup(db: SignalDatabase, includeImageWallpapers: Boolean): ChatArchiveExporter {
//language=sql
val query = """
SELECT
@@ -40,7 +40,7 @@ fun ThreadTable.getThreadsForBackup(db: SignalDatabase): ChatArchiveExporter {
"""
val cursor = readableDatabase.query(query)
return ChatArchiveExporter(cursor, db)
return ChatArchiveExporter(cursor, db, includeImageWallpapers)
}
fun ThreadTable.getThreadGroupStatus(messageIds: Collection<Long>): Map<Long, Boolean> {
@@ -22,7 +22,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper
import java.io.Closeable
import kotlin.time.Duration.Companion.seconds
class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalDatabase) : Iterator<Chat>, Closeable {
class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalDatabase, private val includeImageWallpapers: Boolean) : Iterator<Chat>, Closeable {
override fun hasNext(): Boolean {
return cursor.count > 0 && !cursor.isLast
}
@@ -40,7 +40,14 @@ class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalData
}
val chatWallpaper: Wallpaper? = cursor.requireBlob(RecipientTable.WALLPAPER)?.let { serializedWallpaper ->
Wallpaper.ADAPTER.decodeOrNull(serializedWallpaper)
val wallpaper = Wallpaper.ADAPTER.decodeOrNull(serializedWallpaper)
val isImageWallpaper = wallpaper?.file_ != null
if (includeImageWallpapers || !isImageWallpaper) {
wallpaper
} else {
null
}
}
return Chat(
@@ -300,7 +300,11 @@ class ChatItemArchiveExporter(
if (record.latestRevisionId == null) {
val previousEdits = revisionMap.remove(record.id)
if (previousEdits != null) {
builder.revisions = previousEdits
if (builder.standardMessage != null) {
builder.revisions = previousEdits
} else {
Log.w(TAG, "[${record.dateSent}] Attempted to set revisions on a non-standard message! Ignoring.")
}
}
buffer += builder.build()
} else {
@@ -169,6 +169,14 @@ object LocalArchiver {
EventBus.getDefault().post(LocalBackupV2Event(LocalBackupV2Event.Type.PROGRESS_STICKER))
}
override fun onNotificationProfile() {
EventBus.getDefault().post(LocalBackupV2Event(LocalBackupV2Event.Type.NOTIFICATION_PROFILE))
}
override fun onChatFolder() {
EventBus.getDefault().post(LocalBackupV2Event(LocalBackupV2Event.Type.CHAT_FOLDER))
}
override fun onMessage(currentProgress: Long, approximateCount: Long) {
if (currentProgress == 0L) {
EventBus.getDefault().post(LocalBackupV2Event(LocalBackupV2Event.Type.PROGRESS_MESSAGE))
@@ -8,12 +8,14 @@ package org.thoughtcrime.securesms.backup.v2.processor
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.backup.v2.ExportState
import org.thoughtcrime.securesms.backup.v2.ImportState
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.backup.v2.database.getThreadsForBackup
import org.thoughtcrime.securesms.backup.v2.importer.ChatArchiveImporter
import org.thoughtcrime.securesms.backup.v2.proto.Chat
import org.thoughtcrime.securesms.backup.v2.proto.Frame
import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.RecipientId
/**
@@ -23,7 +25,10 @@ object ChatArchiveProcessor {
val TAG = Log.tag(ChatArchiveProcessor::class.java)
fun export(db: SignalDatabase, exportState: ExportState, emitter: BackupFrameEmitter) {
db.threadTable.getThreadsForBackup(db).use { reader ->
val includeImageWallpapers = SignalStore.backup.backupTier == MessageBackupTier.PAID
Log.i(TAG, "Including wallpapers: $includeImageWallpapers")
db.threadTable.getThreadsForBackup(db, includeImageWallpapers).use { reader ->
for (chat in reader) {
if (exportState.recipientIds.contains(chat.recipientId)) {
exportState.threadIds.add(chat.id)
@@ -46,5 +51,6 @@ object ChatArchiveProcessor {
importState.chatIdToLocalRecipientId[chat.id] = recipientId
importState.chatIdToLocalThreadId[chat.id] = threadId
importState.chatIdToBackupRecipientId[chat.id] = chat.recipientId
importState.recipientIdToLocalThreadId[recipientId] = threadId
}
}
@@ -0,0 +1,144 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.backup.v2.processor
import androidx.core.content.contentValuesOf
import org.signal.core.util.SqlUtil
import org.signal.core.util.insertInto
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.backup.v2.ExportState
import org.thoughtcrime.securesms.backup.v2.ImportState
import org.thoughtcrime.securesms.backup.v2.proto.ChatFolder
import org.thoughtcrime.securesms.backup.v2.proto.Frame
import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter
import org.thoughtcrime.securesms.components.settings.app.chats.folders.ChatFolderRecord
import org.thoughtcrime.securesms.database.ChatFolderTables.ChatFolderMembershipTable
import org.thoughtcrime.securesms.database.ChatFolderTables.ChatFolderTable
import org.thoughtcrime.securesms.database.ChatFolderTables.MembershipType
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.backup.v2.proto.ChatFolder as ChatFolderProto
/**
* Handles exporting and importing [ChatFolderRecord]s.
*/
object ChatFolderProcessor {
private val TAG = Log.tag(ChatFolderProcessor::class)
fun export(db: SignalDatabase, exportState: ExportState, emitter: BackupFrameEmitter) {
val folders = db
.chatFoldersTable
.getChatFolders()
.sortedBy { it.position }
if (folders.isEmpty()) {
Log.d(TAG, "No chat folders, nothing to export")
return
}
if (folders.size == 1 && folders[0].folderType == ChatFolderRecord.FolderType.ALL) {
Log.d(TAG, "Only ALL chat folder present, skipping chat folder export")
return
}
if (folders.none { it.folderType == ChatFolderRecord.FolderType.ALL }) {
Log.w(TAG, "Missing ALL chat folder, exporting as first position")
emitter.emit(ChatFolderRecord.getAllChatsFolderForBackup().toBackupFrame(emptyList(), emptyList()))
}
folders.forEach { folder ->
val includedRecipientIds = folder
.includedChats
.map { db.threadTable.getRecipientIdForThreadId(it)!!.toLong() }
.filter { exportState.recipientIds.contains(it) }
val excludedRecipientIds = folder
.excludedChats
.map { db.threadTable.getRecipientIdForThreadId(it)!!.toLong() }
.filter { exportState.recipientIds.contains(it) }
val frame = folder.toBackupFrame(includedRecipientIds, excludedRecipientIds)
emitter.emit(frame)
}
}
fun import(chatFolder: ChatFolderProto, importState: ImportState) {
val chatFolderId = SignalDatabase
.writableDatabase
.insertInto(ChatFolderTable.TABLE_NAME)
.values(
ChatFolderTable.NAME to chatFolder.name,
ChatFolderTable.POSITION to importState.getNextChatFolderPosition(),
ChatFolderTable.SHOW_UNREAD to chatFolder.showOnlyUnread,
ChatFolderTable.SHOW_MUTED to chatFolder.showMutedChats,
ChatFolderTable.SHOW_INDIVIDUAL to chatFolder.includeAllIndividualChats,
ChatFolderTable.SHOW_GROUPS to chatFolder.includeAllGroupChats,
ChatFolderTable.FOLDER_TYPE to chatFolder.folderType.toLocal().value
)
.run()
if (chatFolderId < 0) {
Log.w(TAG, "Chat folder already exists")
return
}
val includedChatsQueries = chatFolder.includedRecipientIds.toMembershipInsertQueries(chatFolderId, importState, MembershipType.INCLUDED)
includedChatsQueries.forEach {
SignalDatabase.writableDatabase.execSQL(it.where, it.whereArgs)
}
val excludedChatsQueries = chatFolder.excludedRecipientIds.toMembershipInsertQueries(chatFolderId, importState, MembershipType.EXCLUDED)
excludedChatsQueries.forEach {
SignalDatabase.writableDatabase.execSQL(it.where, it.whereArgs)
}
}
}
private fun ChatFolderRecord.toBackupFrame(includedRecipientIds: List<Long>, excludedRecipientIds: List<Long>): Frame {
val chatFolder = ChatFolderProto(
name = this.name,
showOnlyUnread = this.showUnread,
showMutedChats = this.showMutedChats,
includeAllIndividualChats = this.showIndividualChats,
includeAllGroupChats = this.showGroupChats,
folderType = when (this.folderType) {
ChatFolderRecord.FolderType.ALL -> ChatFolderProto.FolderType.ALL
ChatFolderRecord.FolderType.CUSTOM -> ChatFolderProto.FolderType.CUSTOM
else -> throw IllegalStateException("Only ALL or CUSTOM should be in the db")
},
includedRecipientIds = includedRecipientIds,
excludedRecipientIds = excludedRecipientIds
)
return Frame(chatFolder = chatFolder)
}
private fun ChatFolderProto.FolderType.toLocal(): ChatFolderRecord.FolderType {
return when (this) {
ChatFolder.FolderType.UNKNOWN -> throw IllegalStateException()
ChatFolder.FolderType.ALL -> ChatFolderRecord.FolderType.ALL
ChatFolder.FolderType.CUSTOM -> ChatFolderRecord.FolderType.CUSTOM
}
}
private fun List<Long>.toMembershipInsertQueries(chatFolderId: Long, importState: ImportState, membershipType: MembershipType): List<SqlUtil.Query> {
val values = this
.mapNotNull { importState.remoteToLocalRecipientId[it] }
.map { recipientId -> importState.recipientIdToLocalThreadId[recipientId] ?: SignalDatabase.threads.getOrCreateThreadIdFor(recipientId, importState.recipientIdToIsGroup[recipientId] == true) }
.map { threadId ->
contentValuesOf(
ChatFolderMembershipTable.CHAT_FOLDER_ID to chatFolderId,
ChatFolderMembershipTable.THREAD_ID to threadId,
ChatFolderMembershipTable.MEMBERSHIP_TYPE to membershipType.value
)
}
return SqlUtil.buildBulkInsert(
ChatFolderMembershipTable.TABLE_NAME,
arrayOf(ChatFolderMembershipTable.CHAT_FOLDER_ID, ChatFolderMembershipTable.THREAD_ID, ChatFolderMembershipTable.MEMBERSHIP_TYPE),
values
)
}
@@ -0,0 +1,131 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.backup.v2.processor
import org.signal.core.util.insertInto
import org.signal.core.util.logging.Log
import org.signal.core.util.toInt
import org.thoughtcrime.securesms.backup.v2.ExportState
import org.thoughtcrime.securesms.backup.v2.ImportState
import org.thoughtcrime.securesms.backup.v2.proto.Frame
import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
import org.thoughtcrime.securesms.database.NotificationProfileTables.NotificationProfileAllowedMembersTable
import org.thoughtcrime.securesms.database.NotificationProfileTables.NotificationProfileScheduleTable
import org.thoughtcrime.securesms.database.NotificationProfileTables.NotificationProfileTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.serialize
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
import org.thoughtcrime.securesms.recipients.RecipientId
import java.lang.IllegalStateException
import java.time.DayOfWeek
import org.thoughtcrime.securesms.backup.v2.proto.NotificationProfile as NotificationProfileProto
/**
* Handles exporting and importing [NotificationProfile] models.
*/
object NotificationProfileProcessor {
private val TAG = Log.tag(NotificationProfileProcessor::class)
fun export(db: SignalDatabase, exportState: ExportState, emitter: BackupFrameEmitter) {
db.notificationProfileTables
.getProfiles()
.forEach { profile ->
val frame = profile.toBackupFrame(includeRecipient = { id -> exportState.recipientIds.contains(id.toLong()) })
emitter.emit(frame)
}
}
fun import(profile: NotificationProfileProto, importState: ImportState) {
val profileId = SignalDatabase
.writableDatabase
.insertInto(NotificationProfileTable.TABLE_NAME)
.values(
NotificationProfileTable.NAME to profile.name,
NotificationProfileTable.EMOJI to (profile.emoji ?: ""),
NotificationProfileTable.COLOR to (AvatarColor.fromColor(profile.color) ?: AvatarColor.random()).serialize(),
NotificationProfileTable.CREATED_AT to profile.createdAtMs,
NotificationProfileTable.ALLOW_ALL_CALLS to profile.allowAllCalls.toInt(),
NotificationProfileTable.ALLOW_ALL_MENTIONS to profile.allowAllMentions.toInt()
)
.run()
if (profileId < 0) {
Log.w(TAG, "Notification profile name already exists")
return
}
SignalDatabase
.writableDatabase
.insertInto(NotificationProfileScheduleTable.TABLE_NAME)
.values(
NotificationProfileScheduleTable.NOTIFICATION_PROFILE_ID to profileId,
NotificationProfileScheduleTable.ENABLED to profile.scheduleEnabled.toInt(),
NotificationProfileScheduleTable.START to profile.scheduleStartTime,
NotificationProfileScheduleTable.END to profile.scheduleEndTime,
NotificationProfileScheduleTable.DAYS_ENABLED to profile.scheduleDaysEnabled.map { it.toLocal() }.toSet().serialize()
)
.run()
profile
.allowedMembers
.mapNotNull { importState.remoteToLocalRecipientId[it] }
.forEach { recipientId ->
SignalDatabase
.writableDatabase
.insertInto(NotificationProfileAllowedMembersTable.TABLE_NAME)
.values(
NotificationProfileAllowedMembersTable.NOTIFICATION_PROFILE_ID to profileId,
NotificationProfileAllowedMembersTable.RECIPIENT_ID to recipientId.serialize()
)
.run()
}
}
}
private fun NotificationProfile.toBackupFrame(includeRecipient: (RecipientId) -> Boolean): Frame {
val profile = NotificationProfileProto(
name = this.name,
emoji = this.emoji.takeIf { it.isNotBlank() },
color = this.color.colorInt(),
createdAtMs = this.createdAt,
allowAllCalls = this.allowAllCalls,
allowAllMentions = this.allowAllMentions,
allowedMembers = this.allowedMembers.filter { includeRecipient(it) }.map { it.toLong() },
scheduleEnabled = this.schedule.enabled,
scheduleStartTime = this.schedule.start,
scheduleEndTime = this.schedule.end,
scheduleDaysEnabled = this.schedule.daysEnabled.map { it.toBackupProto() }
)
return Frame(notificationProfile = profile)
}
private fun DayOfWeek.toBackupProto(): NotificationProfileProto.DayOfWeek {
return when (this) {
DayOfWeek.MONDAY -> NotificationProfileProto.DayOfWeek.MONDAY
DayOfWeek.TUESDAY -> NotificationProfileProto.DayOfWeek.TUESDAY
DayOfWeek.WEDNESDAY -> NotificationProfileProto.DayOfWeek.WEDNESDAY
DayOfWeek.THURSDAY -> NotificationProfileProto.DayOfWeek.THURSDAY
DayOfWeek.FRIDAY -> NotificationProfileProto.DayOfWeek.FRIDAY
DayOfWeek.SATURDAY -> NotificationProfileProto.DayOfWeek.SATURDAY
DayOfWeek.SUNDAY -> NotificationProfileProto.DayOfWeek.SUNDAY
}
}
private fun NotificationProfileProto.DayOfWeek.toLocal(): DayOfWeek {
return when (this) {
NotificationProfileProto.DayOfWeek.UNKNOWN -> throw IllegalStateException()
NotificationProfileProto.DayOfWeek.MONDAY -> DayOfWeek.MONDAY
NotificationProfileProto.DayOfWeek.TUESDAY -> DayOfWeek.TUESDAY
NotificationProfileProto.DayOfWeek.WEDNESDAY -> DayOfWeek.WEDNESDAY
NotificationProfileProto.DayOfWeek.THURSDAY -> DayOfWeek.THURSDAY
NotificationProfileProto.DayOfWeek.FRIDAY -> DayOfWeek.FRIDAY
NotificationProfileProto.DayOfWeek.SATURDAY -> DayOfWeek.SATURDAY
NotificationProfileProto.DayOfWeek.SUNDAY -> DayOfWeek.SUNDAY
}
}
@@ -57,6 +57,7 @@ private const val NONE = -1
@Composable
fun BackupStatusBanner(
data: BackupStatusData,
onBannerClick: () -> Unit = {},
onActionClick: (BackupStatusData) -> Unit = {},
onDismissClick: () -> Unit = {},
contentPadding: PaddingValues = PaddingValues(horizontal = 12.dp, vertical = 8.dp)
@@ -68,6 +69,7 @@ fun BackupStatusBanner(
.border(1.dp, color = MaterialTheme.colorScheme.outline.copy(alpha = 0.38f), shape = RoundedCornerShape(12.dp))
.fillMaxWidth()
.defaultMinSize(minHeight = 48.dp)
.clickable(onClick = onBannerClick)
.padding(12.dp)
) {
Icon(
@@ -309,7 +311,7 @@ sealed interface BackupStatusData {
RestoreStatus.FINISHED -> bytesTotal.toUnitString()
}
override val progress: Float = if (bytesTotal.bytes > 0 && restoreStatus == RestoreStatus.NORMAL) {
override val progress: Float = if (bytesTotal.bytes > 0) {
min(1f, max(0f, bytesDownloaded.bytes.toFloat() / bytesTotal.bytes.toFloat()))
} else {
NONE.toFloat()
@@ -73,6 +73,7 @@ class MediaRestoreProgressBanner(private val listener: RestoreProgressBannerList
override fun DisplayBanner(model: BackupStatusData, contentPadding: PaddingValues) {
BackupStatusBanner(
data = model,
onBannerClick = listener::onBannerClick,
onActionClick = listener::onActionClick,
onDismissClick = listener::onDismissComplete
)
@@ -107,14 +108,15 @@ class MediaRestoreProgressBanner(private val listener: RestoreProgressBannerList
return flow
.throttleLatest(1.seconds)
.map {
val totalRestoreSize = SignalStore.backup.totalRestorableAttachmentSize
val remainingAttachmentSize = SignalDatabase.attachments.getRemainingRestorableAttachmentSize()
val completedBytes = totalRestoreSize - remainingAttachmentSize
when {
!WifiConstraint.isMet(AppDependencies.application) && !SignalStore.backup.restoreWithCellular -> BackupStatusData.RestoringMedia(restoreStatus = BackupStatusData.RestoreStatus.WAITING_FOR_WIFI)
!NetworkConstraint.isMet(AppDependencies.application) -> BackupStatusData.RestoringMedia(restoreStatus = BackupStatusData.RestoreStatus.WAITING_FOR_INTERNET)
!BatteryNotLowConstraint.isMet() -> BackupStatusData.RestoringMedia(restoreStatus = BackupStatusData.RestoreStatus.LOW_BATTERY)
!WifiConstraint.isMet(AppDependencies.application) && !SignalStore.backup.restoreWithCellular -> BackupStatusData.RestoringMedia(completedBytes.bytes, totalRestoreSize.bytes, BackupStatusData.RestoreStatus.WAITING_FOR_WIFI)
!NetworkConstraint.isMet(AppDependencies.application) -> BackupStatusData.RestoringMedia(completedBytes.bytes, totalRestoreSize.bytes, BackupStatusData.RestoreStatus.WAITING_FOR_INTERNET)
!BatteryNotLowConstraint.isMet() -> BackupStatusData.RestoringMedia(completedBytes.bytes, totalRestoreSize.bytes, BackupStatusData.RestoreStatus.LOW_BATTERY)
else -> {
val totalRestoreSize = SignalStore.backup.totalRestorableAttachmentSize
val remainingAttachmentSize = SignalDatabase.attachments.getRemainingRestorableAttachmentSize()
val completedBytes = totalRestoreSize - remainingAttachmentSize
val availableBytes = SignalStore.backup.spaceAvailableOnDiskBytes
if (availableBytes > -1L && remainingAttachmentSize > availableBytes) {
@@ -129,11 +131,13 @@ class MediaRestoreProgressBanner(private val listener: RestoreProgressBannerList
}
interface RestoreProgressBannerListener {
fun onBannerClick()
fun onActionClick(data: BackupStatusData)
fun onDismissComplete()
}
private object EmptyListener : RestoreProgressBannerListener {
override fun onBannerClick() = Unit
override fun onActionClick(data: BackupStatusData) = Unit
override fun onDismissComplete() = Unit
}
@@ -74,6 +74,7 @@ class AppSettingsActivity : DSLSettingsActivity(), InAppPaymentComponent {
CreateFoldersFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).folderId,
CreateFoldersFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).threadId
)
StartLocation.BACKUPS_SETTINGS -> AppSettingsFragmentDirections.actionDirectToBackupsSettingsFragment()
}
}
@@ -216,6 +217,9 @@ class AppSettingsActivity : DSLSettingsActivity(), InAppPaymentComponent {
return getIntentForStartLocation(context, StartLocation.CREATE_CHAT_FOLDER).putExtra(START_ARGUMENTS, arguments)
}
@JvmStatic
fun backupsSettings(context: Context): Intent = getIntentForStartLocation(context, StartLocation.BACKUPS_SETTINGS)
private fun getIntentForStartLocation(context: Context, startLocation: StartLocation): Intent {
return Intent(context, AppSettingsActivity::class.java)
.putExtra(ARG_NAV_GRAPH, R.navigation.app_settings_with_change_number)
@@ -242,7 +246,8 @@ class AppSettingsActivity : DSLSettingsActivity(), InAppPaymentComponent {
RECOVER_USERNAME(15),
REMOTE_BACKUPS(16),
CHAT_FOLDERS(17),
CREATE_CHAT_FOLDER(18);
CREATE_CHAT_FOLDER(18),
BACKUPS_SETTINGS(19);
companion object {
fun fromCode(code: Int?): StartLocation {
@@ -81,7 +81,7 @@ class BackupsSettingsFragment : ComposeFragment() {
BackupsSettingsContent(
backupsSettingsState = state,
onNavigationClick = { findNavController().popBackStack() },
onNavigationClick = { requireActivity().onNavigateUp() },
onBackupsRowClick = {
when (state.enabledState) {
is BackupsSettingsState.EnabledState.Active, BackupsSettingsState.EnabledState.Inactive -> {
@@ -164,7 +164,8 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
lastBackupTimestamp = SignalStore.backup.lastBackupTime,
backupSize = SignalStore.backup.totalBackupSize,
backupsFrequency = SignalStore.backup.backupFrequency,
canBackUpUsingCellular = SignalStore.backup.backupWithCellular
canBackUpUsingCellular = SignalStore.backup.backupWithCellular,
canRestoreUsingCellular = SignalStore.backup.restoreWithCellular
)
}
@@ -49,4 +49,15 @@ data class ChatFolderRecord(
}
}
}
companion object {
fun getAllChatsFolderForBackup(): ChatFolderRecord {
return ChatFolderRecord(
folderType = ChatFolderRecord.FolderType.ALL,
showIndividualChats = true,
showGroupChats = true,
showMutedChats = true
)
}
}
}
@@ -7,7 +7,6 @@ package org.thoughtcrime.securesms.components.settings.app.internal.backup
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
@@ -23,13 +22,11 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu
@@ -42,7 +39,6 @@ import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
@@ -67,18 +63,15 @@ import androidx.compose.ui.unit.dp
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.signal.core.ui.Buttons
import org.signal.core.ui.Dividers
import org.signal.core.ui.Previews
import org.signal.core.ui.Rows
import org.signal.core.ui.SignalPreview
import org.signal.core.ui.Snackbars
import org.signal.core.ui.theme.SignalTheme
import org.signal.core.util.bytes
import org.signal.core.util.getLength
import org.signal.core.util.roundedString
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.attachments.AttachmentId
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.BackupState
import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.BackupUploadState
import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.ScreenState
import org.thoughtcrime.securesms.compose.ComposeFragment
import org.thoughtcrime.securesms.jobs.LocalBackupJob
@@ -87,46 +80,47 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
class InternalBackupPlaygroundFragment : ComposeFragment() {
private val viewModel: InternalBackupPlaygroundViewModel by viewModels()
private lateinit var exportFileLauncher: ActivityResultLauncher<Intent>
private lateinit var importFileLauncher: ActivityResultLauncher<Intent>
private lateinit var validateFileLauncher: ActivityResultLauncher<Intent>
private lateinit var savePlaintextcopyLauncher: ActivityResultLauncher<Intent>
private lateinit var saveEncryptedBackupToDiskLauncher: ActivityResultLauncher<Intent>
private lateinit var savePlaintextBackupToDiskLauncher: ActivityResultLauncher<Intent>
private lateinit var importEncryptedBackupFromDiskLauncher: ActivityResultLauncher<Intent>
private lateinit var savePlaintextCopyLauncher: ActivityResultLauncher<Intent>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
exportFileLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
saveEncryptedBackupToDiskLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
result.data?.data?.let { uri ->
requireContext().contentResolver.openOutputStream(uri)?.use { outputStream ->
outputStream.write(viewModel.backupData!!)
Toast.makeText(requireContext(), "Saved successfully", Toast.LENGTH_SHORT).show()
} ?: Toast.makeText(requireContext(), "Failed to open output stream", Toast.LENGTH_SHORT).show()
viewModel.exportEncrypted(
openStream = { requireContext().contentResolver.openOutputStream(uri)!! },
appendStream = { requireContext().contentResolver.openOutputStream(uri, "wa")!! }
)
} ?: Toast.makeText(requireContext(), "No URI selected", Toast.LENGTH_SHORT).show()
}
}
importFileLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
savePlaintextBackupToDiskLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
result.data?.data?.let { uri ->
viewModel.exportPlaintext(
openStream = { requireContext().contentResolver.openOutputStream(uri)!! },
appendStream = { requireContext().contentResolver.openOutputStream(uri, "wa")!! }
)
} ?: Toast.makeText(requireContext(), "No URI selected", Toast.LENGTH_SHORT).show()
}
}
importEncryptedBackupFromDiskLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
result.data?.data?.let { uri ->
requireContext().contentResolver.getLength(uri)?.let { length ->
viewModel.import(length) { requireContext().contentResolver.openInputStream(uri)!! }
viewModel.importEncryptedBackup(length) { requireContext().contentResolver.openInputStream(uri)!! }
}
} ?: Toast.makeText(requireContext(), "No URI selected", Toast.LENGTH_SHORT).show()
}
}
validateFileLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
result.data?.data?.let { uri ->
requireContext().contentResolver.getLength(uri)?.let { length ->
viewModel.validate(length) { requireContext().contentResolver.openInputStream(uri)!! }
}
} ?: Toast.makeText(requireContext(), "No URI selected", Toast.LENGTH_SHORT).show()
}
}
savePlaintextcopyLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
savePlaintextCopyLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
result.data?.data?.let { uri ->
viewModel.fetchRemoteBackupAndWritePlaintext(requireContext().contentResolver.openOutputStream(uri))
@@ -152,54 +146,32 @@ class InternalBackupPlaygroundFragment : ComposeFragment() {
mainContent = {
Screen(
state = state,
onExportClicked = { viewModel.export() },
onExportDirectoryClicked = { LocalBackupJob.enqueueArchive() },
onImportMemoryClicked = { viewModel.import() },
onImportFileClicked = {
val intent = Intent().apply {
action = Intent.ACTION_GET_CONTENT
type = "application/octet-stream"
addCategory(Intent.CATEGORY_OPENABLE)
}
importFileLauncher.launch(intent)
},
onImportDirectoryClicked = {
viewModel.import(SignalStore.settings.signalBackupDirectory!!)
},
onPlaintextClicked = { viewModel.onPlaintextToggled() },
onSaveToDiskClicked = {
onBackupTierSelected = { tier -> viewModel.onBackupTierSelected(tier) },
onCheckRemoteBackupStateClicked = { viewModel.checkRemoteBackupState() },
onEnqueueRemoteBackupClicked = { viewModel.triggerBackupJob() },
onHaltAllBackupJobsClicked = { viewModel.haltAllJobs() },
onValidateBackupClicked = { viewModel.validateBackup() },
onSaveEncryptedBackupToDiskClicked = {
val intent = Intent().apply {
action = Intent.ACTION_CREATE_DOCUMENT
type = "application/octet-stream"
addCategory(Intent.CATEGORY_OPENABLE)
putExtra(Intent.EXTRA_TITLE, "backup-${if (state.plaintext) "plaintext" else "encrypted"}-${System.currentTimeMillis()}.bin")
putExtra(Intent.EXTRA_TITLE, "backup-encrypted-${System.currentTimeMillis()}.bin")
}
exportFileLauncher.launch(intent)
saveEncryptedBackupToDiskLauncher.launch(intent)
},
onUploadToRemoteClicked = { viewModel.uploadBackupToRemote() },
onCheckRemoteBackupStateClicked = { viewModel.checkRemoteBackupState() },
onValidateFileClicked = {
onSavePlaintextBackupToDiskClicked = {
val intent = Intent().apply {
action = Intent.ACTION_GET_CONTENT
action = Intent.ACTION_CREATE_DOCUMENT
type = "application/octet-stream"
addCategory(Intent.CATEGORY_OPENABLE)
putExtra(Intent.EXTRA_TITLE, "backup-plaintext-${System.currentTimeMillis()}.bin")
}
validateFileLauncher.launch(intent)
savePlaintextBackupToDiskLauncher.launch(intent)
},
onTriggerBackupJobClicked = { viewModel.triggerBackupJob() },
onWipeDataAndRestoreClicked = {
MaterialAlertDialogBuilder(context)
.setTitle("Are you sure?")
.setMessage("This will delete all of your chats! Make sure you've finished a backup first, we don't check for you. Only do this on a test device!")
.setPositiveButton("Wipe and restore") { _, _ -> viewModel.wipeAllDataAndRestoreFromRemote() }
.show()
},
onBackupTierSelected = { tier -> viewModel.onBackupTierSelected(tier) },
onHaltAllJobs = { viewModel.haltAllJobs() },
onSavePlaintextCopy = {
onSavePlaintextCopyOfRemoteBackupClicked = {
val intent = Intent().apply {
action = Intent.ACTION_CREATE_DOCUMENT
type = "application/octet-stream"
@@ -207,7 +179,31 @@ class InternalBackupPlaygroundFragment : ComposeFragment() {
putExtra(Intent.EXTRA_TITLE, "backup-plaintext-${System.currentTimeMillis()}.binproto")
}
savePlaintextcopyLauncher.launch(intent)
savePlaintextCopyLauncher.launch(intent)
},
onExportNewStyleLocalBackupClicked = { LocalBackupJob.enqueueArchive() },
onWipeDataAndRestoreFromRemoteClicked = {
MaterialAlertDialogBuilder(context)
.setTitle("Are you sure?")
.setMessage("This will delete all of your chats! Make sure you've finished a backup first, we don't check for you. Only do this on a test device!")
.setPositiveButton("Wipe and restore") { _, _ -> viewModel.wipeAllDataAndRestoreFromRemote() }
.show()
},
onImportEncryptedBackupFromDiskClicked = {
val intent = Intent().apply {
action = Intent.ACTION_GET_CONTENT
type = "application/octet-stream"
addCategory(Intent.CATEGORY_OPENABLE)
}
importEncryptedBackupFromDiskLauncher.launch(intent)
},
onImportNewStyleLocalBackupClicked = {
MaterialAlertDialogBuilder(context)
.setTitle("Are you sure?")
.setMessage("After you choose a file to import, this will delete all of your chats, then restore them from the file! Only do this on a test device!")
.setPositiveButton("Wipe and restore") { _, _ -> viewModel.import(SignalStore.settings.signalBackupDirectory!!) }
.show()
}
)
},
@@ -290,21 +286,18 @@ fun Tabs(
@Composable
fun Screen(
state: ScreenState,
onExportClicked: () -> Unit = {},
onExportDirectoryClicked: () -> Unit = {},
onImportMemoryClicked: () -> Unit = {},
onImportFileClicked: () -> Unit = {},
onImportDirectoryClicked: () -> Unit = {},
onPlaintextClicked: () -> Unit = {},
onSaveToDiskClicked: () -> Unit = {},
onValidateFileClicked: () -> Unit = {},
onUploadToRemoteClicked: () -> Unit = {},
onExportNewStyleLocalBackupClicked: () -> Unit = {},
onImportNewStyleLocalBackupClicked: () -> Unit = {},
onCheckRemoteBackupStateClicked: () -> Unit = {},
onTriggerBackupJobClicked: () -> Unit = {},
onWipeDataAndRestoreClicked: () -> Unit = {},
onEnqueueRemoteBackupClicked: () -> Unit = {},
onWipeDataAndRestoreFromRemoteClicked: () -> Unit = {},
onBackupTierSelected: (MessageBackupTier?) -> Unit = {},
onHaltAllJobs: () -> Unit = {},
onSavePlaintextCopy: () -> Unit = {}
onHaltAllBackupJobsClicked: () -> Unit = {},
onSavePlaintextCopyOfRemoteBackupClicked: () -> Unit = {},
onValidateBackupClicked: () -> Unit = {},
onSaveEncryptedBackupToDiskClicked: () -> Unit = {},
onSavePlaintextBackupToDiskClicked: () -> Unit = {},
onImportEncryptedBackupFromDiskClicked: () -> Unit = {}
) {
val scrollState = rememberScrollState()
val options = remember {
@@ -322,7 +315,6 @@ fun Screen(
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)
.padding(16.dp)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Tier", fontWeight = FontWeight.Bold)
@@ -339,191 +331,121 @@ fun Screen(
Dividers.Default()
Buttons.LargePrimary(
onClick = onTriggerBackupJobClicked
) {
Text("Enqueue remote backup")
}
Button(
onClick = onWipeDataAndRestoreClicked,
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFC33C00))
) {
Text("Wipe data and restore")
}
Buttons.LargeTonal(
onClick = onHaltAllJobs
) {
Text("Halt all backup jobs")
}
Buttons.LargeTonal(
onClick = onSavePlaintextCopy
) {
Text("Save plaintext copy of remote backup")
}
Rows.TextRow(
text = {
Text(
text = state.statusMessage ?: "Status messages will appear here as you perform operations",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
)
Dividers.Default()
Buttons.LargeTonal(
Rows.TextRow(
text = "Check remote backup state",
label = "Get a summary of what your remote backup looks like (presence, size, etc).",
onClick = onCheckRemoteBackupStateClicked
)
Rows.TextRow(
text = "Enqueue remote backup",
label = "Schedules a job that will perform a routine remote backup.",
onClick = onEnqueueRemoteBackupClicked
)
Rows.TextRow(
text = "Halt all backup jobs",
label = "Stops all backup-related jobs to the best of our ability.",
onClick = onHaltAllBackupJobsClicked
)
Rows.TextRow(
text = "Validate backup",
label = "Generates a new backup and reports whether it passes validation. Does not save or upload anything.",
onClick = onValidateBackupClicked
)
Rows.TextRow(
text = "Save encrypted backup to disk",
label = "Generates an encrypted backup (the same thing you would upload) and saves it to your local disk.",
onClick = onSaveEncryptedBackupToDiskClicked
)
Rows.TextRow(
text = "Save plaintext backup to disk",
label = "Generates a plaintext, uncompressed backup and saves it to your local disk.",
onClick = onSavePlaintextBackupToDiskClicked
)
Rows.TextRow(
text = "Save plaintext copy of remote backup",
label = "Downloads your most recently uploaded backup and saves it to disk, plaintext and uncompressed.",
onClick = onSavePlaintextCopyOfRemoteBackupClicked
)
Rows.TextRow(
text = "Perform a new-style local backup",
label = "Creates a local backup (in your already-chosen backup directory) using the new on-disk backup format. This is the successor to the local backups existing users can build.",
onClick = onExportNewStyleLocalBackupClicked
)
Dividers.Default()
Text(
text = "DANGER ZONE",
color = Color.Red,
fontWeight = FontWeight.Bold
)
Rows.TextRow(
text = {
Text(
text = "The following operations are potentially destructive! Only use them if you know what you're doing.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
)
Rows.TextRow(
text = "Wipe data and restore from remote",
label = "Erases all content on your device, followed by a restore of your remote backup.",
onClick = onWipeDataAndRestoreFromRemoteClicked
)
Rows.TextRow(
text = "Clear backup init flag",
label = "Clears our local state around whether backups have been initialized or not. Will force us to make request to claim backupId and set public keys.",
onClick = {
SignalStore.backup.backupsInitialized = false
}
) {
Text("Clear backup init flag")
}
)
Buttons.LargeTonal(
Rows.TextRow(
text = "Clear backup credentials",
label = "Clears any cached backup credentials, for both our message and media backups.",
onClick = {
SignalStore.backup.messageCredentials.clearAll()
SignalStore.backup.mediaCredentials.clearAll()
}
) {
Text("Clear backup credentials")
}
)
Rows.TextRow(
text = "Wipe all data and restore from file",
label = "Erases all content on your device, followed by a restore of an encrypted backup selected from disk.",
onClick = onImportEncryptedBackupFromDiskClicked
)
Rows.TextRow(
text = "Wipe all data and restore a new-style local backup",
label = "Erases all content on your device, followed by a restore of a previously-generated new-style local backup.",
onClick = onImportNewStyleLocalBackupClicked
)
Dividers.Default()
Row(
verticalAlignment = Alignment.CenterVertically
) {
StateLabel(text = "Plaintext?")
Spacer(modifier = Modifier.width(8.dp))
Switch(
checked = state.plaintext,
onCheckedChange = { onPlaintextClicked() }
)
}
Spacer(modifier = Modifier.height(8.dp))
Buttons.LargePrimary(
onClick = onExportClicked,
enabled = !state.backupState.inProgress
) {
Text("Export")
}
Buttons.LargePrimary(
onClick = onExportDirectoryClicked,
enabled = !state.backupState.inProgress && state.canReadWriteBackupDirectory
) {
Text("Export to backup directory")
}
Dividers.Default()
Buttons.LargeTonal(
onClick = onImportMemoryClicked,
enabled = state.backupState == BackupState.EXPORT_DONE
) {
Text("Import from memory")
}
Buttons.LargeTonal(
onClick = onImportFileClicked
) {
Text("Import from file")
}
Buttons.LargeTonal(
onClick = onImportDirectoryClicked,
enabled = state.canReadWriteBackupDirectory
) {
Text("Import from backup directory")
}
Buttons.LargeTonal(
onClick = onValidateFileClicked
) {
Text("Validate file")
}
Spacer(modifier = Modifier.height(16.dp))
when (state.backupState) {
BackupState.NONE -> {
StateLabel("")
}
BackupState.EXPORT_IN_PROGRESS -> {
StateLabel("Export in progress...")
}
BackupState.EXPORT_DONE -> {
StateLabel("Export complete. Sitting in memory. You can click 'Import' to import that data, save it to a file, or upload it to remote.")
Spacer(modifier = Modifier.height(8.dp))
Buttons.MediumTonal(onClick = onSaveToDiskClicked) {
Text("Save to file")
}
}
BackupState.BACKUP_JOB_DONE -> {
StateLabel("Backup complete and uploaded")
}
BackupState.IMPORT_IN_PROGRESS -> {
StateLabel("Import in progress...")
}
}
Dividers.Default()
Buttons.LargeTonal(
onClick = onCheckRemoteBackupStateClicked
) {
Text("Check remote backup state")
}
Spacer(modifier = Modifier.height(8.dp))
when (state.remoteBackupState) {
is InternalBackupPlaygroundViewModel.RemoteBackupState.Available -> {
StateLabel("Exists/allocated. ${state.remoteBackupState.response.mediaCount} media items, using ${state.remoteBackupState.response.usedSpace} bytes (${state.remoteBackupState.response.usedSpace.bytes.inMebiBytes.roundedString(3)} MiB)")
}
InternalBackupPlaygroundViewModel.RemoteBackupState.GeneralError -> {
StateLabel("Hit an unknown error. Check the logs.")
}
InternalBackupPlaygroundViewModel.RemoteBackupState.NotFound -> {
StateLabel("Not found.")
}
InternalBackupPlaygroundViewModel.RemoteBackupState.Unknown -> {
StateLabel("Hit the button above to check the state.")
}
}
Spacer(modifier = Modifier.height(8.dp))
Buttons.LargePrimary(
onClick = onUploadToRemoteClicked,
enabled = state.backupState == BackupState.EXPORT_DONE
) {
Text("Upload to remote")
}
Spacer(modifier = Modifier.height(8.dp))
when (state.uploadState) {
BackupUploadState.NONE -> {
StateLabel("")
}
BackupUploadState.UPLOAD_IN_PROGRESS -> {
StateLabel("Upload in progress...")
}
BackupUploadState.UPLOAD_DONE -> {
StateLabel("Upload complete.")
}
BackupUploadState.UPLOAD_FAILED -> {
StateLabel("Upload failed.")
}
}
}
}
}
@@ -713,57 +635,18 @@ private data class MediaMultiSelectState(
val expandedOption: AttachmentId? = null
)
@Preview(name = "Light Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_YES)
@SignalPreview
@Composable
fun PreviewScreen() {
SignalTheme {
Surface {
Screen(state = ScreenState(backupState = BackupState.NONE, plaintext = false))
}
Previews.Preview {
Screen(state = ScreenState())
}
}
@Preview(name = "Light Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_YES)
@SignalPreview
@Composable
fun PreviewScreenExportInProgress() {
SignalTheme {
Surface {
Screen(state = ScreenState(backupState = BackupState.EXPORT_IN_PROGRESS, plaintext = false))
}
}
}
@Preview(name = "Light Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun PreviewScreenExportDone() {
SignalTheme {
Surface {
Screen(state = ScreenState(backupState = BackupState.EXPORT_DONE, plaintext = false))
}
}
}
@Preview(name = "Light Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun PreviewScreenImportInProgress() {
SignalTheme {
Surface {
Screen(state = ScreenState(backupState = BackupState.IMPORT_IN_PROGRESS, plaintext = false))
}
}
}
@Preview(name = "Light Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun PreviewScreenUploadInProgress() {
SignalTheme {
Surface {
Screen(state = ScreenState(uploadState = BackupUploadState.UPLOAD_IN_PROGRESS, plaintext = false))
}
Previews.Preview {
Screen(state = ScreenState(statusMessage = "Some random status message."))
}
}
@@ -18,14 +18,17 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.kotlin.subscribeBy
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.bytes
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.copyTo
import org.signal.core.util.logging.Log
import org.signal.core.util.readNBytesOrThrow
import org.signal.core.util.roundedString
import org.signal.core.util.stream.LimitedInputStream
import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.thoughtcrime.securesms.attachments.AttachmentId
import org.thoughtcrime.securesms.attachments.DatabaseAttachment
import org.thoughtcrime.securesms.backup.v2.ArchiveValidator
import org.thoughtcrime.securesms.backup.v2.BackupMetadata
import org.thoughtcrime.securesms.backup.v2.BackupRepository
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
@@ -54,7 +57,7 @@ import org.thoughtcrime.securesms.providers.BlobProvider
import org.thoughtcrime.securesms.recipients.Recipient
import org.whispersystems.signalservice.api.NetworkResult
import org.whispersystems.signalservice.api.backup.MediaName
import java.io.ByteArrayInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
@@ -72,15 +75,10 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
private val TAG = Log.tag(InternalBackupPlaygroundViewModel::class)
}
var backupData: ByteArray? = null
val disposables = CompositeDisposable()
private val _state: MutableState<ScreenState> = mutableStateOf(
ScreenState(
backupState = BackupState.NONE,
uploadState = BackupUploadState.NONE,
plaintext = false,
canReadWriteBackupDirectory = SignalStore.settings.signalBackupDirectory?.let {
val file = DocumentFile.fromTreeUri(AppDependencies.application, it)
file != null && file.canWrite() && file.canRead()
@@ -93,66 +91,94 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
private val _mediaState: MutableState<MediaState> = mutableStateOf(MediaState())
val mediaState: State<MediaState> = _mediaState
fun export() {
_state.value = _state.value.copy(backupState = BackupState.EXPORT_IN_PROGRESS)
val plaintext = _state.value.plaintext
disposables += Single.fromCallable { BackupRepository.debugExport(plaintext = plaintext) }
fun exportEncrypted(openStream: () -> OutputStream, appendStream: () -> OutputStream) {
_state.value = _state.value.copy(statusMessage = "Exporting encrypted backup to disk...")
disposables += Single
.fromCallable {
BackupRepository.export(
outputStream = openStream(),
append = { bytes -> appendStream().use { it.write(bytes) } }
)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { data ->
backupData = data
_state.value = _state.value.copy(backupState = BackupState.EXPORT_DONE)
_state.value = _state.value.copy(statusMessage = "Encrypted backup complete!")
}
}
fun exportPlaintext(openStream: () -> OutputStream, appendStream: () -> OutputStream) {
_state.value = _state.value.copy(statusMessage = "Exporting plaintext backup to disk...")
disposables += Single
.fromCallable {
BackupRepository.export(
outputStream = openStream(),
append = { bytes -> appendStream().use { it.write(bytes) } },
plaintext = true
)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { data ->
_state.value = _state.value.copy(statusMessage = "Plaintext backup complete!")
}
}
fun validateBackup() {
_state.value = _state.value.copy(statusMessage = "Exporting to a temporary file...")
val tempFile = BlobProvider.getInstance().forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application)
disposables += Single
.fromCallable {
BackupRepository.export(
outputStream = FileOutputStream(tempFile),
append = { bytes -> tempFile.appendBytes(bytes) }
)
_state.value = _state.value.copy(statusMessage = "Export complete! Validating...")
ArchiveValidator.validate(tempFile, SignalStore.backup.messageBackupKey)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { result ->
val message = when (result) {
is ArchiveValidator.ValidationResult.ReadError -> "Failed to read backup file!"
ArchiveValidator.ValidationResult.Success -> "Validation passed!"
is ArchiveValidator.ValidationResult.ValidationError -> {
Log.w(TAG, "Validation failed!", result.exception)
"Validation failed :( Check the logs for details."
}
}
_state.value = _state.value.copy(statusMessage = message)
}
}
fun triggerBackupJob() {
_state.value = _state.value.copy(backupState = BackupState.EXPORT_IN_PROGRESS)
_state.value = _state.value.copy(statusMessage = "Upload job in progress...")
disposables += Single.fromCallable { AppDependencies.jobManager.runSynchronously(BackupMessagesJob(), 120_000) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy {
_state.value = _state.value.copy(backupState = BackupState.BACKUP_JOB_DONE)
.subscribeBy { result ->
_state.value = _state.value.copy(statusMessage = "Upload job complete! Result: ${result.takeIf { it.isPresent }?.get() ?: "N/A"}")
}
}
fun import() {
backupData?.let {
_state.value = _state.value.copy(backupState = BackupState.IMPORT_IN_PROGRESS)
val plaintext = _state.value.plaintext
val self = Recipient.self()
val selfData = BackupRepository.SelfData(self.aci.get(), self.pni.get(), self.e164.get(), ProfileKey(self.profileKey))
disposables += Single.fromCallable { BackupRepository.import(it.size.toLong(), { ByteArrayInputStream(it) }, selfData, plaintext = plaintext) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy {
backupData = null
_state.value = _state.value.copy(backupState = BackupState.NONE)
}
}
}
fun import(length: Long, inputStreamFactory: () -> InputStream) {
_state.value = _state.value.copy(backupState = BackupState.IMPORT_IN_PROGRESS)
val plaintext = _state.value.plaintext
fun importEncryptedBackup(length: Long, inputStreamFactory: () -> InputStream) {
_state.value = _state.value.copy(statusMessage = "Importing encrypted backup...")
val self = Recipient.self()
val selfData = BackupRepository.SelfData(self.aci.get(), self.pni.get(), self.e164.get(), ProfileKey(self.profileKey))
disposables += Single.fromCallable { BackupRepository.import(length, inputStreamFactory, selfData, plaintext = plaintext) }
disposables += Single.fromCallable { BackupRepository.import(length, inputStreamFactory, selfData, plaintext = false) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy {
backupData = null
_state.value = _state.value.copy(backupState = BackupState.NONE)
_state.value = _state.value.copy(statusMessage = "Encrypted backup import complete!")
}
}
fun import(uri: Uri) {
_state.value = _state.value.copy(backupState = BackupState.IMPORT_IN_PROGRESS)
_state.value = _state.value.copy(statusMessage = "Importing new-style local backup...")
val self = Recipient.self()
val selfData = BackupRepository.SelfData(self.aci.get(), self.pni.get(), self.e164.get(), ProfileKey(self.profileKey))
@@ -170,42 +196,59 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy {
backupData = null
_state.value = _state.value.copy(backupState = BackupState.NONE)
_state.value = _state.value.copy(statusMessage = "New-style local backup import complete!")
}
}
fun validate(length: Long, inputStreamFactory: () -> InputStream) {
val self = Recipient.self()
val selfData = BackupRepository.SelfData(self.aci.get(), self.pni.get(), self.e164.get(), ProfileKey(self.profileKey))
disposables += Single.fromCallable { BackupRepository.validate(length, inputStreamFactory, selfData) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy {
backupData = null
_state.value = _state.value.copy(backupState = BackupState.NONE)
}
fun haltAllJobs() {
AppDependencies.jobManager.cancelAllInQueue(BackfillDigestJob.QUEUE)
AppDependencies.jobManager.cancelAllInQueue("ArchiveAttachmentJobs_0")
AppDependencies.jobManager.cancelAllInQueue("ArchiveAttachmentJobs_1")
AppDependencies.jobManager.cancelAllInQueue("ArchiveThumbnailUploadJob")
AppDependencies.jobManager.cancelAllInQueue("BackupRestoreJob")
AppDependencies.jobManager.cancelAllInQueue("__LOCAL_BACKUP__")
}
fun onPlaintextToggled() {
_state.value = _state.value.copy(plaintext = !_state.value.plaintext)
}
fun fetchRemoteBackupAndWritePlaintext(outputStream: OutputStream?) {
check(outputStream != null)
fun uploadBackupToRemote() {
_state.value = _state.value.copy(uploadState = BackupUploadState.UPLOAD_IN_PROGRESS)
SignalExecutors.BOUNDED_IO.execute {
Log.d(TAG, "Downloading file...")
val tempBackupFile = BlobProvider.getInstance().forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application)
disposables += Single
.fromCallable { BackupRepository.debugUploadBackupFile(backupData!!.inputStream(), backupData!!.size.toLong()) is NetworkResult.Success }
.subscribeOn(Schedulers.io())
.subscribe { success ->
_state.value = _state.value.copy(uploadState = if (success) BackupUploadState.UPLOAD_DONE else BackupUploadState.UPLOAD_FAILED)
when (val result = BackupRepository.downloadBackupFile(tempBackupFile)) {
is NetworkResult.Success -> Log.i(TAG, "Download successful")
else -> {
Log.w(TAG, "Failed to download backup file", result.getCause())
throw IOException(result.getCause())
}
}
val encryptedStream = tempBackupFile.inputStream()
val iv = encryptedStream.readNBytesOrThrow(16)
val backupKey = SignalStore.backup.messageBackupKey
val keyMaterial = backupKey.deriveBackupSecrets(Recipient.self().aci.get())
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding").apply {
init(Cipher.DECRYPT_MODE, SecretKeySpec(keyMaterial.aesKey, "AES"), IvParameterSpec(iv))
}
val plaintextStream = GZIPInputStream(
CipherInputStream(
LimitedInputStream(
wrapped = encryptedStream,
maxBytes = tempBackupFile.length() - MAC_SIZE
),
cipher
)
)
Log.d(TAG, "Copying...")
plaintextStream.copyTo(outputStream)
Log.d(TAG, "Done!")
}
}
fun checkRemoteBackupState() {
_state.value = _state.value.copy(remoteBackupState = RemoteBackupState.Unknown)
disposables += Single
.fromCallable {
BackupRepository.restoreBackupTier(SignalStore.account.requireAci())
@@ -215,16 +258,18 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
.subscribe { result ->
when {
result is NetworkResult.Success -> {
_state.value = _state.value.copy(remoteBackupState = RemoteBackupState.Available(result.result))
_state.value = _state.value.copy(
statusMessage = "Remote backup exists. ${result.result.mediaCount} media items, using ${result.result.usedSpace} bytes (${result.result.usedSpace.bytes.inMebiBytes.roundedString(3)} MiB)"
)
}
result is NetworkResult.StatusCodeError && result.code == 404 -> {
_state.value = _state.value.copy(remoteBackupState = RemoteBackupState.NotFound)
_state.value = _state.value.copy(statusMessage = "Remote backup does not exists.")
}
else -> {
Log.w(TAG, "Error checking remote backup state", result.getCause())
_state.value = _state.value.copy(remoteBackupState = RemoteBackupState.GeneralError)
_state.value = _state.value.copy(statusMessage = "Failed to fetch remote backup state.")
}
}
}
@@ -242,7 +287,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
}
private fun restoreFromRemote() {
_state.value = _state.value.copy(backupState = BackupState.IMPORT_IN_PROGRESS)
_state.value = _state.value.copy(statusMessage = "Importing from remote...")
disposables += Single.fromCallable {
AppDependencies
@@ -255,7 +300,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy {
_state.value = _state.value.copy(backupState = BackupState.NONE)
_state.value = _state.value.copy(statusMessage = "Import complete!")
}
}
@@ -437,33 +482,21 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
}
data class ScreenState(
val backupState: BackupState = BackupState.NONE,
val uploadState: BackupUploadState = BackupUploadState.NONE,
val remoteBackupState: RemoteBackupState = RemoteBackupState.Unknown,
val plaintext: Boolean,
val canReadWriteBackupDirectory: Boolean = false,
val backupTier: MessageBackupTier? = null
val backupTier: MessageBackupTier? = null,
val statusMessage: String? = null
)
enum class BackupState(val inProgress: Boolean = false) {
NONE,
EXPORT_IN_PROGRESS(true),
EXPORT_DONE,
BACKUP_JOB_DONE,
IMPORT_IN_PROGRESS(true)
}
enum class BackupUploadState(val inProgress: Boolean = false) {
NONE,
UPLOAD_IN_PROGRESS(true),
UPLOAD_DONE,
UPLOAD_FAILED
}
sealed class RemoteBackupState {
object Unknown : RemoteBackupState()
object NotFound : RemoteBackupState()
object GeneralError : RemoteBackupState()
data class Available(val response: BackupMetadata) : RemoteBackupState()
}
@@ -532,52 +565,4 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
fun <T> MutableState<T>.set(update: T.() -> T) {
this.value = this.value.update()
}
fun haltAllJobs() {
AppDependencies.jobManager.cancelAllInQueue(BackfillDigestJob.QUEUE)
AppDependencies.jobManager.cancelAllInQueue("ArchiveAttachmentJobs_0")
AppDependencies.jobManager.cancelAllInQueue("ArchiveAttachmentJobs_1")
AppDependencies.jobManager.cancelAllInQueue("ArchiveThumbnailUploadJob")
AppDependencies.jobManager.cancelAllInQueue("BackupRestoreJob")
AppDependencies.jobManager.cancelAllInQueue("__LOCAL_BACKUP__")
}
fun fetchRemoteBackupAndWritePlaintext(outputStream: OutputStream?) {
check(outputStream != null)
SignalExecutors.BOUNDED_IO.execute {
Log.d(TAG, "Downloading file...")
val tempBackupFile = BlobProvider.getInstance().forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application)
when (val result = BackupRepository.downloadBackupFile(tempBackupFile)) {
is NetworkResult.Success -> Log.i(TAG, "Download successful")
else -> {
Log.w(TAG, "Failed to download backup file", result.getCause())
throw IOException(result.getCause())
}
}
val encryptedStream = tempBackupFile.inputStream()
val iv = encryptedStream.readNBytesOrThrow(16)
val backupKey = SignalStore.backup.messageBackupKey
val keyMaterial = backupKey.deriveBackupSecrets(Recipient.self().aci.get())
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding").apply {
init(Cipher.DECRYPT_MODE, SecretKeySpec(keyMaterial.aesKey, "AES"), IvParameterSpec(iv))
}
val plaintextStream = GZIPInputStream(
CipherInputStream(
LimitedInputStream(
wrapped = encryptedStream,
maxBytes = tempBackupFile.length() - MAC_SIZE
),
cipher
)
)
Log.d(TAG, "Copying...")
plaintextStream.copyTo(outputStream)
Log.d(TAG, "Done!")
}
}
}
@@ -4,7 +4,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Single
import org.thoughtcrime.securesms.database.NotificationProfileDatabase
import org.thoughtcrime.securesms.database.NotificationProfileTables
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
class EditNotificationProfileViewModel(private val profileId: Long, private val repository: NotificationProfilesRepository) : ViewModel() {
@@ -34,8 +34,8 @@ class EditNotificationProfileViewModel(private val profileId: Long, private val
return save.map { r ->
when (r) {
is NotificationProfileDatabase.NotificationProfileChangeResult.Success -> SaveNotificationProfileResult.Success(r.notificationProfile, createMode)
NotificationProfileDatabase.NotificationProfileChangeResult.DuplicateName -> SaveNotificationProfileResult.DuplicateNameFailure
is NotificationProfileTables.NotificationProfileChangeResult.Success -> SaveNotificationProfileResult.Success(r.notificationProfile, createMode)
NotificationProfileTables.NotificationProfileChangeResult.DuplicateName -> SaveNotificationProfileResult.DuplicateNameFailure
}
}.observeOn(AndroidSchedulers.mainThread())
}
@@ -8,7 +8,7 @@ import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
import org.thoughtcrime.securesms.database.DatabaseObserver
import org.thoughtcrime.securesms.database.NotificationProfileDatabase
import org.thoughtcrime.securesms.database.NotificationProfileTables
import org.thoughtcrime.securesms.database.RxDatabaseObserver
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.AppDependencies
@@ -24,7 +24,7 @@ import org.thoughtcrime.securesms.util.toMillis
* One stop shop for all your Notification Profile data needs.
*/
class NotificationProfilesRepository {
private val database: NotificationProfileDatabase = SignalDatabase.notificationProfiles
private val database: NotificationProfileTables = SignalDatabase.notificationProfiles
fun getProfiles(): Flowable<List<NotificationProfile>> {
return RxDatabaseObserver
@@ -54,17 +54,17 @@ class NotificationProfilesRepository {
}.subscribeOn(Schedulers.io())
}
fun createProfile(name: String, selectedEmoji: String): Single<NotificationProfileDatabase.NotificationProfileChangeResult> {
fun createProfile(name: String, selectedEmoji: String): Single<NotificationProfileTables.NotificationProfileChangeResult> {
return Single.fromCallable { database.createProfile(name = name, emoji = selectedEmoji, color = AvatarColor.random(), createdAt = System.currentTimeMillis()) }
.subscribeOn(Schedulers.io())
}
fun updateProfile(profileId: Long, name: String, selectedEmoji: String): Single<NotificationProfileDatabase.NotificationProfileChangeResult> {
fun updateProfile(profileId: Long, name: String, selectedEmoji: String): Single<NotificationProfileTables.NotificationProfileChangeResult> {
return Single.fromCallable { database.updateProfile(profileId = profileId, name = name, emoji = selectedEmoji) }
.subscribeOn(Schedulers.io())
}
fun updateProfile(profile: NotificationProfile): Single<NotificationProfileDatabase.NotificationProfileChangeResult> {
fun updateProfile(profile: NotificationProfile): Single<NotificationProfileTables.NotificationProfileChangeResult> {
return Single.fromCallable { database.updateProfile(profile) }
.subscribeOn(Schedulers.io())
}
@@ -99,7 +99,7 @@ class NotificationProfilesRepository {
.take(1)
.singleOrError()
.flatMap { updateProfile(it.copy(allowAllMentions = !it.allowAllMentions)) }
.map { (it as NotificationProfileDatabase.NotificationProfileChangeResult.Success).notificationProfile }
.map { (it as NotificationProfileTables.NotificationProfileChangeResult.Success).notificationProfile }
}
fun toggleAllowAllCalls(profileId: Long): Single<NotificationProfile> {
@@ -107,7 +107,7 @@ class NotificationProfilesRepository {
.take(1)
.singleOrError()
.flatMap { updateProfile(it.copy(allowAllCalls = !it.allowAllCalls)) }
.map { (it as NotificationProfileDatabase.NotificationProfileChangeResult.Success).notificationProfile }
.map { (it as NotificationProfileTables.NotificationProfileChangeResult.Success).notificationProfile }
}
fun manuallyToggleProfile(profile: NotificationProfile, now: Long = System.currentTimeMillis()): Completable {
@@ -46,8 +46,9 @@ object InAppDonations {
private fun isPayPalAvailableForDonateToSignalType(inAppPaymentType: InAppPaymentType): Boolean {
return when (inAppPaymentType) {
InAppPaymentType.UNKNOWN -> error("Unsupported type UNKNOWN")
InAppPaymentType.ONE_TIME_DONATION, InAppPaymentType.ONE_TIME_GIFT -> RemoteConfig.paypalOneTimeDonations
InAppPaymentType.RECURRING_DONATION -> RemoteConfig.paypalRecurringDonations
InAppPaymentType.ONE_TIME_DONATION -> true
InAppPaymentType.ONE_TIME_GIFT -> true
InAppPaymentType.RECURRING_DONATION -> true
InAppPaymentType.RECURRING_BACKUP -> false
} && !LocaleRemoteConfig.isPayPalDisabled()
}
@@ -63,7 +64,7 @@ object InAppDonations {
* Whether the user is in a region that supports PayPal, based off local phone number.
*/
fun isPayPalAvailable(): Boolean {
return (RemoteConfig.paypalOneTimeDonations || RemoteConfig.paypalRecurringDonations) && !LocaleRemoteConfig.isPayPalDisabled()
return !LocaleRemoteConfig.isPayPalDisabled()
}
/**
@@ -4,9 +4,11 @@ import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
/**
* A serializable set of color constants that can be used for avatars.
@@ -27,8 +29,11 @@ public enum AvatarColor {
UNKNOWN("UNKNOWN", 0x00000000),
ON_SURFACE_VARIANT("ON_SURFACE_VARIANT", 0x00000000);
/** Fast map of name to enum, while also giving us a location to map old colors to new ones. */
/**
* Fast map of name to enum, while also giving us a location to map old colors to new ones.
*/
private static final Map<String, AvatarColor> NAME_MAP = new HashMap<>();
static {
for (AvatarColor color : AvatarColor.values()) {
NAME_MAP.put(color.serialize(), color);
@@ -97,7 +102,9 @@ public enum AvatarColor {
NAME_MAP.put("grey", A210);
}
/** Colors that can be assigned via {@link #random()}. */
/**
* Colors that can be assigned via {@link #random()}.
*/
static final AvatarColor[] RANDOM_OPTIONS = new AvatarColor[] {
A100,
A110,
@@ -137,4 +144,11 @@ public enum AvatarColor {
public static @NonNull AvatarColor deserialize(@Nullable String name) {
return Objects.requireNonNull(NAME_MAP.getOrDefault(name, A210));
}
public static @Nullable AvatarColor fromColor(@ColorInt int color) {
return Arrays.stream(values())
.filter(c -> c.color == color)
.findFirst()
.orElse(null);
}
}
@@ -914,6 +914,11 @@ public class ConversationListFragment extends MainFragment implements ActionMode
return Unit.INSTANCE;
}),
new MediaRestoreProgressBanner(new MediaRestoreProgressBanner.RestoreProgressBannerListener() {
@Override
public void onBannerClick() {
startActivity(AppSettingsActivity.backupsSettings(requireContext()));
}
@Override
public void onActionClick(@NonNull BackupStatusData backupStatusData) {
if (backupStatusData instanceof BackupStatusData.NotEnoughFreeSpace) {
@@ -1035,7 +1040,11 @@ public class ConversationListFragment extends MainFragment implements ActionMode
lifecycleDisposable.add(
viewModel.getSelectedState().subscribe(conversations -> {
defaultAdapter.setSelectedConversations(conversations);
updateMultiSelectState();
if (conversations.isEmpty()) {
endActionModeIfActive();
} else {
updateMultiSelectState();
}
})
);
}
@@ -1448,12 +1457,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
handleCreateConversation(conversation.getThreadRecord().getThreadId(), conversation.getThreadRecord().getRecipient(), conversation.getThreadRecord().getDistributionType());
} else {
viewModel.toggleConversationSelected(conversation);
if (viewModel.currentSelectedConversations().isEmpty()) {
endActionModeIfActive();
} else {
updateMultiSelectState();
}
}
}
@@ -526,7 +526,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
}
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
writableDatabase.update(TABLE_NAME)
val count = writableDatabase.update(TABLE_NAME)
.values(
contentValuesOf(
RECIPIENT_ID to toId.toLong()
@@ -534,5 +534,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
)
.where("$RECIPIENT_ID = ?", fromId.toLong())
.run()
Log.d(TAG, "Remapped $fromId to $toId. count: $count")
}
}
@@ -1489,11 +1489,13 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
}
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
writableDatabase
val count = writableDatabase
.update(TABLE_NAME)
.values(PEER to toId.serialize())
.where("$PEER = ?", fromId)
.run()
Log.d(TAG, "Remapped $fromId to $toId. count: $count")
}
/**
@@ -525,12 +525,14 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign
.run()
}
override fun remapRecipient(oldId: RecipientId, newId: RecipientId) {
writableDatabase
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
val count = writableDatabase
.update(MembershipTable.TABLE_NAME)
.values(MembershipTable.RECIPIENT_ID to newId.serialize())
.where("${MembershipTable.RECIPIENT_ID} = ?", oldId)
.values(MembershipTable.RECIPIENT_ID to toId.serialize())
.where("${MembershipTable.RECIPIENT_ID} = ?", fromId)
.run(SQLiteDatabase.CONFLICT_REPLACE)
Log.d(TAG, "Remapped $fromId to $toId. count: $count")
}
fun deleteList(distributionListId: DistributionListId, deletionTimestamp: Long = System.currentTimeMillis()) {
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database
import android.content.Context
import android.database.Cursor
import androidx.annotation.VisibleForTesting
import androidx.core.content.contentValuesOf
import org.signal.core.util.CursorUtil
import org.signal.core.util.SqlUtil
@@ -12,7 +13,8 @@ import java.util.Currency
class DonationReceiptTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper) {
companion object {
private const val TABLE_NAME = "donation_receipt"
@VisibleForTesting
const val TABLE_NAME = "donation_receipt"
private const val ID = "_id"
private const val TYPE = "receipt_type"
@@ -8,6 +8,7 @@ import org.signal.core.util.SqlUtil
import org.signal.core.util.delete
import org.signal.core.util.deleteAll
import org.signal.core.util.forEach
import org.signal.core.util.logging.Log
import org.signal.core.util.readToList
import org.signal.core.util.requireBoolean
import org.signal.core.util.requireInt
@@ -20,6 +21,8 @@ import org.thoughtcrime.securesms.recipients.RecipientId
class GroupReceiptTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseTable(context, databaseHelper), RecipientIdDatabaseReference {
companion object {
private val TAG = Log.tag(GroupReceiptTable::class)
const val TABLE_NAME = "group_receipts"
private const val ID = "_id"
const val MMS_ID = "mms_id"
@@ -174,11 +177,13 @@ class GroupReceiptTable(context: Context?, databaseHelper: SignalDatabase?) : Da
}
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
writableDatabase
val count = writableDatabase
.update(TABLE_NAME)
.values(RECIPIENT_ID to toId.serialize())
.where("$RECIPIENT_ID = ?", fromId)
.run()
Log.d(TAG, "Remapped $fromId to $toId. count: $count")
}
private fun Cursor.toGroupReceiptInfo(): GroupReceiptInfo {
@@ -6,6 +6,7 @@ import org.signal.core.util.SqlUtil
import org.signal.core.util.delete
import org.signal.core.util.deleteAll
import org.signal.core.util.insertInto
import org.signal.core.util.logging.Log
import org.signal.core.util.readToList
import org.signal.core.util.requireInt
import org.signal.core.util.requireLong
@@ -18,6 +19,8 @@ import org.thoughtcrime.securesms.recipients.RecipientId
class MentionTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper), RecipientIdDatabaseReference, ThreadIdDatabaseReference {
companion object {
private val TAG = Log.tag(MentionTable::class)
const val TABLE_NAME = "mention"
const val ID = "_id"
const val THREAD_ID = "thread_id"
@@ -165,11 +168,13 @@ class MentionTable(context: Context, databaseHelper: SignalDatabase) : DatabaseT
}
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
writableDatabase
val count = writableDatabase
.update("$TABLE_NAME INDEXED BY $RECIPIENT_ID_INDEX")
.values(RECIPIENT_ID to toId.serialize())
.where("$RECIPIENT_ID = ?", fromId)
.run()
Log.d(TAG, "Remapped $fromId to $toId. count: $count")
}
override fun remapThread(fromId: Long, toId: Long) {
@@ -11,6 +11,7 @@ import org.signal.core.util.readToList
import org.signal.core.util.requireBoolean
import org.signal.core.util.requireLong
import org.signal.core.util.toInt
import org.signal.core.util.update
import org.thoughtcrime.securesms.database.model.MessageId
import org.thoughtcrime.securesms.database.model.MessageLogEntry
import org.thoughtcrime.securesms.recipients.Recipient
@@ -419,16 +420,14 @@ class MessageSendLogTables constructor(context: Context?, databaseHelper: Signal
writableDatabase.execSQL(MslPayloadTable.AFTER_MESSAGE_DELETE_TRIGGER)
}
override fun remapRecipient(oldRecipientId: RecipientId, newRecipientId: RecipientId) {
val values = ContentValues().apply {
put(MslRecipientTable.RECIPIENT_ID, newRecipientId.serialize())
}
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
val count = writableDatabase
.update(MslRecipientTable.TABLE_NAME)
.values(MslRecipientTable.RECIPIENT_ID to toId.serialize())
.where("${MslRecipientTable.RECIPIENT_ID} = ?", fromId)
.run()
val db = databaseHelper.signalWritableDatabase
val query = "${MslRecipientTable.RECIPIENT_ID} = ?"
val args = SqlUtil.buildArgs(oldRecipientId.serialize())
db.update(MslRecipientTable.TABLE_NAME, values, query, args)
Log.d(TAG, "Remapped $fromId to $toId. count: $count")
}
private data class RecipientDevice(val recipientId: RecipientId, val devices: List<Int>)
@@ -4803,17 +4803,25 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
writableDatabase
val fromCount = writableDatabase
.update(TABLE_NAME)
.values(FROM_RECIPIENT_ID to toId.serialize())
.where("$FROM_RECIPIENT_ID = ?", fromId)
.run()
writableDatabase
val toCount = writableDatabase
.update(TABLE_NAME)
.values(TO_RECIPIENT_ID to toId.serialize())
.where("$TO_RECIPIENT_ID = ?", fromId)
.run()
val quoteAuthorCount = writableDatabase
.update(TABLE_NAME)
.values(QUOTE_AUTHOR to toId.serialize())
.where("$QUOTE_AUTHOR = ?", fromId)
.run()
Log.d(TAG, "Remapped $fromId to $toId. fromRecipient: $fromCount, toRecipient: $toCount, quoteAuthor: $quoteAuthorCount")
}
override fun remapThread(fromId: Long, toId: Long) {
@@ -7,11 +7,13 @@ import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteConstraintException
import org.signal.core.util.SqlUtil
import org.signal.core.util.logging.Log
import org.signal.core.util.requireBoolean
import org.signal.core.util.requireInt
import org.signal.core.util.requireLong
import org.signal.core.util.requireString
import org.signal.core.util.toInt
import org.signal.core.util.update
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
@@ -22,9 +24,11 @@ import java.time.DayOfWeek
/**
* Database for maintaining Notification Profiles, Notification Profile Schedules, and Notification Profile allowed memebers.
*/
class NotificationProfileDatabase(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper), RecipientIdDatabaseReference {
class NotificationProfileTables(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper), RecipientIdDatabaseReference {
companion object {
private val TAG = Log.tag(NotificationProfileTable::class)
@JvmField
val CREATE_TABLE: Array<String> = arrayOf(NotificationProfileTable.CREATE_TABLE, NotificationProfileScheduleTable.CREATE_TABLE, NotificationProfileAllowedMembersTable.CREATE_TABLE)
@@ -32,7 +36,7 @@ class NotificationProfileDatabase(context: Context, databaseHelper: SignalDataba
val CREATE_INDEXES: Array<String> = arrayOf(NotificationProfileScheduleTable.CREATE_INDEX, NotificationProfileAllowedMembersTable.CREATE_INDEX)
}
private object NotificationProfileTable {
object NotificationProfileTable {
const val TABLE_NAME = "notification_profile"
const val ID = "_id"
@@ -56,7 +60,7 @@ class NotificationProfileDatabase(context: Context, databaseHelper: SignalDataba
"""
}
private object NotificationProfileScheduleTable {
object NotificationProfileScheduleTable {
const val TABLE_NAME = "notification_profile_schedule"
const val ID = "_id"
@@ -82,7 +86,7 @@ class NotificationProfileDatabase(context: Context, databaseHelper: SignalDataba
const val CREATE_INDEX = "CREATE INDEX notification_profile_schedule_profile_index ON $TABLE_NAME ($NOTIFICATION_PROFILE_ID)"
}
private object NotificationProfileAllowedMembersTable {
object NotificationProfileAllowedMembersTable {
const val TABLE_NAME = "notification_profile_allowed_members"
const val ID = "_id"
@@ -296,16 +300,15 @@ class NotificationProfileDatabase(context: Context, databaseHelper: SignalDataba
AppDependencies.databaseObserver.notifyNotificationProfileObservers()
}
override fun remapRecipient(oldId: RecipientId, newId: RecipientId) {
val query = "${NotificationProfileAllowedMembersTable.RECIPIENT_ID} = ?"
val args = SqlUtil.buildArgs(oldId)
val values = ContentValues().apply {
put(NotificationProfileAllowedMembersTable.RECIPIENT_ID, newId.serialize())
}
databaseHelper.signalWritableDatabase.update(NotificationProfileAllowedMembersTable.TABLE_NAME, values, query, args)
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
val count = writableDatabase
.update(NotificationProfileAllowedMembersTable.TABLE_NAME)
.values(NotificationProfileAllowedMembersTable.RECIPIENT_ID to toId.serialize())
.where("${NotificationProfileAllowedMembersTable.RECIPIENT_ID} = ?", fromId)
.run()
AppDependencies.databaseObserver.notifyNotificationProfileObservers()
Log.d(TAG, "Remapped $fromId to $toId. count: $count")
}
private fun getProfile(cursor: Cursor): NotificationProfile {
@@ -367,7 +370,7 @@ class NotificationProfileDatabase(context: Context, databaseHelper: SignalDataba
}
}
private fun Iterable<DayOfWeek>.serialize(): String {
fun Iterable<DayOfWeek>.serialize(): String {
return joinToString(separator = ",", transform = { it.serialize() })
}
@@ -478,7 +478,9 @@ public final class PaymentTable extends DatabaseTable implements RecipientIdData
public void remapRecipient(@NonNull RecipientId fromId, @NonNull RecipientId toId) {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, toId.serialize());
getWritableDatabase().update(TABLE_NAME, values, RECIPIENT_ID + " = ?", SqlUtil.buildArgs(fromId));
int count = getWritableDatabase().update(TABLE_NAME, values, RECIPIENT_ID + " = ?", SqlUtil.buildArgs(fromId));
Log.d(TAG, "Remapped " + fromId + " to " + toId + ". count: " + count);
}
public boolean markPaymentSubmitted(@NonNull UUID uuid,
@@ -121,11 +121,13 @@ class PendingPniSignatureMessageTable(context: Context, databaseHelper: SignalDa
writableDatabase.deleteAll(TABLE_NAME)
}
override fun remapRecipient(oldId: RecipientId, newId: RecipientId) {
writableDatabase
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
val count = writableDatabase
.update(TABLE_NAME)
.values(RECIPIENT_ID to newId.serialize())
.where("$RECIPIENT_ID = ?", oldId)
.values(RECIPIENT_ID to toId.serialize())
.where("$RECIPIENT_ID = ?", fromId)
.run()
Log.d(TAG, "Remapped $fromId to $toId. count: $count")
}
}
@@ -8,6 +8,7 @@ import androidx.annotation.NonNull;
import net.zetetic.database.sqlcipher.SQLiteDatabase;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.model.PendingRetryReceiptModel;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -24,6 +25,8 @@ import java.util.List;
*/
public final class PendingRetryReceiptTable extends DatabaseTable implements RecipientIdDatabaseReference, ThreadIdDatabaseReference {
private static final String TAG = Log.tag(PendingRetryReceiptTable.class);
public static final String TABLE_NAME = "pending_retry_receipts";
private static final String ID = "_id";
@@ -87,9 +90,11 @@ public final class PendingRetryReceiptTable extends DatabaseTable implements Rec
public void remapRecipient(@NonNull RecipientId fromId, @NonNull RecipientId toId) {
ContentValues values = new ContentValues();
values.put(AUTHOR, toId.serialize());
getWritableDatabase().update(TABLE_NAME, values, AUTHOR + " = ?", SqlUtil.buildArgs(fromId));
int count = getWritableDatabase().update(TABLE_NAME, values, AUTHOR + " = ?", SqlUtil.buildArgs(fromId));
AppDependencies.getPendingRetryReceiptCache().clear();
Log.d(TAG, "Remapped " + fromId + " to " + toId + ". count: " + count);
}
@Override
@@ -7,6 +7,7 @@ import org.signal.core.util.CursorUtil
import org.signal.core.util.SqlUtil
import org.signal.core.util.delete
import org.signal.core.util.forEach
import org.signal.core.util.logging.Log
import org.signal.core.util.select
import org.signal.core.util.update
import org.thoughtcrime.securesms.database.model.MessageId
@@ -20,6 +21,8 @@ import org.thoughtcrime.securesms.recipients.RecipientId
class ReactionTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper), RecipientIdDatabaseReference {
companion object {
private val TAG = Log.tag(ReactionTable::class)
const val TABLE_NAME = "reaction"
private const val ID = "_id"
@@ -167,14 +170,14 @@ class ReactionTable(context: Context, databaseHelper: SignalDatabase) : Database
}
}
override fun remapRecipient(oldAuthorId: RecipientId, newAuthorId: RecipientId) {
val query = "$AUTHOR_ID = ?"
val args = SqlUtil.buildArgs(oldAuthorId)
val values = ContentValues().apply {
put(AUTHOR_ID, newAuthorId.serialize())
}
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
val count = writableDatabase
.update(TABLE_NAME)
.values(AUTHOR_ID to toId.serialize())
.where("$AUTHOR_ID = ?", fromId)
.run()
readableDatabase.update(TABLE_NAME, values, query, args)
Log.d(TAG, "Remapped $fromId to $toId. count: $count")
}
fun deleteAbandonedReactions() {
@@ -2133,67 +2133,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
.readToSingleObject { PhoneNumberDiscoverableState.fromId(it.requireInt(PHONE_NUMBER_DISCOVERABLE)) }
}
/**
* @return True if setting the phone number resulted in changed recipientId, otherwise false.
*/
fun setPhoneNumber(id: RecipientId, e164: String): Boolean {
val db = writableDatabase
db.beginTransaction()
return try {
setPhoneNumberOrThrow(id, e164)
db.setTransactionSuccessful()
false
} catch (e: SQLiteConstraintException) {
Log.w(TAG, "[setPhoneNumber] Hit a conflict when trying to update $id. Possibly merging.")
val existing: RecipientRecord = getRecord(id)
val newId = getAndPossiblyMerge(existing.aci, e164)
Log.w(TAG, "[setPhoneNumber] Resulting id: $newId")
db.setTransactionSuccessful()
newId != existing.id
} finally {
db.endTransaction()
}
}
private fun removePhoneNumber(recipientId: RecipientId) {
val values = ContentValues().apply {
putNull(E164)
putNull(PNI_COLUMN)
}
if (update(recipientId, values)) {
rotateStorageId(recipientId)
}
}
/**
* Should only use if you are confident that this will not result in any contact merging.
*/
@Throws(SQLiteConstraintException::class)
fun setPhoneNumberOrThrow(id: RecipientId, e164: String) {
val contentValues = ContentValues(1).apply {
put(E164, e164)
}
if (update(id, contentValues)) {
rotateStorageId(id)
AppDependencies.databaseObserver.notifyRecipientChanged(id)
StorageSyncHelper.scheduleSyncForDataChange()
}
}
@Throws(SQLiteConstraintException::class)
fun setPhoneNumberOrThrowSilent(id: RecipientId, e164: String) {
val contentValues = ContentValues(1).apply {
put(E164, e164)
}
if (update(id, contentValues)) {
rotateStorageId(id)
}
}
/**
* Associates the provided IDs together. The assumption here is that all of the IDs correspond to the local user and have been verified.
*/
@@ -4051,6 +3990,13 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
}
}
/**
* Exposes the merge functionality for the sake of an app migration.
*/
fun mergeForMigration(primaryId: RecipientId, secondaryId: RecipientId) {
merge(primaryId, secondaryId, pniVerified = true)
}
/**
* Merges one ACI recipient with an E164 recipient. It is assumed that the E164 recipient does
* *not* have an ACI.
@@ -63,7 +63,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
val messageSendLogTables: MessageSendLogTables = MessageSendLogTables(context, this)
val avatarPickerDatabase: AvatarPickerDatabase = AvatarPickerDatabase(context, this)
val reactionTable: ReactionTable = ReactionTable(context, this)
val notificationProfileDatabase: NotificationProfileDatabase = NotificationProfileDatabase(context, this)
val notificationProfileTables: NotificationProfileTables = NotificationProfileTables(context, this)
val donationReceiptTable: DonationReceiptTable = DonationReceiptTable(context, this)
val distributionListTables: DistributionListTables = DistributionListTables(context, this)
val storySendTable: StorySendTable = StorySendTable(context, this)
@@ -120,7 +120,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
executeStatements(db, SearchTable.CREATE_TABLE)
executeStatements(db, RemappedRecordTables.CREATE_TABLE)
executeStatements(db, MessageSendLogTables.CREATE_TABLE)
executeStatements(db, NotificationProfileDatabase.CREATE_TABLE)
executeStatements(db, NotificationProfileTables.CREATE_TABLE)
executeStatements(db, DistributionListTables.CREATE_TABLE)
executeStatements(db, ChatFolderTables.CREATE_TABLE)
db.execSQL(BackupMediaSnapshotTable.CREATE_TABLE)
@@ -137,7 +137,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
executeStatements(db, MentionTable.CREATE_INDEXES)
executeStatements(db, PaymentTable.CREATE_INDEXES)
executeStatements(db, MessageSendLogTables.CREATE_INDEXES)
executeStatements(db, NotificationProfileDatabase.CREATE_INDEXES)
executeStatements(db, NotificationProfileTables.CREATE_INDEXES)
executeStatements(db, DonationReceiptTable.CREATE_INDEXS)
executeStatements(db, StorySendTable.CREATE_INDEXS)
executeStatements(db, DistributionListTables.CREATE_INDEXES)
@@ -456,8 +456,8 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
@get:JvmStatic
@get:JvmName("notificationProfiles")
val notificationProfiles: NotificationProfileDatabase
get() = instance!!.notificationProfileDatabase
val notificationProfiles: NotificationProfileTables
get() = instance!!.notificationProfileTables
@get:JvmStatic
@get:JvmName("payments")
@@ -5,6 +5,7 @@ import android.content.Context
import androidx.core.content.contentValuesOf
import org.signal.core.util.CursorUtil
import org.signal.core.util.SqlUtil
import org.signal.core.util.logging.Log
import org.signal.core.util.readToList
import org.signal.core.util.requireLong
import org.signal.core.util.select
@@ -26,6 +27,8 @@ import org.whispersystems.signalservice.api.push.DistributionId
class StorySendTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper), RecipientIdDatabaseReference {
companion object {
private val TAG = Log.tag(StorySendTable::class)
const val TABLE_NAME = "story_sends"
const val ID = "_id"
const val MESSAGE_ID = "message_id"
@@ -210,12 +213,14 @@ class StorySendTable(context: Context, databaseHelper: SignalDatabase) : Databas
return null
}
override fun remapRecipient(oldId: RecipientId, newId: RecipientId) {
val query = "$RECIPIENT_ID = ?"
val args = SqlUtil.buildArgs(oldId)
val values = contentValuesOf(RECIPIENT_ID to newId.serialize())
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
val count = writableDatabase
.update(TABLE_NAME)
.values(RECIPIENT_ID to toId.serialize())
.where("$RECIPIENT_ID = ?", fromId.serialize())
.run()
writableDatabase.update(TABLE_NAME, values, query, args)
Log.d(TAG, "Remapped $fromId to $toId. count: $count")
}
/**
@@ -116,6 +116,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V256_FixIncremental
import org.thoughtcrime.securesms.database.helpers.migration.V257_CreateBackupMediaSyncTable
import org.thoughtcrime.securesms.database.helpers.migration.V258_FixGroupRevokedInviteeUpdate
import org.thoughtcrime.securesms.database.helpers.migration.V259_AdjustNotificationProfileMidnightEndTimes
import org.thoughtcrime.securesms.database.helpers.migration.V260_RemapQuoteAuthors
/**
* Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness.
@@ -234,10 +235,11 @@ object SignalDatabaseMigrations {
256 to V256_FixIncrementalDigestColumns,
257 to V257_CreateBackupMediaSyncTable,
258 to V258_FixGroupRevokedInviteeUpdate,
259 to V259_AdjustNotificationProfileMidnightEndTimes
259 to V259_AdjustNotificationProfileMidnightEndTimes,
260 to V260_RemapQuoteAuthors
)
const val DATABASE_VERSION = 259
const val DATABASE_VERSION = 260
@JvmStatic
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
@@ -0,0 +1,66 @@
package org.thoughtcrime.securesms.database.helpers.migration
import android.app.Application
import net.zetetic.database.sqlcipher.SQLiteDatabase
import org.signal.core.util.logging.Log
import org.signal.core.util.readToSingleLong
import org.signal.core.util.select
/**
* Previously, we weren't properly remapping quote authors when recipients were remapped. This repairs those scenarios the best we can.
*/
@Suppress("ClassName")
object V260_RemapQuoteAuthors : SignalDatabaseMigration {
private val TAG = Log.tag(V260_RemapQuoteAuthors::class)
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
// The following queries are really expensive without an index. So we create a temporary one.
db.execSQL("CREATE INDEX tmp_quote_author ON message (quote_author)")
// Even with an index, the updates can be a little expensive, so we try to figure out if we need them at all by using a quick check.
val invalidQuoteCount = db
.select("count(*)")
.from("message INDEXED BY tmp_quote_author")
.where("quote_author != 0 AND quote_author NOT IN (SELECT _id FROM recipient)")
.run()
.readToSingleLong()
if (invalidQuoteCount == 0L) {
Log.i(TAG, "No invalid quote authors, can skip migration.")
db.execSQL("DROP INDEX tmp_quote_author")
return
}
// Remap all quote_authors using a remapped recipient
db.execSQL(
"""
UPDATE
message INDEXED BY tmp_quote_author
SET
quote_author = (SELECT new_id FROM remapped_recipients WHERE old_id = message.quote_author)
WHERE
quote_author IN (SELECT old_id FROM remapped_recipients)
"""
)
// If there are any remaining quote_authors that don't reference a real recipient, we have no choice but to clear the quote
db.execSQL(
"""
UPDATE
message INDEXED BY tmp_quote_author
SET
quote_id = 0,
quote_author = 0,
quote_body = null,
quote_missing = 0,
quote_mentions = null,
quote_type = 0
WHERE
quote_author != 0 AND quote_author NOT IN (SELECT _id FROM recipient)
"""
)
db.execSQL("DROP INDEX tmp_quote_author")
}
}
@@ -5,6 +5,7 @@
package org.thoughtcrime.securesms.jobs
import androidx.annotation.VisibleForTesting
import io.reactivex.rxjava3.core.Single
import org.signal.core.util.logging.Log
import org.signal.core.util.money.FiatMoney
@@ -40,7 +41,8 @@ import kotlin.time.Duration.Companion.days
*/
class InAppPaymentAuthCheckJob private constructor(parameters: Parameters) : BaseJob(parameters), StripeApi.PaymentIntentFetcher, StripeApi.SetupIntentHelper {
private constructor() : this(
@VisibleForTesting
constructor() : this(
Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setMaxAttempts(Parameters.UNLIMITED)
@@ -138,7 +140,10 @@ class InAppPaymentAuthCheckJob private constructor(parameters: Parameters) : Bas
)
)
checkIntentStatus(stripeIntentData.status)
val checkIntentStatusResult = checkIntentStatus(stripeIntentData.status)
if (checkIntentStatusResult !is CheckResult.Success) {
return checkIntentStatusResult
}
Log.i(TAG, "Creating and inserting receipt.", true)
val receipt = when (inAppPayment.type) {
@@ -54,6 +54,7 @@ import org.thoughtcrime.securesms.migrations.BackfillDigestsForDuplicatesMigrati
import org.thoughtcrime.securesms.migrations.BackfillDigestsMigrationJob;
import org.thoughtcrime.securesms.migrations.BackupJitterMigrationJob;
import org.thoughtcrime.securesms.migrations.BackupNotificationMigrationJob;
import org.thoughtcrime.securesms.migrations.BadE164MigrationJob;
import org.thoughtcrime.securesms.migrations.BlobStorageLocationMigrationJob;
import org.thoughtcrime.securesms.migrations.CachedAttachmentsMigrationJob;
import org.thoughtcrime.securesms.migrations.ClearGlideCacheMigrationJob;
@@ -277,6 +278,7 @@ public final class JobManagerFactories {
put(BackupMediaSnapshotSyncJob.KEY, new BackupMediaSnapshotSyncJob.Factory());
put(BackupNotificationMigrationJob.KEY, new BackupNotificationMigrationJob.Factory());
put(BackupRefreshJob.KEY, new BackupRefreshJob.Factory());
put(BadE164MigrationJob.KEY, new BadE164MigrationJob.Factory());
put(BlobStorageLocationMigrationJob.KEY, new BlobStorageLocationMigrationJob.Factory());
put(CachedAttachmentsMigrationJob.KEY, new CachedAttachmentsMigrationJob.Factory());
put(ClearGlideCacheMigrationJob.KEY, new ClearGlideCacheMigrationJob.Factory());
@@ -163,9 +163,11 @@ public class ApplicationMigrations {
static final int AEP_INTRODUCTION = 119;
static final int GROUP_EXTRAS_DB_FIX = 120;
static final int EMOJI_SEARCH_INDEX_CHECK_2 = 121;
static final int QUOTE_AUTHOR_FIX = 122;
static final int BAD_E164_FIX = 123;
}
public static final int CURRENT_VERSION = 121;
public static final int CURRENT_VERSION = 123;
/**
* This *must* be called after the {@link JobManager} has been instantiated, but *before* the call
@@ -748,6 +750,14 @@ public class ApplicationMigrations {
jobs.put(Version.EMOJI_SEARCH_INDEX_CHECK_2, new EmojiSearchIndexCheckMigrationJob());
}
if (lastSeenVersion < Version.QUOTE_AUTHOR_FIX) {
jobs.put(Version.QUOTE_AUTHOR_FIX, new DatabaseMigrationJob());
}
if (lastSeenVersion < Version.BAD_E164_FIX) {
jobs.put(Version.BAD_E164_FIX, new BadE164MigrationJob());
}
return jobs;
}
@@ -0,0 +1,176 @@
package org.thoughtcrime.securesms.migrations
import android.database.sqlite.SQLiteConstraintException
import org.signal.core.util.Stopwatch
import org.signal.core.util.delete
import org.signal.core.util.logging.Log
import org.signal.core.util.readToList
import org.signal.core.util.requireLong
import org.signal.core.util.requireNonNullString
import org.signal.core.util.select
import org.signal.core.util.update
import org.signal.core.util.withinTransaction
import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.RecipientTable.Companion.ACI_COLUMN
import org.thoughtcrime.securesms.database.RecipientTable.Companion.E164
import org.thoughtcrime.securesms.database.RecipientTable.Companion.ID
import org.thoughtcrime.securesms.database.RecipientTable.Companion.PNI_COLUMN
import org.thoughtcrime.securesms.database.RecipientTable.Companion.TABLE_NAME
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
import org.thoughtcrime.securesms.recipients.RecipientId
/**
* Through testing, we've discovered that some badly-formatted e164's wound up in the e164 column of the recipient table.
* We believe this was mostly a result of a legacy chat-creation path where we'd basically create a recipient with any number that was input.
* This makes a best-effort to clear out that bad data in a safe manner.
* Normally we'd do something like this in a DB migration, but we wanted to have access to recipient merging and number formatting, which could
* be unnecessarily difficult in a DB migration.
*/
internal class BadE164MigrationJob(
parameters: Parameters = Parameters.Builder().build()
) : MigrationJob(parameters) {
companion object {
val TAG = Log.tag(BadE164MigrationJob::class.java)
const val KEY = "BadE164MigrationJob"
/**
* Accounts for the following types of invalid numbers:
* - Contains an invalid char anywhere (i.e. not a digit or +)
* - A shortcode that doesn't start with a number
* - A non-shortcode (longcode?) that doesn't start with +{digit}
* - A number with exactly 7 chars (strange but true -- neither shortcodes nor longcodes can be 7 chars long)
*/
private const val INVALID_E164_QUERY =
"""
(
$E164 NOT NULL AND (
$E164 GLOB '*[^+0-9]*'
OR (LENGTH($E164) > 7 AND $E164 NOT GLOB '+[0-9]*')
OR (LENGTH($E164) == 7)
OR (LENGTH($E164) < 7 AND $E164 NOT GLOB '[0-9]*')
)
)
"""
}
override fun getFactoryKey(): String = KEY
override fun isUiBlocking(): Boolean = false
override fun performMigration() {
val stopwatch = Stopwatch("bad-e164")
val invalidWithOtherIdentifiersCount = SignalDatabase.recipients.removeInvalidE164sFromRecipientsWithOtherIdentifiers()
Log.d(TAG, "Removed $invalidWithOtherIdentifiersCount invalid e164's from recipients that had another identifier.")
stopwatch.split("alt-id")
val invalidWithNoMessagesCount = SignalDatabase.recipients.deleteRecipientsWithInvalidE164sAndNoMessages()
Log.d(TAG, "Deleted $invalidWithNoMessagesCount recipients with invalid e164's and no messages.")
stopwatch.split("no-msg")
val invalidEntries = SignalDatabase.recipients.getRecipientsWithInvalidE164s()
stopwatch.split("fetch-invalid")
if (invalidEntries.isEmpty()) {
Log.d(TAG, "No more invalid e164s, we're done.")
stopwatch.stop(TAG)
return
}
Log.i(TAG, "There are ${invalidEntries.size} remaining entries ")
val hasLettersRegex = "[^+0-9]".toRegex()
for (entry in invalidEntries) {
if (entry.e164.matches(hasLettersRegex)) {
Log.w(TAG, "We have encountered an e164-only recipient, with messages, that has invalid characters in the e164.")
continue
}
val formattedNumber = PhoneNumberFormatter.get(context).formatOrNull(entry.e164)
if (formattedNumber == null) {
Log.w(TAG, "We have encountered an e164-only recipient, with messages, whose value characters are all valid, but still remains completely unparsable.")
continue
}
if (formattedNumber == entry.e164) {
Log.w(TAG, "We have encountered an e164-only recipient, with messages, whose value characters are all valid, but after formatting the number, it's the same!")
continue
}
SignalDatabase.recipients.updateE164(entry.id, entry.e164)
Log.w(TAG, "Update the E164 to the new, formatted number.")
}
stopwatch.split("merge")
stopwatch.stop(TAG)
}
override fun shouldRetry(e: Exception): Boolean = false
private fun RecipientTable.removeInvalidE164sFromRecipientsWithOtherIdentifiers(): Int {
return readableDatabase
.update(TABLE_NAME)
.values(E164 to null)
.where("$INVALID_E164_QUERY AND ($ACI_COLUMN NOT NULL OR $PNI_COLUMN NOT NULL)")
.run()
}
private fun RecipientTable.deleteRecipientsWithInvalidE164sAndNoMessages(): Int {
return readableDatabase
.delete(TABLE_NAME)
.where(
"""
$INVALID_E164_QUERY AND
$ID NOT IN (
SELECT ${MessageTable.TO_RECIPIENT_ID} FROM ${MessageTable.TABLE_NAME}
UNION
SELECT ${MessageTable.FROM_RECIPIENT_ID} FROM ${MessageTable.TABLE_NAME}
)
"""
)
.run()
}
private fun RecipientTable.getRecipientsWithInvalidE164s(): List<InvalidEntry> {
return readableDatabase
.select(ID, E164)
.from(TABLE_NAME)
.where(INVALID_E164_QUERY)
.run()
.readToList {
InvalidEntry(
id = it.requireLong(ID),
e164 = it.requireNonNullString(E164)
)
}
}
private fun RecipientTable.updateE164(id: Long, e164: String) {
writableDatabase.withinTransaction { db ->
try {
db.update(TABLE_NAME)
.values(E164 to e164)
.where("$ID = ?", id)
.run()
} catch (e: SQLiteConstraintException) {
Log.w(TAG, "There was a conflict when trying to update Recipient::$id with a properly-formatted E164. Merging.")
val existing = getByE164(e164).get()
mergeForMigration(existing, RecipientId.from(id))
}
}
}
private data class InvalidEntry(
val id: Long,
val e164: String
)
class Factory : Job.Factory<BadE164MigrationJob> {
override fun create(parameters: Parameters, serializedData: ByteArray?): BadE164MigrationJob {
return BadE164MigrationJob(parameters)
}
}
}
@@ -32,7 +32,6 @@ public class PhoneNumberFormatter {
private static final String UNKNOWN_NUMBER = "Unknown";
private static final Set<String> EXCLUDE_FROM_MANUAL_SHORTCODE_4 = SetUtil.newHashSet("AC", "NC", "NU", "TK");
private static final Set<String> MANUAL_SHORTCODE_6 = SetUtil.newHashSet("DE", "FI", "GB", "SK");
private static final Set<Integer> NATIONAL_FORMAT_COUNTRY_CODES = SetUtil.newHashSet(1 /*US*/, 44 /*UK*/);
private static final Pattern US_NO_AREACODE = Pattern.compile("^(\\d{7})$");
@@ -128,11 +127,7 @@ public class PhoneNumberFormatter {
else return number.trim();
}
if (bareNumber.length() <= 6 && MANUAL_SHORTCODE_6.contains(localCountryCode)) {
return bareNumber;
}
if (bareNumber.length() <= 4 && !EXCLUDE_FROM_MANUAL_SHORTCODE_4.contains(localCountryCode)) {
if (bareNumber.length() <= 6) {
return bareNumber;
}
@@ -82,8 +82,7 @@ class TransferAccountActivity : PassphraseRequiredActivity() {
private const val KEY_URI = "URI"
// TODO [backups] Put actual learn more url
const val LEARN_MORE_URL = "https://signal.org#"
const val LEARN_MORE_URL = "https://support.signal.org/hc/articles/360007059752-Backup-and-Restore-Messages"
fun intent(context: Context, uri: String): Intent {
return Intent(context, TransferAccountActivity::class.java).apply {
@@ -72,7 +72,7 @@ import org.thoughtcrime.securesms.util.navigation.safeNavigate
class EnterBackupKeyFragment : ComposeFragment() {
companion object {
private const val LEARN_MORE_URL = "https://signal.org" // TODO [backups] but really
private const val LEARN_MORE_URL = "https://support.signal.org/hc/articles/360007059752-Backup-and-Restore-Messages"
}
private val sharedViewModel by activityViewModels<RegistrationViewModel>()
@@ -28,7 +28,7 @@ class EnterBackupKeyViewModel : ViewModel() {
fun updateBackupKey(key: String) {
_state.update {
val newKey = key.removeIllegalCharacters().take(length)
val newKey = key.removeIllegalCharacters().take(length).lowercase()
copy(backupKey = newKey, backupKeyValid = validate(length, newKey))
}
}
@@ -394,8 +394,8 @@ fun RestoreFailedDialog(
onDismiss: () -> Unit = {}
) {
Dialogs.SimpleAlertDialog(
title = "Restore Failed", // TODO [backups] Remote restore error placeholder copy
body = "Unable to restore from backup. Please try again.", // TODO [backups] Placeholder copy
title = stringResource(R.string.RemoteRestoreActivity__couldnt_transfer),
body = stringResource(R.string.RemoteRestoreActivity__error_occurred),
confirm = stringResource(android.R.string.ok),
onConfirm = onDismiss,
onDismiss = onDismiss
@@ -49,6 +49,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
@@ -64,6 +65,7 @@ import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.settings.app.usernamelinks.QrCode
import org.thoughtcrime.securesms.components.settings.app.usernamelinks.QrCodeData
import org.thoughtcrime.securesms.compose.ComposeFragment
import org.thoughtcrime.securesms.registration.data.network.RegisterAccountResult
import org.thoughtcrime.securesms.registrationv3.ui.RegistrationViewModel
import org.thoughtcrime.securesms.registrationv3.ui.shared.RegistrationScreen
import org.thoughtcrime.securesms.util.navigation.safeNavigate
@@ -95,6 +97,24 @@ class RestoreViaQrFragment : ComposeFragment() {
}
}
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
viewModel
.state
.mapNotNull { it.registerAccountResult }
.filter { it !is RegisterAccountResult.Success }
.distinctUntilChanged()
.collect { result ->
when (result) {
is RegisterAccountResult.AttemptsExhausted -> {
findNavController().safeNavigate(RestoreViaQrFragmentDirections.goToAccountLocked())
}
else -> Unit
}
}
}
}
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
sharedViewModel
@@ -103,7 +123,7 @@ class RestoreViaQrFragment : ComposeFragment() {
.filterNotNull()
.collect {
sharedViewModel.registerAccountErrorShown()
viewModel.handleRegistrationFailure()
viewModel.handleRegistrationFailure(it)
}
}
}
@@ -169,8 +189,12 @@ private fun RestoreViaQrScreen(
) {
AnimatedContent(
targetState = state.qrState,
contentKey = { it::class },
contentAlignment = Alignment.Center,
label = "qr-code-progress"
label = "qr-code-progress",
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) { qrState ->
when (qrState) {
is RestoreViaQrViewModel.QrState.Loaded -> {
@@ -184,7 +208,9 @@ private fun RestoreViaQrScreen(
}
RestoreViaQrViewModel.QrState.Loading -> {
CircularProgressIndicator(modifier = Modifier.size(48.dp))
Box(contentAlignment = Alignment.Center) {
CircularProgressIndicator(modifier = Modifier.size(48.dp))
}
}
is RestoreViaQrViewModel.QrState.Scanned,
@@ -246,8 +272,14 @@ private fun RestoreViaQrScreen(
if (state.isRegistering) {
Dialogs.IndeterminateProgressDialog()
} else if (state.showRegistrationError) {
val message = when (state.registerAccountResult) {
is RegisterAccountResult.IncorrectRecoveryPassword -> stringResource(R.string.RestoreViaQr_registration_error)
is RegisterAccountResult.RateLimited -> stringResource(R.string.RegistrationActivity_you_have_made_too_many_attempts_please_try_again_later)
else -> stringResource(R.string.RegistrationActivity_error_connecting_to_service)
}
Dialogs.SimpleMessageDialog(
message = stringResource(R.string.RegistrationActivity_error_connecting_to_service),
message = message,
onDismiss = onRegistrationErrorDismiss,
dismiss = stringResource(android.R.string.ok)
)
@@ -6,10 +6,15 @@
package org.thoughtcrime.securesms.registrationv3.ui.restore
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.CoroutineExceptionHandler
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.signal.core.util.logging.Log
import org.signal.registration.proto.RegistrationProvisionMessage
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
@@ -17,6 +22,7 @@ import org.thoughtcrime.securesms.components.settings.app.usernamelinks.QrCodeDa
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.registration.data.network.RegisterAccountResult
import org.whispersystems.signalservice.api.registration.ProvisioningSocket
import org.whispersystems.signalservice.internal.crypto.SecondaryProvisioningCipher
import java.io.Closeable
@@ -31,24 +37,41 @@ class RestoreViaQrViewModel : ViewModel() {
val state: StateFlow<RestoreViaQrState> = store
private var socketHandle: Closeable
private var socketHandles: MutableList<Closeable> = mutableListOf()
private var startNewSocketJob: Job? = null
init {
socketHandle = start()
restart()
}
fun restart() {
socketHandle.close()
socketHandle = start()
SignalStore.registration.restoreMethodToken = null
shutdown()
startNewSocket()
startNewSocketJob = viewModelScope.launch(Dispatchers.IO) {
var count = 0
while (count < 5 && isActive) {
delay(ProvisioningSocket.LIFESPAN / 2)
if (isActive) {
startNewSocket()
count++
Log.d(TAG, "Started next websocket count: $count")
}
}
}
}
fun handleRegistrationFailure() {
fun handleRegistrationFailure(registerAccountResult: RegisterAccountResult) {
store.update {
if (it.isRegistering) {
Log.w(TAG, "Unable to register [${registerAccountResult::class.simpleName}]", registerAccountResult.getCause())
it.copy(
isRegistering = false,
provisioningMessage = null,
showRegistrationError = true
showRegistrationError = true,
registerAccountResult = registerAccountResult
)
} else {
it
@@ -57,24 +80,77 @@ class RestoreViaQrViewModel : ViewModel() {
}
fun clearRegistrationError() {
store.update { it.copy(showRegistrationError = false) }
store.update {
it.copy(
showRegistrationError = false,
registerAccountResult = null
)
}
restart()
}
override fun onCleared() {
socketHandle.close()
shutdown()
}
private fun startNewSocket() {
synchronized(socketHandles) {
socketHandles += start()
if (socketHandles.size > 2) {
socketHandles.removeAt(0).close()
}
}
}
private fun shutdown() {
startNewSocketJob?.cancel()
synchronized(socketHandles) {
socketHandles.forEach { it.close() }
socketHandles.clear()
}
}
private fun start(): Closeable {
SignalStore.registration.restoreMethodToken = null
store.update { it.copy(qrState = QrState.Loading) }
store.update {
if (it.qrState !is QrState.Loaded) {
it.copy(qrState = QrState.Loading)
} else {
it
}
}
return ProvisioningSocket.start(
identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair(),
configuration = AppDependencies.signalServiceNetworkAccess.getConfiguration(),
handler = CoroutineExceptionHandler { _, _ -> store.update { it.copy(qrState = QrState.Failed) } }
handler = { id, t ->
store.update {
if (it.currentSocketId == null || it.currentSocketId == id) {
Log.w(TAG, "Current socket [$id] has failed, stopping automatic connects", t)
shutdown()
it.copy(currentSocketId = null, qrState = QrState.Failed)
} else {
Log.i(TAG, "Old socket [$id] failed, ignoring")
it
}
}
}
) { socket ->
val url = socket.getProvisioningUrl()
store.update { it.copy(qrState = QrState.Loaded(qrData = QrCodeData.forData(data = url, supportIconOverlay = false))) }
store.update {
Log.d(TAG, "Updating QR code with data from [${socket.id}]")
it.copy(
currentSocketId = socket.id,
qrState = QrState.Loaded(
qrData = QrCodeData.forData(
data = url,
supportIconOverlay = false
)
)
)
}
val result = socket.getRegistrationProvisioningMessage()
@@ -94,8 +170,15 @@ class RestoreViaQrViewModel : ViewModel() {
SignalStore.backup.usedBackupMediaSpace = result.message.backupSizeBytes
}
store.update { it.copy(isRegistering = true, provisioningMessage = result.message, qrState = QrState.Scanned) }
shutdown()
} else {
store.update { it.copy(showProvisioningError = true, qrState = QrState.Scanned) }
store.update {
if (it.currentSocketId == socket.id) {
it.copy(showProvisioningError = true, qrState = QrState.Scanned)
} else {
it
}
}
}
}
}
@@ -105,7 +188,9 @@ class RestoreViaQrViewModel : ViewModel() {
val qrState: QrState = QrState.Loading,
val provisioningMessage: RegistrationProvisionMessage? = null,
val showProvisioningError: Boolean = false,
val showRegistrationError: Boolean = false
val showRegistrationError: Boolean = false,
val registerAccountResult: RegisterAccountResult? = null,
val currentSocketId: Int? = null
)
sealed interface QrState {
@@ -78,7 +78,10 @@ abstract class BaseStoryRecipientSelectionFragment : Fragment(R.layout.stories_b
viewModel.state.observe(viewLifecycleOwner) {
if (it.distributionListId == null || it.privateStory != null) {
getAttachedContactSelectionFragment().markSelected(it.selection.toSet())
if (it.isStartingSelection) {
getAttachedContactSelectionFragment().markSelected(it.selection.toSet())
viewModel.onStartingSelectionAdded()
}
presentTitle(toolbar, it.selection.size)
}
}
@@ -7,5 +7,6 @@ import org.thoughtcrime.securesms.recipients.RecipientId
data class BaseStoryRecipientSelectionState(
val distributionListId: DistributionListId?,
val privateStory: DistributionListRecord? = null,
val selection: Set<RecipientId> = emptySet()
val selection: Set<RecipientId> = emptySet(),
val isStartingSelection: Boolean = false
)
@@ -29,7 +29,7 @@ class BaseStoryRecipientSelectionViewModel(
disposable += repository.getRecord(distributionListId)
.subscribe { record ->
val startingSelection = if (record.privacyMode == DistributionListPrivacyMode.ALL_EXCEPT) record.rawMembers else record.members
store.update { it.copy(privateStory = record, selection = it.selection + startingSelection) }
store.update { it.copy(privateStory = record, selection = it.selection + startingSelection, isStartingSelection = true) }
}
}
}
@@ -61,6 +61,10 @@ class BaseStoryRecipientSelectionViewModel(
}
}
fun onStartingSelectionAdded() {
store.update { it.copy(isStartingSelection = false) }
}
sealed class Action {
data class GoToNextScreen(val recipients: Set<RecipientId>) : Action()
object ExitFlow : Action()
@@ -57,8 +57,8 @@ object DeleteDialog {
}.executeOnExecutor(SignalExecutors.BOUNDED)
}
if (MessageConstraintsUtil.isValidRemoteDeleteSend(messageRecords, System.currentTimeMillis())) {
builder.setNeutralButton(if (isNoteToSelfDelete) R.string.ConversationFragment_delete_everywhere else R.string.ConversationFragment_delete_for_everyone) { _, _ -> handleDeleteForEveryone(context, messageRecords, emitter) }
if (MessageConstraintsUtil.isValidRemoteDeleteSend(messageRecords, System.currentTimeMillis()) && !isNoteToSelfDelete) {
builder.setNeutralButton(R.string.ConversationFragment_delete_for_everyone) { _, _ -> handleDeleteForEveryone(context, messageRecords, emitter) }
}
}
@@ -29,6 +29,7 @@ object Environment {
@JvmStatic
@get:JvmName("getStripeConfiguration")
val STRIPE_CONFIGURATION = StripeApi.Configuration(
baseUrl = BuildConfig.STRIPE_BASE_URL,
publishableKey = BuildConfig.STRIPE_PUBLISHABLE_KEY
)
}
@@ -992,13 +992,6 @@ object RemoteConfig {
hotSwappable = true
)
/** Make CDSI lookups via libsignal-net instead of native websocket. */
val useLibsignalNetForCdsiLookup: Boolean by remoteBoolean(
key = "android.cds.libsignal.4",
defaultValue = false,
hotSwappable = true
)
/** The lifespan of a linked device (i.e. the time it can be inactive for before it expires), in milliseconds. */
@JvmStatic
val linkedDeviceLifespan: Long by remoteValue(
@@ -1129,7 +1122,7 @@ object RemoteConfig {
/** Whether or not this device supports syncing data to newly-linked device. */
@JvmStatic
val linkAndSync: Boolean by remoteBoolean(
key = "android.linkAndSync.2",
key = "android.linkAndSync.3",
defaultValue = false,
hotSwappable = true
)
+54 -3
View File
@@ -18,9 +18,13 @@ message BackupInfo {
// e.g. a Recipient must come before any Chat referencing it.
// 3. All ChatItems must appear in global Chat rendering order.
// (The order in which they were received by the client.)
// 4. ChatFolders must appear in render order (e.g., left to right for
// LTR locales), but can appear anywhere relative to other frames respecting
// rule 2 (after Recipients and Chats).
//
// Recipients, Chats, StickerPacks, AdHocCalls, and NotificationProfiles
// can be in any order. (But must respect rule 2.)
//
// Recipients, Chats, StickerPacks, and AdHocCalls can be in any order.
// (But must respect rule 2.)
// For example, Chats may all be together at the beginning,
// or may each immediately precede its first ChatItem.
message Frame {
@@ -31,6 +35,8 @@ message Frame {
ChatItem chatItem = 4;
StickerPack stickerPack = 5;
AdHocCall adHocCall = 6;
NotificationProfile notificationProfile = 7;
ChatFolder chatFolder = 8;
}
}
@@ -1182,4 +1188,49 @@ message ChatStyle {
}
bool dimWallpaperInDarkMode = 7;
}
}
message NotificationProfile {
enum DayOfWeek {
UNKNOWN = 0;
MONDAY = 1;
TUESDAY = 2;
WEDNESDAY = 3;
THURSDAY = 4;
FRIDAY = 5;
SATURDAY = 6;
SUNDAY = 7;
}
string name = 1;
optional string emoji = 2;
fixed32 color = 3; // 0xAARRGGBB
uint64 createdAtMs = 4;
bool allowAllCalls = 5;
bool allowAllMentions = 6;
repeated uint64 allowedMembers = 7; // generated recipient id for allowed groups and contacts
bool scheduleEnabled = 8;
uint32 scheduleStartTime = 9; // 24-hour clock int, 0000-2359 (e.g., 15, 900, 1130, 2345)
uint32 scheduleEndTime = 10; // 24-hour clock int, 0000-2359 (e.g., 15, 900, 1130, 2345)
repeated DayOfWeek scheduleDaysEnabled = 11;
}
message ChatFolder {
// Represents the default "All chats" folder record vs all other custom folders
enum FolderType {
UNKNOWN = 0;
ALL = 1;
CUSTOM = 2;
}
string name = 1;
bool showOnlyUnread = 2;
bool showMutedChats = 3;
// Folder includes all 1:1 chats, unless excluded
bool includeAllIndividualChats = 4;
// Folder includes all group chats, unless excluded
bool includeAllGroupChats = 5;
FolderType folderType = 6;
repeated uint64 includedRecipientIds = 7; // generated recipient id of groups, contacts, and/or note to self
repeated uint64 excludedRecipientIds = 8; // generated recipient id of groups, contacts, and/or note to self
}
+2
View File
@@ -36,6 +36,8 @@ message ArchiveUploadProgressState {
Call = 4;
Sticker = 5;
Message = 6;
NotificationProfile = 7;
ChatFolder = 8;
}
State state = 1;
@@ -22,7 +22,8 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/section_header_action"
style="@style/Signal.Widget.Button.Large.Tonal"
style="@style/Signal.Widget.Button.Small.Secondary"
android:textColor="@color/signal_colorOnSurface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
@@ -31,6 +32,7 @@
android:visibility="gone"
app:icon="@drawable/ic_plus_12"
app:iconSize="12dp"
app:iconGravity="textStart"
app:materialThemeOverlay="@style/ThemeOverlay.Signal.Button.Surface2"
tools:text="New Story"
tools:visibility="visible" />
@@ -705,7 +705,9 @@
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />
app:popExitAnim="@anim/fragment_close_exit"
app:popUpTo="@id/app_settings"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_direct_to_chatFoldersFragment"
@@ -727,6 +729,16 @@
app:popUpTo="@id/app_settings"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_direct_to_backupsSettingsFragment"
app:destination="@id/backupsSettingsFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit"
app:popUpTo="@id/app_settings"
app:popUpToInclusive="true" />
<!-- endregion -->
<!-- Internal Settings -->
@@ -70,6 +70,15 @@
app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:popUpTo="@id/signup"/>
<action
android:id="@+id/go_to_accountLocked"
app:destination="@id/accountLockedFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:popUpTo="@+id/welcomeFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
+49 -22
View File
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">Aan die gang…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Besig om rugsteun te verifieer…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d tot dusver…</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">%1$s%% tot dusver…</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Wysig naam van toestel</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Toestelnaam</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Stoor</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Naam van toestel opgedateer</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">Kan nie naam van toestel verander nie. Probeer later weer.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Dra boodskapgeskiedenis oor</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Dra jou teksboodskappe en onlangse media na jou gekoppelde toestel oor</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">Moenie oordra nie</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">Geen ou boodskappe of media sal na jou gekoppelde toestel oorgedra word nie</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">Ontkoppel \"%1$s\"?</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">Jou laaste rugsteun is op %1$s om %2$s gemaak.</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">Jou laaste rugsteun is op %1$s om %2$s gemaak. Jou grootte van jou rugsteun is %3$s.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Herwin tans rugsteunbesonderhede…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Slaan herstel oor</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Stel my in kennis van Vermeldings</string>
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">Wil jy jou klets met %1$s voortsit en jou naam en foto met hulle deel?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Sluit by hierdie groep aan en deel jou naam en foto met die lede? Tot jy aanvaar, sal hulle nie weet dat jy hulle boodskappe gesien het nie.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">Sluit by hierdie groep aan en deel jou naam en foto met die lede? Tot jy aanvaar, sal jy nie hulle boodskappe sien nie.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Sluit by die groep aan? Totdat jy aanvaar, sal hulle nie weet dat jy hulle boodskappe gesien het nie.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Ontsper hierdie groep en deel jou naam en foto met die lede? Jy sal geen boodskappe ontvang nie tot jy hulle ontsper.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Tik hier om video aan te skakel</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">Om %1$s te skakel, het Signal toegang tot jou kamera nodig</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">Besig om te skakel…</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">Signal sal (%1$d) bel</item>
<item quantity="other">Signal sal (%1$d) bel</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal sal (%1$d) in kennis stel</item>
<item quantity="other">Signal sal (%1$d) in kennis stel</item>
@@ -2481,7 +2489,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Jy het te veel keer probeer om hierdie nommer te registreer. Probeer asseblief weer oor %1$s.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">Kan nie aan diens koppel nie. Kontroleer netwerkverbinding en probeer weer.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">Signal kon nie \'n SMS-kode stuur nie weens probleme met die SMS-verskaffer. Probeer weer oor \'n paar uur.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">Ons kon nie vir jou \'n verifiëringskode per SMS stuur nie. Probeer om eerder jou kode per stemoproep te ontvang.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Groep bygewerk</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Het die groep verlaat</string>
<string name="ThreadRecord_secure_session_reset">Veilige sessie herstel.</string>
<string name="ThreadRecord_draft">Konsep:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$d klets</item>
<item quantity="other">%1$d kletse</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Mees onlangse van: %1$s</string>
<string name="MessageNotifier_locked_message">Boodskap gesluit</string>
<string name="MessageNotifier_message_delivery_failed">Aflewering van boodskap het misluk.</string>
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Deel slegs met geselekteerde mense</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d mens</item>
<item quantity="other">%1$d mense</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Kies wie na jou storie kan kyk. Veranderinge sal nie stories wat jy reeds gestuur het, beïnvloed nie.</string>
@@ -7317,7 +7327,7 @@
<!-- PendingParticipantsBottomSheet -->
<!-- Title of the bottom sheet displaying requests to join the call link -->
<string name="PendingParticipantsBottomSheet__requests_to_join_this_call">Versoeke om by hierdie oproep aan te sluit</string>
<string name="PendingParticipantsBottomSheet__requests_to_join_this_call">Versoeke om by dié oproep aan te sluit</string>
<!-- Subtitle of the bottom sheet denoting the total number of people waiting -->
<plurals name="PendingParticipantsBottomSheet__d_people_waiting">
<item quantity="one">%1$d persoon wag</item>
@@ -7509,9 +7519,9 @@
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Rugsteun het misluk</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">\'n Fout het voorgekom en jou rugsteun kon nie voltooi word nie. Maak seker dat jy die jongste weergawe van Signal gebruik en probeer weer. Indien die probleem voortduur, kontak die steundiens.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Kyk vir opdatering</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Kanselleer aflaai</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">Besig om af te laai: %1$s van %2$s (%3$s%%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">Nie genoeg stoorruimte om jou rugsteun af te laai nie. Om voort te gaan, stel %1$s stoorruimte beskikbaar.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Slaan aflaai oor</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">Jou laaste rugsteun kon nie voltooi word nie. Maak seker dat jou telefoon aan wi-fi gekoppel is en tik op \"Rugsteun nou\" om weer te probeer.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">Jou laaste rugsteun kon nie voltooi word nie. Maak seker dat jy die jongste weergawe van Signal gebruik en probeer weer.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Vind meer uit</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">Betalingsgeskiedenis</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Rugsteunbesonderhede</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Rugsteungrootte</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Vind meer uit</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">Verwerk tans rugsteun</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">Jy het %1$s rugsteundata wat nie op hierdie toestel is nie. Jou rugsteun sal geskrap word wanneer jou intekening oor %2$d dag eindig.</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">Kon nie rugsteune afskakel en skrap nie</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">\'n Netwerkfout het voorgekom. Gaan asseblief jou internetverbinding na en probeer weer.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">Op ou toestel geskandeer</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Probeer weer</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Dra rekening oor</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">Reg so</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">Geen rugsteun om te herstel nie</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">Omdat jy van iPhone na Android oorgaan, is die enigste manier om jou boodskappe en media oor te dra deur Signal-rugsteun op jou ou toestel te aktiveer.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Maak Signal op jou ou toestel oop</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Tik op Instellings &gt; Rugsteun</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Aktiveer rugsteun en wag totdat jou rugsteun voltooi is</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Slaan herstel oor</string>
File diff suppressed because it is too large Load Diff
+49 -22
View File
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">Davam edir…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Ehtiyat nüsxə yoxlanılır…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">İndiyə qədər %1$d….</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">İndiyə qədər %1$s%%…</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Cihaz adını redaktə et</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Cihaz adı</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Saxla</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Cihaz adı yeniləndi</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">Cihaz adını dəyişmək mümkün olmadı. Daha sonra yenidən cəhd edin.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Mesaj tarixçəsini köçürün</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Mətn mesajlarınızı və son media fayllarınızı əlaqələndirilmiş cihazınıza köçürün</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">Köçürmə</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">Əlaqələndirilmiş cihazınıza heç bir köhnə mesaj və ya media faylları köçürülməyəcək</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">\"%1$s\" ilə əlaqə kəsilsin?</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">Son ehtiyat nüsxəniz çıxarılıb: %1$s tarixi saat %2$s</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">Son ehtiyat nüsxəniz %1$s tarixində saat %2$s radəsində çıxarıldı. Ehtiyat nüsxə ölçüsü %3$s həcmindədir.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Ehtiyat nüsxə təfərrüatları əldə edilir…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Bərpanı ötür</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Adım çəkiləndə bildir</string>
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">%1$s ilə çata davam edib adınızı və fotonuzu onunla paylaşmaq istəyirsiniz?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Bu qrupa qoşularaq adınızı və fotonuzu üzvlərlə paylaşırsınız? Qəbul edənə qədər onların mesajlarını gördüyünüzü bilməyəcəklər.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">Bu qrupa qoşularaq adınızı və fotonuzu üzvlərlə paylaşırsınız? Qəbul edənə qədər onların mesajlarını görməyəcəksiniz.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Bu qrupa qoşulursunuz? Qəbul edənə qədər onların mesajlarını gördüyünüzü bilməyəcəklər.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Bu qrupu blokdan çıxararaq adınızı və fotonuzu üzvlərlə paylaşırsınız? Blokdan çıxaranadək heç bir mesaj almayacaqsınız.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Videonuzu açmaq üçün bura toxunun</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">Signal-ın, %1$s əlaqəsinə zəng etmək üçün kameranıza müraciətinə ehtiyacı var</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">Zəng edilir…</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">Signal (%1$d) nəfərə zəng edəcək</item>
<item quantity="other">Signal (%1$d) nəfərə zəng edəcək</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal (%1$d) nəfərə bildiriş göndərəcək</item>
<item quantity="other">Signal (%1$d) nəfərə bildiriş göndərəcək</item>
@@ -2481,7 +2489,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Bu nömrəni qeydiyyatdan keçirmək üçün həddindən çox cəhd etdiniz. %1$s dəqiqə sonra yenidən cəhd edin.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">Xidmətlə bağlantı qurula bilmir. Zəhmət olmasa şəbəkə bağlantınızı yoxlayıb yenidən sınayın.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">SMS provayderindən irəli gələn problemlər səbəbilə Signal SMS kodu göndərə bilmədi. Bir neçə saat sonra təkrar cəhd edin.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">Təsdiq kodunu sizə SMS vasitəsilə göndərə bilmirik. Bunun əvəzinə kodunuzu audio zənglə almağa çalışın.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Qrup yeniləndi</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Qrupu tərk etdi</string>
<string name="ThreadRecord_secure_session_reset">Güvənli seans sıfırlandı.</string>
<string name="ThreadRecord_draft">Qaralama:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$d çat</item>
<item quantity="other">%1$d çat</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Ən son mesaj: %1$s</string>
<string name="MessageNotifier_locked_message">Kilidli mesaj</string>
<string name="MessageNotifier_message_delivery_failed">Mesajın çatdırılması uğursuz oldu.</string>
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Yalnız seçilmiş insanlarla paylaşın</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d nəfər</item>
<item quantity="other">%1$d nəfər</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Hekayənizə kimin baxa bildiyini seçin. Dəyişikliklər artıq göndərdiyiniz hekayələrə tətbiq olunmayacaq.</string>
@@ -7317,7 +7327,7 @@
<!-- PendingParticipantsBottomSheet -->
<!-- Title of the bottom sheet displaying requests to join the call link -->
<string name="PendingParticipantsBottomSheet__requests_to_join_this_call">Bu zəngə qoşulma sorğuları</string>
<string name="PendingParticipantsBottomSheet__requests_to_join_this_call">Bu zəngə qoşulmaq üçün sorğu göndərin</string>
<!-- Subtitle of the bottom sheet denoting the total number of people waiting -->
<plurals name="PendingParticipantsBottomSheet__d_people_waiting">
<item quantity="one">%1$d nəfər gözləyir</item>
@@ -7509,9 +7519,9 @@
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Nüsxələnmədi</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">Xəta baş verdiyindən ehtiyat nüsxənin çıxarılması tamamlanmadı. Signal-ın ən son versiyasını istifadə etdiyinizdən əmin olun və yenidən cəhd edin. Problem davam edirsə, dəstək xidməti ilə əlaqə saxlayın.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Yeniləməni yoxla</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Endirməni ləğv et</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">Endirilir: %1$s / %2$s (%3$s%%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">Ehtiyat nüsxənizi endirmək üçün kifayət qədər boş yer yoxdur. Davam etmək üçün %1$s yer boşaldın.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Endirməni ötür</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">Son ehtiyat nüsxənizi tamamlamaq mümkün olmadı. Telefonunuzun Wi-Fi-a bağlı olduğunu yoxlayıb, \"İndi ehtiyat nüsxə çıxar\" seçiminə toxunaraq yenidən cəhd edin.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">Son ehtiyat nüsxənizi tamamlamaq mümkün olmadı. Signal-ın ən son versiyasını istifadə etdiyinizdən əmin olun və yenidən cəhd edin.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Daha ətraflı</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">Ödəniş tarixçəsi</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Ehtiyat nüsxənin təfərrüatları</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Ehtiyat nüsxənin həcmi</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Daha ətraflı</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">Ehtiyat nüsxəsi emal olunur</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">Bu cihazda olmayan %1$s ehtiyat nüsxə verilənləriniz var. Abunəliyiniz %2$d gün içində bitdikdən sonra ehtiyat nüsxəniz silinəcək.</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">Söndürmək və ehtiyat nüsxələri silmək mümkün olmadı</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">Bir şəbəkə xətası baş verdi. İnternet bağlantınızı yoxlayıb, yenidən cəhd edin.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">Köhnə cihazla skan edildi</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Yenidən sına</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Hesabı köçür</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">Oldu</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">Bərpa ediləcək ehtiyat nüsxə yoxdur</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">iPhone-dan Android-ə keçdiyiniz üçün mesaj və media fayllarınızı köçürmənin yeganə yolu köhnə cihazınızda Signal ehtiyat nüsxələrini aktivləşdirməkdir.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Signal-ı köhnə cihazınızda açın</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Parametrlər &gt; Ehtiyat nüsxələr seçiminə toxunun</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Ehtiyat nüsxələri aktivləşdirin və ehtiyat nüsxələr tamamlanana qədər gözləyin</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Bərpanı ötür</string>
+50 -23
View File
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">Архивира се…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Проверка на архив…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d архивирани до тук…</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">%1$s%% дотук…</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Редактиране на името на устройството</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Име на устройството</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Запазване</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Името на устройството е актуализирано</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">Неуспешна промяна на името на устройството. Опитайте пак по-късно.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Прехвърляне на историята на съобщенията</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Прехвърляне на текстовите съобщения и скорошните мултимедийни файлове на свързаното ви устройство</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">Не прехвърляй</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">На вашето свързано устройство няма да бъдат прехвърлени стари съобщения или мултимедийни файлове</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">Прекъсване на връзката с „%1$s“?</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">Последното ви резервно копиране е направено на %1$s в %2$s.</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">Последното ви резервно копиране е направено на %1$s в %2$s. Размерът на резервното ви копие е %3$s.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Извличане на данни за резервно копие…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Пропуснете възстановяването</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Уведоми ме за споменавания</string>
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">Ще продължите ли чата си с/ъс %1$s и ще споделите ли името и снимката си с потребителя?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Присъединете се към тази група и споделете вашето име и снимка с нейните членове? Те няма да разберат, че сте видели съобщенията им, докато не приемете.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">Присъединяване към тази група и споделяне на името и снимката ви с нейните членове? Няма да виждате техните съобщения, преди да приемете.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Присъединете се към тази група? Те няма да разберат, че сте виждали съобщенията им, докато не приемете.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Отблокирайте тази група и споделете името и снимката си с членовете? Няма да получавате никакви съобщения, преди да ги отблокирате.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Докоснете тук, за да включите вашето видео</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">За да се обадите на %1$s, Signal се нуждае от достъп до вашата камера</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">Обаждане</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">Signal ще позвъни (%1$d)</item>
<item quantity="other">Signal ще позвъни (%1$d)</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal ще извести (%1$d)</item>
<item quantity="other">Signal ще извести (%1$d)</item>
@@ -2481,7 +2489,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Направихте прекалено много опити за регистриране на този номер. Моля, опитайте отново след %1$s.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">Неуспешно свързване с услугата. Моля, проверете мрежовата връзка и опитайте отново.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">Signal не можа да изпрати SMS код поради проблеми с доставчика на SMS услугата. Опитайте отново след няколко часа.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">Не можахме да ви изпратим код за потвърждаване чрез SMS. Опитайте вместо това да получите кода чрез гласово повикване.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Групата е обновена</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Напусна групата</string>
<string name="ThreadRecord_secure_session_reset">Започване на нова сигурна сесия.</string>
<string name="ThreadRecord_draft">Чернова:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$d чат</item>
<item quantity="other">%1$d чата</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Най-скорошно от: %1$s</string>
<string name="MessageNotifier_locked_message">Заключено съобщение</string>
<string name="MessageNotifier_message_delivery_failed">Изпращането на съобщението неуспешно.</string>
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Споделяне само с избраните хора</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d човек</item>
<item quantity="other">%1$d души</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Изберете кой може да разглежда историята ви. Промените няма да засегнат историите, които вече сте изпратили.</string>
@@ -7317,7 +7327,7 @@
<!-- PendingParticipantsBottomSheet -->
<!-- Title of the bottom sheet displaying requests to join the call link -->
<string name="PendingParticipantsBottomSheet__requests_to_join_this_call">Заявки за присъединяване към повикването</string>
<string name="PendingParticipantsBottomSheet__requests_to_join_this_call">Заявки за присъединяване към разговора</string>
<!-- Subtitle of the bottom sheet denoting the total number of people waiting -->
<plurals name="PendingParticipantsBottomSheet__d_people_waiting">
<item quantity="one">%1$d човек в изчакване</item>
@@ -7507,11 +7517,11 @@
<!-- Dialog text for skipping media restore -->
<string name="BackupAlertBottomSheet__if_you_skip_restore_the">Ако пропуснете възстановяването, останалите прикачени файлове и мултимедия в резервното ви копие ще бъдат изтеглени по-нататък, когато има налично пространство за съхранение.</string>
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Архивирането не бе успешно</string>
<string name="BackupAlertBottomSheet__backup_failed">Архивирането не беше успешно</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">Възникна грешка и резервното копиране не можа да бъде завършено. Уверете се, че използвате най-новата версия на Signal, и опитайте отново. Ако проблемът продължава, свържете се с отдела за поддръжка.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Проверка за актуализация</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Отмяна на изтеглянето</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">Изтегляне: %1$s от %2$s (%3$s%%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">Недостатъчно място за изтегляне на вашето резервно копие. За да продължите, освободете %1$s пространство.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Пропускане на изтеглянето</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">Последното ви резервно копиране не можа да бъде завършено. Уверете се, че телефонът ви е свързан към Wi-Fi и докоснете „Резервно копиране сега“, за да опитате отново.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">Последното ви резервно копиране не можа да бъде завършено. Уверете се, че използвате най-новата версия на Signal, и опитайте отново.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Научете повече</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">История на плащанията</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Подробности за резервно копие</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Размер на резервно копие</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Научете повече</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">Обработване на резервното копие</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">Имате %1$s резервно копие на данни, които не са на това устройство. Вашето резервно копие ще бъде изтрито, когато абонаментът ви приключи след %2$d ден.</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">Неуспешно изключване и изтриване на резервните копия</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">Възникна грешка с мрежата. Моля, проверете интернет връзката си и опитайте отново.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">Сканиран на старото устройство</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Опитайте отново</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Прехвърляне на акаунт</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">Добре</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">Няма резервно копие за възстановяване</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">Тъй като преминавате от iPhone към Android, единственият начин да прехвърлите съобщенията и мултимедийните си файлове е да активирате функцията „Резервни копия на Signal“ на старото си устройство.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Отворете Signal на старото ви устройство</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Докоснете „Настройки &gt; Резервни копия“</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Активирайте резервното копиране и изчакайте, докато то завърши</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Пропуснете възстановяването</string>
+48 -21
View File
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">চলমান…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">ব্যাকআপ যাচাই করা হচ্ছে…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d এখন অব্দি…</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">এখন পর্যন্ত %1$s %%…</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">ডিভাইসের নাম এডিট করুন</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">ডিভাইস এর নাম</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">সেভ করুন</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">ডিভাইসের নাম আপডেট করা হয়েছে</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">ডিভাইসের নাম পরিবর্তন করতে সক্ষম নয়। পরে আবার চেষ্টা করুন।</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">মেসেজের ইতিহাস ট্রান্সফার করুন</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">আপনার লিংককৃত ডিভাইসে আপনার টেক্সট মেসেজ ও সাম্প্রতিক মিডিয়া ট্রান্সফার করুন</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">ট্রান্সফার করবেন না</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">আপনার লিংককৃত ডিভাইসে কোনো পুরানো মেসেজ বা মিডিয়া ট্রান্সফার করা হবে না</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">\"%1$s\" বিযুক্ত করবেন?</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">আপনার সর্বশেষ ব্যাকআপ করা হয়েছিল %1$s তারিখে %2$s-টার সময়।</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">আপনার সর্বশেষ ব্যাকআপ নেওয়া হয়েছিল %1$s তারিখে %2$s-টার সময়। আপনার ব্যাকআপের আকার হলো %3$s</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">ব্যাকআপের তথ্য আনা হচ্ছে…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">পুনরুদ্ধার এড়িয়ে যান</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">মেনশন করা হলে আমাকে অবহিত করুন</string>
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">%1$s-এর সাথে এই চ্যাট চালিয়ে যাবেন এবং তাদের সাথে আপনার নাম ও ছবি শেয়ার করবেন?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">আপনি কি এই গ্রুপে যোগ দিয়ে এর সদস্যদের সাথে আপনার নাম ও ছবি শেয়ার করবেন? আপনি গ্রহণ না করা পর্যন্ত তারা জানবেন না যে আপনি তাদের মেসেজ দেখেছেন।</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">এই গ্রুপে যোগ দিন এবং এর গ্রুপের সদস্যদের সাথে আপনার নাম এবং ছবি শেয়ার করুন? সহমত প্রকাশ করার আগ পর্যন্ত আপনি তাদের ম্যাসেজ দেখতে পাবেন না।</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">এই গ্রুপে যোগ দেবেন? আপনি স্বীকার না করা পর্যন্ত তারা জানবেন না যে আপনি তাদের মেসেজ দেখেছেন।</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">এই গ্রুপটিকে আনব্লক করে এর সদস্যদের সাথে আপনার নাম ও ছবি শেয়ার করবেন? আপনি তাদের আনব্লক না করা পর্যন্ত কোন ম্যাসেজ পাবেন না।</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">আপনার ভিডিও চালু করতে এখানে আলতো চাপুন</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">%1$s কে ফোন করতে Signal কে আপনার ক্যামেরা ব্যাবহার করতে হবে।</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">কল করা হচ্ছে…</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">Signal (%1$d)-কে রিং করবে</item>
<item quantity="other">Signal (%1$d)-কে রিং করবে</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal (%1$d)-কে জানাবে</item>
<item quantity="other">Signal (%1$d)-কে জানাবে</item>
@@ -2481,7 +2489,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">আপনি এই নম্বরটি রেজিস্ট্রেশন করার জন্য ইতোমধ্যে অনেকবার চেষ্টা করে ফেলেছেন। অনুগ্রহ করে %1$s মিনিটের মধ্যে আবার চেষ্টা করুন।</string>
<string name="RegistrationActivity_unable_to_connect_to_service">পরিষেবাতে সংযোগ করতে অক্ষম। নেটওয়ার্ক সংযোগ পরীক্ষা করুন এবং আবার চেষ্টা করুন।</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">SMS প্রোভাইডারের সাথে সৃষ্ট সমস্যার কারণে Signal কোনো SMS কোড পাঠাতে পারেনি। কয়েক ঘন্টা পরে আবার চেষ্টা করুন।</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">আমরা আপনাকে SMS-এর মাধ্যমে যাচাইকরণ কোড পাঠাতে পারিনি। এর পরিবর্তে আপনি ভয়েস কলের মাধ্যমে আপনার কোড পাওয়ার চেষ্টা করুন।</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">গ্রুপ অাপডেট হয়েছে</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">গ্রুপ ত্যাগ করেছে</string>
<string name="ThreadRecord_secure_session_reset">নিরাপদ সেশন পুনঃস্থাপন করা</string>
<string name="ThreadRecord_draft">ড্রাফ্ট:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$dটি চ্যাট</item>
<item quantity="other">%1$dটি চ্যাট</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">%1$sথেকে অতি সাম্প্রতিক :</string>
<string name="MessageNotifier_locked_message">লক্ করা বার্তা</string>
<string name="MessageNotifier_message_delivery_failed">বার্তা পৌছে দিতে ব্যর্থ।</string>
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">শুধু নির্বাচিত ব্যক্তির সাথে শেয়ার করুন</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d মানুষ</item>
<item quantity="other">%1$d মানুষ</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">আপনার স্টোরি কে দেখতে পাবেন তা নির্ধারণ করুন৷ পরিবর্তনগুলো আপনার ইতোমধ্যে পাঠানো স্টোরিগুলোকে প্রভাবিত করবে না।</string>
@@ -7509,9 +7519,9 @@
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">ব্যাকআপ ব্যর্থ হয়েছে</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">একটি ত্রুটি দেখা দিয়েছে এবং আপনার ব্যাকআপ সম্পন্ন করা যায়নি। নিশ্চিত করুন যে আপনি Signal-এর সর্বশেষ সংস্করণে আছেন এবং আবার চেষ্টা করুন। এই সমস্যা অব্যাহত থাকলে, সহায়তার সাথে যোগাযোগ করুন।</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">আপডেট পেতে দেখুন</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">ডাউনলোড বাতিল করুন</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">ডাউনলোড হচ্ছে: %2$s-এর মধ্যে %1$s (%3$s%%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">আপনার ব্যাকআপ ডাউনলোড করার জন্য পর্যাপ্ত জায়গা নেই। এগিয়ে যেতে %1$s জায়গা খালি করুন।</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">ডাউনলোড এড়িয়ে যান</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">আপনার শেষ ব্যাকআপ সম্পন্ন করা যায়নি। আপনার ফোন Wi-Fi-এর সাথে সংযুক্ত আছে কিনা দেখুন এবং আবার চেষ্টা করতে \"এখনই ব্যাক আপ নিন\"-এ ট্যাপ করুন।</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">আপনার শেষ ব্যাকআপ সম্পন্ন করা যায়নি। নিশ্চিত করুন যে আপনি Signal-এর সর্বশেষ সংস্করণে আছেন এবং আবার চেষ্টা করুন।</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">আরো জানুন</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">পেমেন্টের ইতিহাস</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">ব্যাকআপের বিস্তারিত</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">ব্যাকআপের আকার</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">আরো জানুন</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">ব্যাকআপ প্রক্রিয়া করা হচ্ছে</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">আপনার কাছে %1$s-টি ব্যাকআপ ডেটা আছে যা এই ডিভাইসে নেই। আপনার সাবস্ক্রিপশন %2$d দিনের মধ্যে শেষ হলে আপনার ব্যাকআপ মুছে ফেলা হবে।</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">ব্যাকআপ বন্ধ করা এবং মুছে ফেলা যায়নি</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">নেটওয়ার্কে কোনো ত্রুটি ঘটেছে। আপনার ইন্টারনেট সংযোগ ঠিক আছে কি না দেখুন এবং আবার চেষ্টা করুন।</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">পুরানো ডিভাইসে স্ক্যান করা হয়েছে</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">পুনরায় চেষ্টা করুন</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">অ্যাকাউন্ট স্থানান্তর করুন</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">ঠিক আছে</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">পুনর্বহাল করার জন্য কোনো ব্যাকআপ নেই</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">যেহেতু আপনি iPhone থেকে Android-এ চলে যাচ্ছেন, তাই আপনার মেসেজ ও মিডিয়া ট্রান্সফার করার একমাত্র উপায় হলো আপনার পুরানো ডিভাইসে Signal ব্যাকআপ সচল করা।</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">আপনার পুরানো ডিভাইসে Signal খুলুন</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">সেটিংস &gt; ব্যাকআপ ট্যাপ করুন</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">ব্যাকআপ সক্রিয় করুন এবং ব্যাকআপ সম্পন্ন না হওয়া পর্যন্ত অপেক্ষা করুন</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">পুনরুদ্ধার এড়িয়ে যান</string>
+52 -23
View File
@@ -929,6 +929,7 @@
<string name="BackupsPreferenceFragment__in_progress">U toku…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Provjera sigurnosne kopije…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d do sada…</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">%1$s%% do sada…</string>
@@ -1056,15 +1057,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Uredite naziv uređaja</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Naziv uređaja</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Pohrani</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Naziv uređaja je ažuriran</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">Nije moguće promijeniti naziv uređaja. Pokušajte ponovo kasnije.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1092,11 +1093,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Prijenos historije poruka</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Prenesite svoje tekstualne poruke i nedavne medije na povezani uređaj</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">Ne prenosi</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">Stare poruke ili mediji neće se prenijeti na vaš povezani uređaj</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">Prekini vezu \"%1$s\"?</string>
@@ -1452,11 +1453,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">Vaša zadnja sigurnosna kopija je napravljena %1$s u %2$s.</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">Vaša zadnja sigurnosna kopija je napravljena %1$s u %2$s. Veličina vaše rezervne kopije je %3$s.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Preuzimanje detalja sigurnosne kopije…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Preskoči obnovu</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Obavijesti me kad me neko spomene</string>
@@ -2105,6 +2110,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">Nastavi chat sa %1$s i podijeli svoje ime i fotografiju s tom osobom?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Pristupiti ovoj grupi i dopustiti članovima da vide Vaše ime i sliku? Oni neće znati da ste vidjeli njihove poruke dok ne prihvatite.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">Pristupiti ovoj grupi i dopustiti članovima da vide Vaše ime i sliku? Nećete vidjeti njihove poruke dok ne prihvatite.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Pristupiti ovoj grupi? Članovi neće znati da ste vidjeli njihove poruke dok ne prihvatite.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Želite li odblokirati ovu grupu i dopustiti njenim članovima da vide vaše ime i sliku? Nećete primati poruke dok je ne odblokirate.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2302,6 +2308,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Pritisnite ovdje da uključite video</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">Da biste nazvali %1$s, Signalu je potreban pristup Vašoj kameri</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">Pozivam…</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2584,6 +2591,7 @@
<item quantity="many">Signal će pozvati (%1$d)</item>
<item quantity="other">Signal će pozvati (%1$d)</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal će obavijestiti (%1$d)</item>
<item quantity="few">Signal će obavijestiti (%1$d)</item>
@@ -2649,7 +2657,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Previše puta ste pokušali registrirati ovaj broj. Pokušajte ponovo za %1$s.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">Povezivanje nije uspjelo. Molimo provjerite internet-konekciju i pokušajte ponovo.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">Signal nije mogao poslati SMS kod zbog problema sa SMS pružateljem usluge. Pokušajte ponovo za nekoliko sati.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">Nismo mogli da vam pošaljemo verifikacioni kôd putem SMS-a. Umjesto toga pokušajte primiti kôd putem glasovnog poziva.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2815,6 +2823,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Grupa ažurirana</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Napustio/la grupu</string>
<string name="ThreadRecord_secure_session_reset">Zaštićena sesija pokrenuta je iznova.</string>
<string name="ThreadRecord_draft">U obradi:</string>
@@ -3025,6 +3034,7 @@
<item quantity="many">%1$d chatova</item>
<item quantity="other">%1$d chatova</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Zadnje od: %1$s</string>
<string name="MessageNotifier_locked_message">Zaključana poruka</string>
<string name="MessageNotifier_message_delivery_failed">Neuspjela isporuka poruke.</string>
@@ -6564,10 +6574,10 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Dijeljenje samo s odabranim osobama</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d osoba</item>
<item quantity="few">%1$d osobe</item>
<item quantity="many">%1$d osoba</item>
<item quantity="other">%1$d osoba</item>
<item quantity="one">%1$d viewer</item>
<item quantity="few">%1$d viewers</item>
<item quantity="many">%1$d viewers</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Odaberite ko može vidjeti vašu priču. Promjene neće uticati na priče koje ste već poslali.</string>
@@ -7831,9 +7841,9 @@
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Neuspjelo kreiranje rezervne kopije</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">Došlo je do greške i vaša sigurnosna kopija nije mogla biti dovršena. Provjerite koristite li najnoviju verziju Signala i pokušajte ponovo. Ako se ovaj problem ne riješi, kontaktirajte podršku.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Provjerite ima li ažuriranja</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7860,15 +7870,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Otkaži preuzimanje</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">Preuzimanje: %1$s od %2$s (%3$s%%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">Nema dovoljno prostora za preuzimanje vaše sigurnosne kopije. Da nastavite oslobodite %1$s prostora.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Preskoči preuzimanje</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">Vaša posljednja sigurnosna kopija nije mogla biti dovršena. Provjerite je li vaš telefon povezan na Wi-Fi i dodirnite \"Kreiraj sigurnosnu kopiju sada\" da pokušate ponovo.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">Vaša posljednja sigurnosna kopija nije mogla biti dovršena. Provjerite koristite li najnoviju verziju Signala i pokušajte ponovo.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Saznaj više</string>
@@ -7971,6 +7987,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">Historija plaćanja</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Detalji sigurnosne kopije</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Veličina rezervne kopije</string>
<!-- Row label for backup frequency -->
@@ -8079,7 +8097,16 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Saznaj više</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">Obrada sigurnosne kopije</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="few">Processing %1$d of %2$d (%3$d%%) messages</item>
<item quantity="many">Processing %1$d of %2$d (%3$d%%) messages</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">Imate %1$s podatak sigurnosne kopije koji nije na ovom uređaju. Vaša sigurnosna kopija će biti izbrisana kada vaša pretplata završi za %2$d dan.</item>
@@ -8104,8 +8131,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">Nije moguće isključiti i izbrisati sigurnosne kopije</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">Desila se greška mreže. Provjerite internetsku vezu i pokušajte ponovo.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -8295,6 +8322,8 @@
<string name="RestoreViaQr_qr_code_scanned">Skenirano na starom uređaju</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Pokušaj</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Prenesi račun</string>
@@ -8325,15 +8354,15 @@
<string name="RestoreCompleteBottomSheet_button">U redu</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">Nema rezervne kopije za vraćanje</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">Budući da prelazite s iPhone-a na Android, jedini način za prijenos vaših poruka i medija je omogućavanje sigurnosne kopije signala na vašem starom uređaju.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Otvorite Signal na svom starom uređaju</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Dodirnite Postavke &gt; Sigurnosne kopije</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Omogućite sigurnosne kopije i pričekajte dok se sigurnosna kopija ne završi</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Preskoči obnovu</string>
+49 -22
View File
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">En curs…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Verificant la còpia de seguretat…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d fins ara…</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">%1$s%% fins ara…</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Editar el nom del dispositiu</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Nom del dispositiu</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Desar</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Nom del dispositiu actualitzat</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">No es pot canviar el nom del dispositiu. Torna-ho a provar més tard.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Transferir l\'historial de missatges</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfereix els teus missatges de text i arxius recents al teu dispositiu vinculat</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">No transferir</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No es transferiran missatges ni arxius antics al teu dispositiu vinculat</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">Voleu desenllaçar «%1$s»?</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">La teva darrera còpia de seguretat es va fer el dia %1$s a les %2$s.</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">La teva darrera còpia de seguretat es va fer el dia %1$s a les %2$s. Mida de la teva còpia de seguretat: %3$s.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Obtenint detalls de la còpia de seguretat…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Ometre la restauració</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Notifica\'m les mencions</string>
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">Vols continuar el xat amb %1$s i compartir-hi el nom i la fotografia?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Voleu afegir-vos a aquest grup i compartir el nom i la fotografia amb els seus membres? No sabran que heu vist els seus missatges fins que no ho accepteu.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">Voleu afegir-vos a aquest grup i compartir el nom i la fotografia amb els seus membres? No veureu els seus missatges fins que no ho accepteu.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Voleu afegir-vos a aquest grup? No sabran que n\'heu vist els missatges fins que no ho accepteu.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Vols desbloquejar aquest grup i compartir el nom i la fotografia amb els seus membres? No rebràs cap missatge fins que no el desbloquegis.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Toqueu aquí per activar el vídeo</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">Per trucar a %1$s, el Signal necessita accés a la càmera.</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Trucada del Signal: %1$s</string>
<string name="WebRtcCallActivity__calling">Es truca…</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">Signal trucarà a (%1$d)</item>
<item quantity="other">Signal trucarà a (%1$d)</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal notificarà a (%1$d)</item>
<item quantity="other">Signal notificarà a (%1$d)</item>
@@ -2481,7 +2489,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Has fet massa intents per registrar aquest número. Si us plau, torna-ho a provar en %1$s.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">No es pot connectar al servei. Si us plau, comproveu la connexió de xarxa i torneu-ho a provar.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">Signal no ha pogut enviar un codi SMS a causa d\'un problema amb el proveïdor d\'SMS. Torna-ho a provar d\'aquí a unes hores.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">No t\'hem pogut enviar un codi de verificació per SMS. Prova de rebre el teu codi mitjançant una trucada de veu.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">S\'ha actualitzat el grup</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Ha abandonat el grup</string>
<string name="ThreadRecord_secure_session_reset">Restabliment de la sessió segura.</string>
<string name="ThreadRecord_draft">Esborrany:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$d xat</item>
<item quantity="other">%1$d xats</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Més recent de %1$s</string>
<string name="MessageNotifier_locked_message">Missatge blocat</string>
<string name="MessageNotifier_message_delivery_failed">El missatge no s\'ha pogut entregar.</string>
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Compartir només amb les persones seleccionades</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d persona</item>
<item quantity="other">%1$d persones</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Tria qui pot veure la teva història. Aquests canvis no afectaran les històries que ja hagis pujat.</string>
@@ -7317,7 +7327,7 @@
<!-- PendingParticipantsBottomSheet -->
<!-- Title of the bottom sheet displaying requests to join the call link -->
<string name="PendingParticipantsBottomSheet__requests_to_join_this_call">Sol·licituds per unir-se a aquesta trucada</string>
<string name="PendingParticipantsBottomSheet__requests_to_join_this_call">Sol·licituds per unir-se a la trucada</string>
<!-- Subtitle of the bottom sheet denoting the total number of people waiting -->
<plurals name="PendingParticipantsBottomSheet__d_people_waiting">
<item quantity="one">%1$d persona esperant</item>
@@ -7509,9 +7519,9 @@
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Ha fallat la còpia de seguretat.</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">S\'ha produït un error i la còpia de seguretat no s\'ha pogut completar. Assegura\'t d\'estar utilitzant la darrera versió de Signal i torna-ho a provar. Si el problema persisteix, posa\'t en contacte amb el servei de suport.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Buscar actualitzacions</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Cancel·la la descàrrega</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">Descarregant: %1$s de %2$s (%3$s %%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">No hi ha prou espai per descarregar la còpia de seguretat. Per continuar, allibera %1$s d\'espai.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Ometre la descàrrega</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">La teva darrera còpia de seguretat no s\'ha pogut completar. Assegura\'t que el teu telèfon estigui connectat a una xarxa wifi i toca \"Fer una còpia de seguretat ara\" per tornar-ho a provar.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">La teva darrera còpia de seguretat no s\'ha pogut completar. Assegura\'t d\'estar utilitzant la darrera versió de Signal i torna-ho a provar.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Més informació</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">Historial de pagaments</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Detalls de la còpia de seguretat</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Mida de la còpia de seguretat</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Més informació</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">S\'està processant la còpia de seguretat</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">Hi ha %1$s de dades de la còpia de seguretat que no s\'han copiat en aquest dispositiu. La teva còpia de seguretat s\'eliminarà quan la teva subscripció finalitzi d\'aquí a %2$d dia.</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">No s\'ha pogut desactivar i eliminar les còpies de seguretat</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">S\'ha produït un error de xarxa. Comprova teva la connexió a Internet i torna-ho a provar.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">Escanejat en l\'antic dispositiu</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Torna a provar-ho</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Transfereix el compte</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">D\'acord</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">No hi ha cap còpia de seguretat per restaurar</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">Com que estàs passant d\'iPhone a Android, l\'única manera de transferir els teus missatges i arxius és habilitant les Còpies de seguretat de Signal al teu antic dispositiu.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Obre Signal al teu dispositiu antic</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Toca Ajustos &gt; Còpies de seguretat</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Activa les còpies de seguretat i espera fins que la teva còpia de seguretat estigui completa</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Ometre la restauració</string>
+54 -25
View File
@@ -929,6 +929,7 @@
<string name="BackupsPreferenceFragment__in_progress">Probíhá…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Ověřování zálohy…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d dosud…</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">Zatím %1$s%%…</string>
@@ -1056,15 +1057,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Upravit název zařízení</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Název zařízení</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Uložit</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Název zařízení aktualizován</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">Nelze změnit název zařízení. Zkuste to znovu později.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1092,11 +1093,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Přenést historii zpráv</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Přenesete textové zprávy a nedávná média do propojeného zařízení</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">Nepřenášet</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">Do propojeného zařízení nebudou přeneseny žádné staré zprávy ani média</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">Zrušit propojení „%1$s“?</string>
@@ -1452,11 +1453,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">Poslední záloha proběhla dne %1$s v %2$s.</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">Poslední záloha proběhla dne %1$s v %2$s. Velikost vaší zálohy je %3$s.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Načítají se údaje o zálohování…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Přeskočit obnovení</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Upozornit mne na zmínky</string>
@@ -2105,6 +2110,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">Pokračovat v chatu s uživatelem %1$s a sdílet s ním své jméno a fotografii?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Připojit se k této skupině a sdílet vaše jméno a fotografii s jejími členy? Nebudou vědět, že jste viděl jejich zprávy, dokud je nepřijmete.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">Přidat se k této skupině a sdílet své jméno a fotografii s jejími členy? Jejich zprávy uvidíte až po přijetí.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Chcete se připojit k této skupině? Dokud tak neučiníte, členové se nedozví, že jste viděli jejich zprávy.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Odblokovat tuto skupinu a sdílet své jméno a fotografii s jejími členy? Dokud ji neodblokujete, nebudete dostávat žádné zprávy.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2302,6 +2308,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Klepnutím sem zapnete své video</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">Signal potřebuje přístup k vašemu fotoaparátu pro volání %1$s</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">Volání…</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2584,6 +2591,7 @@
<item quantity="many">Signal bude volat tyto uživatele: (%1$d)</item>
<item quantity="other">Signal bude volat tyto uživatele: (%1$d)</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal upozorní tohoto uživatele: (%1$d)</item>
<item quantity="few">Signal upozorní tyto uživatele: (%1$d)</item>
@@ -2649,7 +2657,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Učinili jste příliš mnoho pokusů o registraci tohoto čísla. Zkuste to prosím znovu za %1$s.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">Nelze se připojit k službě. Prosím zkontrolujte připojení k internetu a poté to zkuste znovu.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">Aplikace Signal nemohla odeslat SMS kód, protože se vyskytl problém u poskytovatele SMS. Zkuste to znovu za několik hodin.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">Nepodařilo se vám zaslat ověřovací kód prostřednictvím SMS. Zkuste místo toho kód získat prostřednictvím hlasového hovoru.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2815,6 +2823,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Skupina upravena</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Opustil skupinu.</string>
<string name="ThreadRecord_secure_session_reset">Reset zabezpečené konverzace.</string>
<string name="ThreadRecord_draft">Koncept:</string>
@@ -3025,6 +3034,7 @@
<item quantity="many">%1$d chatů</item>
<item quantity="other">%1$d chatů</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Poslední od: %1$s</string>
<string name="MessageNotifier_locked_message">Zamčená zpráva</string>
<string name="MessageNotifier_message_delivery_failed">Selhalo doručení zprávy.</string>
@@ -6564,10 +6574,10 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Sdílet jen s vybranými lidmi</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d člověk</item>
<item quantity="few">%1$d lidé</item>
<item quantity="many">%1$d lidí</item>
<item quantity="other">%1$d lidí</item>
<item quantity="one">%1$d viewer</item>
<item quantity="few">%1$d viewers</item>
<item quantity="many">%1$d viewers</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Vyberte, kdo si může váš příběh zobrazit. Změny se nedotknou příběhů, které jste už sdíleli.</string>
@@ -7633,7 +7643,7 @@
<!-- PendingParticipantsBottomSheet -->
<!-- Title of the bottom sheet displaying requests to join the call link -->
<string name="PendingParticipantsBottomSheet__requests_to_join_this_call">Žádosti o připojení k tomuto hovoru</string>
<string name="PendingParticipantsBottomSheet__requests_to_join_this_call">Žádosti o připojení k tomuto hovoru</string>
<!-- Subtitle of the bottom sheet denoting the total number of people waiting -->
<plurals name="PendingParticipantsBottomSheet__d_people_waiting">
<item quantity="one">Čeká %1$d osoba</item>
@@ -7829,11 +7839,11 @@
<!-- Dialog text for skipping media restore -->
<string name="BackupAlertBottomSheet__if_you_skip_restore_the">Pokud obnovu přeskočíte, můžete zbývající média a přílohy v záloze stáhnout později, až bude k dispozici úložný prostor.</string>
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Zálohování selhalo</string>
<string name="BackupAlertBottomSheet__backup_failed">Zálohování se nezdařilo</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">Vyskytla se chyba a vaši zálohu se nepodařilo dokončit. Ujistěte se, že máte nejnovější verzi aplikace Signal a zkuste to znovu. Pokud problém přetrvává, kontaktujte podporu.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Zkontrolovat aktualizace</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7860,15 +7870,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Zrušit stahování</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">Stahování: %1$s z %2$s (%3$s %%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">Není dostatek místa pro stažení vaší zálohy. Chcete-li pokračovat, uvolněte %1$s místa.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Přeskočit stahování</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">Poslední zálohu se nepodařilo dokončit. Ujistěte se, že je telefon připojen k Wi-Fi, klepněte na „Zálohovat nyní“ a zkuste to znovu.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">Poslední zálohu se nepodařilo dokončit. Ujistěte se, že máte nejnovější verzi aplikace Signal a zkuste to znovu.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Zjistit více</string>
@@ -7971,6 +7987,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">Historie plateb</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Podrobnosti o zálohování</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Velikost zálohy</string>
<!-- Row label for backup frequency -->
@@ -8079,7 +8097,16 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Zjistit více</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">Probíhá zpracování zálohy</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="few">Processing %1$d of %2$d (%3$d%%) messages</item>
<item quantity="many">Processing %1$d of %2$d (%3$d%%) messages</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">Máte %1$s záložních dat, která nejsou uložena v tomto zařízení. Vaše záloha bude smazána, až vaše předplatné skončí za %2$d den.</item>
@@ -8104,8 +8131,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">Nepodařilo se vypnout a odstranit zálohy</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">Vyskytla se chyba sítě. Zkontrolujte prosím své internetové připojení a zkuste to znovu.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -8295,6 +8322,8 @@
<string name="RestoreViaQr_qr_code_scanned">Již naskenován na starém zařízení</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Zkusit znovu</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Přenést účet</string>
@@ -8325,15 +8354,15 @@
<string name="RestoreCompleteBottomSheet_button">V pořádku</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">Žádná záloha k obnovení</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">Protože přecházíte z iPhonu na Android, jediným způsobem, jak přenést zprávy a média, je povolit zálohování služby Signal ve starém zařízení.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Otevřete aplikaci Signal ve starém zařízení</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Klepněte na Nastavení &gt; Zálohování</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Povolte zálohování a počkejte, dokud nebude zálohování dokončeno</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Přeskočit obnovení</string>
+48 -21
View File
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">I gang …</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Verificerer sikkerhedskopi…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d indtil videre …</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">%1$s%% indtil videre…</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Rediger enhedsnavn</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Enhedsnavn</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Gem</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Enhedsnavn opdateret</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">Kunne ikke ændre enhedens navn. Prøv igen senere.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Overfør beskedhistorik</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Overfør dine beskeder og seneste medier til din forbundne enhed</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">Overfør ikke</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">Ingen gamle beskeder eller medier vil blive overført til din forbundne enhed</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">Frakobl \"%1$s\"?</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">Din sidste sikkerhedskopi blev lavet %1$s kl. %2$s.</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">Din sidste sikkerhedskopi blev oprettet %1$s kl. %2$s. Sikkerhedskopiens størrelse er %3$s.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Henter sikkerhedskopieringsoplysninger…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Spring gendannelse over</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Underret mig ved omtaler</string>
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">Fortsæt chatten med %1$s, og del dit navn og billede?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Tilmeld dig gruppen og del dit navn og billede med dens medlemmer? De ved ikke, at du har set deres beskeder, før du accepterer.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">Tilmeld dig gruppen og del dit navn og billede med dens medlemmer? Du vil ikke se deres beskeder, før du accepterer.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Bliv medlem af gruppen? De ved ikke at du har set deres beskeder, før du accepterer</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Fjern blokering af gruppen og del dit navn og billede med dens medlemmer? Du modtager ikke nogen beskeder, før du fjerner blokeringen</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Tryk her for at starte video</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">For at ringe til %1$s har Signal brug for tilladelse til at tilgå dit kamera</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">Ringer op …</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">Signal ringer (%1$d)</item>
<item quantity="other">Signal ringer (%1$d)</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal sender en notifikation (%1$d)</item>
<item quantity="other">Signal sender en notifikation (%1$d)</item>
@@ -2481,7 +2489,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Du har forsøgt at registrere dette nummer for mange gange. Prøv igen om %1$s.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">Ikke muligt at få forbindelse til service. Tjek venligst din netværksforbindelse og prøv igen.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">Signal kunne ikke sende en sms-kode på grund af problemer med sms-udbyderen. Prøv igen om et par timer.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">Vi kunne ikke sende dig en bekræftelseskode via sms. Prøv at modtage din kode via et taleopkald i stedet.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Gruppe opdateret</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Forlod gruppen</string>
<string name="ThreadRecord_secure_session_reset">Nulstil sikker forbindelse</string>
<string name="ThreadRecord_draft">Kladde:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$d chat</item>
<item quantity="other">%1$d chats</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Seneste fra: %1$s</string>
<string name="MessageNotifier_locked_message">Låst besked</string>
<string name="MessageNotifier_message_delivery_failed">Levering af besked mislykkedes.</string>
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Del kun med udvalgte personer</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d person</item>
<item quantity="other">%1$d personer</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Vælg, hvem der kan se din historie. Ændringer vil ikke påvirke historier, som du allerede har sendt.</string>
@@ -7509,9 +7519,9 @@
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Sikkerhedskopiering mislykkedes</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">Der opstod en fejl, og din sikkerhedskopiering kunne ikke fuldføres. Tjek, at du har den nyeste version af Signal, og prøv igen. Kontakt support, hvis problemet fortsætter.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Tjek efter ny version</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Annuller download</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">Downloader: %1$s af %2$s (%3$s %%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">Ikke nok plads til at gemme din sikkerhedskopi. Frigør %1$s for at fortsætte.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Spring download over</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">Din sidste sikkerhedskopiering kunne ikke gennemføres. Tjek, om din telefon er tilsluttet Wi-Fi-netværket, og tryk på \"Sikkerhedskopiér nu\" for at prøve igen.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">Din sidste sikkerhedskopiering kunne ikke gennemføres. Tjek, at du har den nyeste version af Signal, og prøv igen.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Få mere at vide</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">Betalingshistorik</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Sikkerhedskopioplysninger</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Sikkerhedskopistørrelse</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Få mere at vide</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">Behandler sikkerhedskopi</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">Du har %1$s sikkerhedskopieret data, der ikke er på denne enhed. Din sikkerhedskopi slettes, når dit abonnement slutter om %2$d dag.</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">Kunne ikke deaktivere og slette sikkerhedskopier</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">Der opstod en netværksfejl. Tjek din internetforbindelse, og prøv igen.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">Scannet på gammel enhed</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Prøv igen</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Overfør konto</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">Okay</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">Ingen sikkerhedskopi til gendannelse</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">Da du flytter fra iPhone til Android, er den eneste måde at overføre dine beskeder og medier ved at aktivere Signal-sikkerhedskopier på din gamle enhed.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Åbn Signal på din gamle enhed</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Tryk på Indstillinger &gt; Sikkerhedskopier</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Slå sikkerhedskopiering til, og vent til din sikkerhedskopiering er færdig</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Spring gendannelse over</string>
+48 -21
View File
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">In Bearbeitung …</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Datensicherung wird überprüft…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d bisher …</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">%1$s%% bisher …</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Gerätename bearbeiten</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Gerätename</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Speichern</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Gerätename aktualisiert</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">Gerätename konnte nicht geändert werden. Versuche es später erneut.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Nachrichtenverlauf übertragen</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Übertrage deine Textnachrichten und zuletzt verwendete Medien auf dein gekoppeltes Gerät</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">Nicht übertragen</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">Es werden keine alten Nachrichten oder Medien auf dein gekoppeltes Gerät übertragen</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">»%1$s« entkoppeln?</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">Deine letzte Datensicherung wurde am %1$s um %2$s durchgeführt.</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">Deine letzte Datensicherung wurde am %1$s um %2$s durchgeführt. Die Größe deiner Datensicherung beträgt %3$s.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Backup-Details abrufen…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Wiederherstellen überspringen</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Mich bei Erwähnungen benachrichtigen</string>
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">Chat mit %1$s fortsetzen und deinen Namen und dein Foto mit diesem Kontakt teilen?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Möchtest du dieser Gruppe beitreten und deinen Namen und dein Foto mit ihren Mitgliedern teilen? Diese wissen nicht, dass du ihre Nachrichten gesehen hast, bis du die Anfrage annimmst.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">Möchtest du dieser Gruppe beitreten und deinen Namen und dein Foto mit ihren Mitgliedern teilen? Du kannst deren Nachrichten nicht sehen, bis du die Anfrage annimmst.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Dieser Gruppe beitreten? Die Gruppenmitglieder wissen nicht, dass du ihre Nachrichten gesehen hast, bis du die Anfrage annimmst.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Möchtest du diese Gruppe freigeben und deinen Namen und dein Foto mit deren Mitgliedern teilen? Du wirst keine Nachrichten erhalten, es sei denn, du erteilst eine Freigabe.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Hier antippen, um Videoübertragung zu aktivieren</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">Um %1$s anzurufen, benötigt Signal Zugriff auf deine Kamera</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">Anrufen …</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">Signal wird klingeln (%1$d)</item>
<item quantity="other">Signal wird klingeln (%1$d)</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal wird benachrichtigen (%1$d)</item>
<item quantity="other">Signal wird benachrichtigen (%1$d)</item>
@@ -2481,7 +2489,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Du hast zu oft versucht, diese Nummer zu registrieren. Bitte versuche es erneut in %1$s.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">Keine Verbindung zum Dienst möglich. Bitte überprüfe deine Netzverbindung und versuche es erneut.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">Aufgrund von Problemen mit dem SMS-Anbieter konnte Signal keinen SMS-Code senden. Versuche es in einigen Stunden erneut.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">Wir konnten dir keinen Verifizierungscode per SMS schicken. Versuche stattdessen, den Code per Sprachanruf zu erhalten.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Gruppe aktualisiert</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Gruppe verlassen</string>
<string name="ThreadRecord_secure_session_reset">Sichere Sitzung wurde zurückgesetzt.</string>
<string name="ThreadRecord_draft">Entwurf:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$d Chat</item>
<item quantity="other">%1$d Chats</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Neueste von: %1$s</string>
<string name="MessageNotifier_locked_message">Gesperrte Nachricht</string>
<string name="MessageNotifier_message_delivery_failed">Nachrichtenzustellung gescheitert.</string>
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Nur mit ausgewählten Personen teilen</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d Person</item>
<item quantity="other">%1$d Personen</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Wähle aus, wer deine Story sehen kann. Änderungen wirken sich nicht auf bereits gesendete Storys aus.</string>
@@ -7509,9 +7519,9 @@
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Datensicherung gescheitert</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">Aufgrund eines Fehlers konnte deine letzte Datensicherung nicht abgeschlossen werden. Vergewissere dich, dass du die neueste Version von Signal verwendest, und versuche es erneut. Falls das Problem weiterhin besteht, kontaktiere bitte den Support.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Nach Updates suchen</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Download abbrechen</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">%1$s von %2$s (%3$s%%) werden heruntergeladen</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">Nicht genug Speicherplatz, um deine Datensicherung zu speichern. Gib %1$s Speicherplatz frei, um fortzufahren.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Download überspringen</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">Deine letzte Datensicherung konnte nicht abgeschlossen werden. Stelle sicher, dass dein Smartphone mit dem WLAN verbunden ist, und tippe auf »Jetzt sichern«, um es erneut zu versuchen.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">Deine letzte Datensicherung konnte nicht abgeschlossen werden. Vergewissere dich, dass du die neueste Version von Signal verwendest, und versuche es erneut.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Mehr erfahren</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">Zahlungsverlauf</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Details zur Datensicherung</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Größe der Datensicherung</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Mehr erfahren</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">Datensicherung wird bearbeitet</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">Du verfügst über %1$s gesicherte Daten, die sich nicht auf diesem Gerät befinden. Deine Datensicherung wird gelöscht, wenn dein Abo in %2$d Tag endet.</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">Datensicherungen konnten nicht ausgeschaltet und gelöscht werden.</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">Ein Netzwerkfehler ist aufgetreten. Bitte überprüfe deine Internetverbindung und versuche es erneut.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">Auf altem Gerät gescannt</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Erneut versuchen</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Konto übertragen</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">OK</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">Kein Backup zum Wiederherstellen</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">Da du von iPhone auf Android umsteigst, kannst du deine Nachrichten und Medien nur übertragen, wenn du auf deinem alten Gerät Signal Backups aktivierst.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Öffne Signal auf deinem alten Gerät</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Tippe auf Einstellungen &gt; Backups</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Aktiviere Backups und warte, bis die Datensicherung abgeschlossen ist</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Wiederherstellen überspringen</string>
+50 -23
View File
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">Σε εξέλιξη…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Επιβεβαίωση αντιγράφου ασφαλείας…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d μέχρι τώρα…</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">%1$s%% μέχρι τώρα…</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Επεξεργασία ονόματος συσκευής</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Όνομα συσκεύης</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Αποθήκευση</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Το όνομα της συσκευής ενημερώθηκε</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">Δεν είναι δυνατή η αλλαγή του ονόματος της συσκευής. Δοκίμασε ξανά αργότερα.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Μεταφορά ιστορικού μηνυμάτων</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Μετάφερε τα μηνύματα και τα πρόσφατα πολυμέσα στη συνδεδεμένη συσκευή σου</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">Να μη γίνει μεταφορά</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">Δεν θα μεταφερθούν παλιά μηνύματα ή πολυμέσα στη συνδεδεμένη συσκευή σου</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">Αποσύνδεση της \"%1$s\";</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">Το τελευταίο σου αντίγραφο ασφαλείας έγινε στις %1$s στις %2$s.</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">Το τελευταίο σου αντίγραφο ασφαλείας έγινε στις %1$s στις %2$s. Το μέγεθος του αντιγράφου ασφαλείας είναι %3$s.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Λήψη στοιχείων αντιγράφων ασφαλείας…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Παράλειψη επαναφοράς</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Να ειδοποιούμαι για αναφορές</string>
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">Να συνεχίσεις τη συνομιλία σου με τον/την %1$s και να μοιραστείς το όνομα και τη φωτογραφία σου μαζί του/της;</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Θέλεις να μπεις στην ομάδα και να μοιραστείς το όνομα και τη φωτογραφία σου με τα μέλη της; Δεν θα ξέρουν ότι έχεις δει τα μηνύματά τους μέχρι να δεχτείς.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">Θέλεις να μπεις στην ομάδα και να μοιραστείς το όνομα και τη φωτογραφία σου με τα μέλη της; Δεν θα μπορείς να δεις τα μηνύματά τους μέχρι να δεχτείς.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Θέλεις να μπεις σε αυτή την ομάδα; Δεν θα γνωρίζουν οτι έχεις δει τα μηνυματά τους μέχρι να δεχτείς.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Θέλεις να καταργηθεί ο αποκλεισμός αυτής της ομάδας και να μοιραστείς το όνομα και τη φωτογραφία σου με τα μέλη της; Δεν θα λάβεις κανένα μήνυμα μέχρι να καταργήσεις τον αποκλεισμό της.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Πάτα εδώ για να ενεργοποιήσεις το βίντεό σου</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">Για να καλέσεις τον/την %1$s, το Signal χρειάζεται πρόσβαση στην κάμερά σου</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">Καλεί…</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">Το Signal θα κάνει κλήση (%1$d)</item>
<item quantity="other">Το Signal θα κάνει κλήση (%1$d)</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Το Signal θα ειδοποιήσει (%1$d)</item>
<item quantity="other">Το Signal θα ειδοποιήσει (%1$d)</item>
@@ -2481,7 +2489,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Έχεις προσπαθήσει πάρα πολλές φορές να εγγράψεις αυτόν τον αριθμό. Ξαναπροσπάθησε σε %1$s.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">Αδυναμία σύνδεσης στην υπηρεσία. Παρακαλώ έλεγξε τη σύνδεση στο δίκτυο και ξαναπροσπάθησε.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">Το Signal δεν μπόρεσε να στείλει κωδικό SMS λόγω προβλημάτων με τον πάροχο SMS. Δοκίμασε ξανά σε λίγες ώρες.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">Δεν μπορέσαμε να σου στείλουμε κωδικό επαλήθευσης μέσω SMS. Δοκίμασε να λάβεις τον κωδικό μέσω κλήσης.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Η ομάδα ενημερώθηκε</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Έφυγες απ\' την ομάδα</string>
<string name="ThreadRecord_secure_session_reset">Η ασφαλής συνεδρία επανεκκινήθηκε.</string>
<string name="ThreadRecord_draft">Πρόχειρο:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$d συνομιλία</item>
<item quantity="other">%1$d συνομιλίες</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Τελευταίο μήνυμα από: %1$s</string>
<string name="MessageNotifier_locked_message">Κλειδωμένο μήνυμα</string>
<string name="MessageNotifier_message_delivery_failed">Η παράδοση του μηνύματος απέτυχε.</string>
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Κοινοποίηση μόνο σε συγκεκριμένα άτομα</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d άτομο</item>
<item quantity="other">%1$d άτομα</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Επίλεξε ποια άτομα μπορούν να δουν την ιστορία σου. Οι αλλαγές δεν επηρεάζουν ιστορίες που έχεις στείλει ήδη.</string>
@@ -7507,11 +7517,11 @@
<!-- Dialog text for skipping media restore -->
<string name="BackupAlertBottomSheet__if_you_skip_restore_the">Αν παραλείψεις την επαναφορά, τα υπόλοιπα πολυμέσα και τα συνημμένα στο αντίγραφο ασφαλείας σου μπορούν να ληφθούν αργότερα, όταν θα υπάρχει διαθέσιμος χώρος αποθήκευσης.</string>
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Ή δημιουργία αντίγραφου ασφαλείας απέτυχε</string>
<string name="BackupAlertBottomSheet__backup_failed">Η δημιουργία αντίγραφου ασφαλείας απέτυχε</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">Παρουσιάστηκε σφάλμα και η δημιουργία αντιγράφων ασφαλείας δεν ήταν δυνατό να ολοκληρωθεί. Βεβαιώσου ότι έχεις την τελευταία έκδοση του Signal και δοκίμασε ξανά. Εάν το πρόβλημα παραμένει, επικοινώνησε με την υποστήριξη.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Έλεγχος για ενημέρωση</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Ακύρωση λήψης</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">Λήψη: %1$s από %2$s (%3$s%%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">Δεν υπάρχει αρκετός χώρος για την αποθήκευση του αντίγραφου ασφαλείας. Για να συνεχίσεις, απελευθέρωσε %1$s χώρου.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Παράλειψη λήψης</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">Δεν ήταν δυνατή η ολοκλήρωση του τελευταίου αντιγράφου ασφαλείας. Βεβαιώσου ότι η συσκευή σου είναι συνδεδεμένη σε Wi-Fi και πάτα \"Δημιουργία αντιγράφων ασφαλείας τώρα\" για να προσπαθήσεις ξανά.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">Δεν ήταν δυνατή η ολοκλήρωση του τελευταίου αντιγράφου ασφαλείας. Βεβαιώσου ότι έχεις την τελευταία έκδοση του Signal και δοκίμασε ξανά.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Μάθε περισσότερα</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">Ιστορικό πληρωμών</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Λεπτομέρειες αντιγράφου ασφαλείας</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Μέγεθος αντίγραφου ασφαλείας</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Μάθε περισσότερα</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">Επεξεργασία αντιγράφων ασφαλείας</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">Έχεις %1$s δεδομένα αντιγράφων ασφαλείας που δεν υπάρχουν σε αυτήν τη συσκευή. Το αντίγραφο ασφαλείας σου θα διαγραφεί όταν λήξει η συνδρομή σου σε %2$d ημέρα.</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">Δεν ήταν δυνατή η απενεργοποίηση και η διαγραφή των αντιγράφων ασφαλείας</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">Παρουσιάστηκε σφάλμα δικτύου. Έλεγξε τη σύνδεσή σου στο διαδίκτυο και δοκίμασε ξανά.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">Σαρώθηκε σε παλιά συσκευή</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Επανάληψη</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Μεταφορά λογαριασμού</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">Εντάξει</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">Δεν υπάρχουν αντίγραφα ασφαλείας για επαναφορά</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">Επειδή αλλάζεις από iPhone σε Android, ο μόνος τρόπος για να μεταφέρεις τα μηνύματα και τα πολυμέσα σου είναι να ενεργοποιήσεις τα Αντίγραφα ασφαλείας Signal στην παλιά σου συσκευή.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Άνοιξε το Signal στη παλιά σου συσκευή</string>
<string name="NoBackupToRestore_step1">Άνοιξε το Signal στην παλιά σου συσκευή</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Πάτα Ρυθμίσεις &gt; Αντίγραφα ασφαλείας</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Ενεργοποίησε τα αντίγραφα ασφαλείας και περίμενε μέχρι να ολοκληρωθεί η δημιουργία αντιγράφων ασφαλείας</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Παράλειψη επαναφοράς</string>
+59 -32
View File
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">En curso…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Verificando copia de seguridad…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d completados…</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">%1$s %% completado…</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Editar nombre del dispositivo</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Nombre del dispositivo</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Guardar</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Nombre del dispositivo actualizado</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">No se ha podido cambiar el nombre del dispositivo. Inténtalo de nuevo más tarde.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Transferir historial de mensajes</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfiere tus mensajes de texto y archivos multimedia recientes a tu dispositivo vinculado</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">No transferir</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No se transferirán mensajes ni archivos multimedia antiguos a tu dispositivo vinculado</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">¿Desvincular \"%1$s\"?</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">Última copia de seguridad: %2$s del %1$s.</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">Última copia de seguridad: %2$s del %1$s. El tamaño de tu copia de seguridad es de %3$s.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Obteniendo detalles de la copia de seguridad…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Omitir restauración</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Notificarme cuando alguien me mencione</string>
@@ -1424,7 +1429,7 @@
<!-- Displayed above the conversation list when a user needs to address an issue with their username link -->
<string name="UsernameOutOfSyncReminder__link_corrupt">Hubo un problema con el código QR y el enlace de tu alias; ya no es válido. Crea un nuevo enlace para compartir.</string>
<!-- Action text to navigate user to manually fix the issue with their username -->
<string name="UsernameOutOfSyncReminder__fix_now">Corregirlo ahora</string>
<string name="UsernameOutOfSyncReminder__fix_now">Corregir ahora</string>
<!-- ManageRecipientActivity -->
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">¿Seguir chateando con %1$s y compartir tu nombre y foto con esta persona?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">¿Unirte a este grupo y compartir tu nombre y foto con sus participantes? No sabrán que has visto sus mensajes hasta que aceptes.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">¿Unirte a este grupo y compartir tu nombre y foto con sus participantes? No podrás ver sus mensajes hasta que aceptes.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">¿Unirte a este grupo? Sus participantes no sabrán que has visto sus mensajes hasta que aceptes.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">¿Desbloquear este grupo y compartir tu nombre y foto con sus participantes? No recibirás ningún mensaje del grupo hasta que lo desbloquees.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Toca aquí para activar tu cámara</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">Para llamar a %1$s, Signal necesita acceder a tu cámara</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">Llamando…</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">Signal llamará a (%1$d)</item>
<item quantity="other">Signal llamará a (%1$d)</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal notificará a (%1$d)</item>
<item quantity="other">Signal notificará a (%1$d)</item>
@@ -2481,9 +2489,9 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Has hecho demasiados intentos para registrar este número. Inténtalo de nuevo en %1$s.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">No se ha podido conectar con el servidor. Comprueba la conexión de red e inténtalo de nuevo.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">Signal no ha podido enviar un código SMS debido a problemas con el proveedor de SMS. Vuelve a intentarlo dentro de unas horas.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">No hemos podido enviarte el código de verificación por SMS. Intenta recibir tu código a través de una llamada.</string>
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">No hemos podido enviarte el código de verificación por SMS. Intenta recibir tu código mediante llamada.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
<string name="RegistrationActivity_unable_to_request_verification_code">No se ha podido solicitar un código de verificación. Comprueba la conexión de red e inténtalo de nuevo.</string>
<string name="RegistrationActivity_non_standard_number_format">Número en formato no estándar</string>
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Grupo actualizado</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Abandonó el grupo</string>
<string name="ThreadRecord_secure_session_reset">Sesión segura restablecida.</string>
<string name="ThreadRecord_draft">Borrador:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$d chat</item>
<item quantity="other">%1$d chats</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Más recientes desde: %1$s</string>
<string name="MessageNotifier_locked_message">Mensaje bloqueado</string>
<string name="MessageNotifier_message_delivery_failed">No se ha podido entregar el mensaje.</string>
@@ -3364,12 +3374,12 @@
<string name="EditAboutFragment_about">Sobre mí</string>
<string name="EditAboutFragment_write_a_few_words_about_yourself">Escribe algunas palabras sobre ti…</string>
<string name="EditAboutFragment_count">%1$d/%2$d</string>
<string name="EditAboutFragment_speak_freely">Habla con libertad</string>
<string name="EditAboutFragment_encrypted">Cifrado y seguro</string>
<string name="EditAboutFragment_be_kind">Sé amable</string>
<string name="EditAboutFragment_coffee_lover">Amante del café</string>
<string name="EditAboutFragment_free_to_chat">Libre para charlar</string>
<string name="EditAboutFragment_taking_a_break">Tomándome un descanso</string>
<string name="EditAboutFragment_speak_freely">¡Hola, hola!</string>
<string name="EditAboutFragment_encrypted">Privacidad ante todo</string>
<string name="EditAboutFragment_be_kind">Namasté</string>
<string name="EditAboutFragment_coffee_lover">Recargando energías</string>
<string name="EditAboutFragment_free_to_chat">Todo bien por aquí</string>
<string name="EditAboutFragment_taking_a_break">Desconectando para conectar</string>
<string name="EditAboutFragment_working_on_something_new">Trabajando en algo nuevo</string>
<!-- EditProfileFragment -->
@@ -5312,9 +5322,9 @@
<string name="HelpFragment__licenced_under_the_agplv3">Licencia GNU AGPLv3</string>
<!-- DataAndStorageSettingsFragment -->
<string name="DataAndStorageSettingsFragment__media_quality">Calidad de archivos multimedia</string>
<string name="DataAndStorageSettingsFragment__media_quality">Archivos multimedia enviados</string>
<!-- Category title for the quality of the media to be sent -->
<string name="DataAndStorageSettingsFragment__sent_media_quality">Archivos multimedia enviados</string>
<string name="DataAndStorageSettingsFragment__sent_media_quality">Calidad de fotos y vídeos</string>
<string name="DataAndStorageSettingsFragment__sending_high_quality_media_will_use_more_data">Si seleccionas la opción de alta calidad, se usarán más datos.</string>
<!-- Setting option that can be selected to default media to be sent as high quality by default -->
<string name="DataAndStorageSettingsFragment__high">Alta</string>
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Compartir solo con las personas seleccionadas</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d persona</item>
<item quantity="other">%1$d personas</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Elige quién puede ver tus historias. Estos cambios no afectarán a las historias que ya hayas compartido.</string>
@@ -7330,7 +7340,7 @@
<!-- Button text to allow all participants to join the call -->
<string name="PendingParticipantsBottomSheet__approve_all">Aprobar todas</string>
<!-- Button text to deny all participants from joining the call -->
<string name="PendingParticipantsBottomSheet__deny_all">Denegar todas</string>
<string name="PendingParticipantsBottomSheet__deny_all">Rechazar todas</string>
<!-- Title of a megaphone shown at the bottom of the chat list when a user has disable the system setting for showing full screen notifications used showing incoming calls -->
<string name="GrantFullScreenIntentPermission_megaphone_title">¿Activar las notificaciones de pantalla completa?</string>
@@ -7509,9 +7519,9 @@
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Error en la copia de seguridad</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">Se ha producido un error y no se ha podido completar tu copia de seguridad. Asegúrate de estar usando la última versión de Signal y vuelve a intentarlo. Si el problema persiste, ponte en contacto con el equipo de asistencia.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Buscar actualizaciones</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Cancelar descarga</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">Descargando: %1$s de %2$s (%3$s %%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">No hay suficiente espacio para descargar tu copia de seguridad. Para continuar, libera %1$s de espacio.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Omitir descarga</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">No se ha podido completar tu última copia de seguridad. Asegúrate de que tu teléfono esté conectado a una red Wi-Fi y toca \"Iniciar copia\" para volver a intentarlo.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">No se ha podido completar tu última copia de seguridad. Asegúrate de estar usando la última versión de Signal y vuelve a intentarlo.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Más información</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">Historial de pagos</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Detalles de la copia de seguridad</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Tamaño de la copia de seguridad</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Más información</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">Procesando copia de seguridad</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">Hay %1$s de datos de tu copia de seguridad que no están en este dispositivo. Tu copia de seguridad se eliminará cuando finalice tu suscripción en %2$d día.</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">No se ha podido desactivar y eliminar la copia de seguridad</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">Se ha producido un error de red. Comprueba tu conexión a Internet e inténtalo de nuevo.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">Escaneado en dispositivo anterior</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Reintentar</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Transferir cuenta</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">Aceptar</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">No hay copia de seguridad para restaurar</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">Debido a que estás cambiando de iPhone a Android, la única forma de transferir tus mensajes y archivos multimedia es habilitando las copias de seguridad de Signal en tu dispositivo anterior.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Abre Signal en tu dispositivo anterior</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Toca Ajustes &gt; Copias de seguridad</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Habilita las copias de seguridad y espera hasta que se complete tu copia de seguridad</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Omitir restauración</string>
+48 -21
View File
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">Töötamine…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Varukoopia kontrollimine …</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">Siiani %1$d…</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">%1$s/ %% …</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Muuda seadme nime</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Seadme nimi</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Salvesta</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Seadme nimi uuendatud</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">Seadme nime ei saa muuta. Proovi hiljem uuesti.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Edasta sõnumiajalugu</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Edasta tekstisõnumid ja hiljutine meedia oma lingitud seadmesse</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">Ära edasta</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">Lingitud seadmesse ei edastata vanu sõnumeid ega meediat</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">Kas tühistada seadme „%1$s“ linkimine?</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">Sinu viimane varukoopia tehti %1$s kell %2$s.</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">Sinu viimane varukoopia tehti %1$s kell %2$s. Sinu varukoopia suurus on %3$s.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Varundamidandmete hankimine …</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Jäta taastamine vahele</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Teavita mind mainimise korral</string>
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">Kas jätkata seda vestlust isikuga %1$s ning jagada temaga sinu nime ja fotot?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Kas soovid selle grupiga liituda ja jagada enda nime ning pilti selle liikmetega? Nad ei tea, et oled seda sõnumit näinud enne kui oled sellega nõustunud.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">Kas soovid selle grupiga liituda ning jagada enda nime ja pilti selle liikmetega? Sa ei näe nende sõnumeid enne kui oled sellega nõustunud.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Kas liitud grupiga? Nad ei tea, et oled nende sõnumeid näinud, kuni nõustud.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Kas soovid eemaldada sellelt grupilt blokeering ja jagada selle liikmetega oma nime ja fotot? Sa ei saa sõnumeid enne kui oled blokeeringu eemaldanud.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Klõpsa siia enda video käivitamiseks</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">Signal vajab kasutajale %1$s helistamiseks juurdepääsu seadme kaamerale</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">Helistamine…</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">Signal helistab isikule (%1$d)</item>
<item quantity="other">Signal helistab isikutele (%1$d)</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal teavitab isikut (%1$d)</item>
<item quantity="other">Signal teavitab isikuid (%1$d)</item>
@@ -2481,7 +2489,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Oled proovinud seda numbrit liiga palju kordi registreerida. Palun proovi uuesti %1$s pärast.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">Teenusega ei saadud ühendust. Palun kontrolli võrguühendust ja proovi uuesti.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">Signal ei saanud SMS-i teenusepakkujaga seotud probleemide tõttu SMS-koodi saata. Proovi mõne tunni pärast uuesti.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">Me ei saanud sulle SMS-i kaudu kinnituskoodi saata. Proovi koodi saamiseks parem häälkõnet kasutada.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Grupp uuendatud</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Lahkus grupist</string>
<string name="ThreadRecord_secure_session_reset">Turvaline sessioon lähtestatud.</string>
<string name="ThreadRecord_draft">Mustand:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$d vestlus</item>
<item quantity="other">%1$d vestlust</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Viimatine kontaktilt %1$s</string>
<string name="MessageNotifier_locked_message">Lukustatud sõnum</string>
<string name="MessageNotifier_message_delivery_failed">Sõnumi kohaletoimetamine ebaõnnestus.</string>
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Jaga ainult valitud isikutega</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d inimene</item>
<item quantity="other">%1$d inimest</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Vali, kes saavad sinu lugu vaadata. Muudatused ei mõjuta lugusid, mida oled juba jaganud.</string>
@@ -7509,9 +7519,9 @@
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Varundamine ebaõnnestus</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">Ilmnes viga ja varundamist ei saanud lõpule viia. Veendu, et sul on uusim Signali versioon ja proovi uuesti. Kui probleem püsib, võta ühendust kasutajatoega.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Kontrolli uuendusi</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Tühista allalaadimine</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">Allalaadimine: %1$s kogumahust %2$s (%3$s%%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">Sinu varukoopia allalaadimiseks ei ole piisavalt ruumi. Jätkamiseks vabasta %1$s ruumi.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Jäta allalaadimine vahele</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">Sinu viimast varukoopiat ei saanud lõpule viia. Veendu, et sinu telefon on Wi-Figa ühendatud ning toksa „Varunda nüüd“, et uuesti proovida.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">Sinu viimast varukoopiat ei saanud lõpule viia. Veendu, et sul on uusim Signali versioon ja proovi uuesti.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Rohkem teavet</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">Maksete ajalugu</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Varukoopia andmed</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Varukoopia suurus</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Rohkem teavet</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">Varukoopia töötlemine </string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">Sul on %1$s varundatud andmeid, mis ei ole selles seadmes. Sinu varukoopia kustutatakse, kui su tellimus %2$d päeva pärast lõpeb.</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">Varukoopiaid ei saanud välja lülitada ega kustutada</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">Tekkis võrgu viga. Kontrolli oma internetiühendust ja proovi uuesti.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">Vanas seadmes skannitud</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Proovi uuesti</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Kanna konto üle</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">Olgu</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">Ühtegi varukoopiat taastamiseks ei ole</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">Kuna liigud iPhone\'ist Androidi, on ainus viis sõnumite ja meedia edastamiseks lülitada sisse vanas seadmes Signali varukoopiad.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Ava Signal oma vanas seadmes</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Vali Sätted &gt; Varukoopiad</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Lülita varundamine sisse ja oota, kuni see lõpule jõuab</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Jäta taastamine vahele</string>
+48 -21
View File
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">Martxan…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">Babeskopia egiaztatzen…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d dagoeneko…</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">%% %1$s oraingoz…</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">Editatu gailuaren izena</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">Gailuaren izena</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">Gorde</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">Eguneratu da gailuaren izena</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">Ezin da aldatu gailuaren izena. Saiatu berriro geroago.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">Transferitu mezu-historia</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transferitu testu-mezuak eta azkenaldiko multimedia-edukiak lotutako gailura</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">Ez transferitu</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">Ez da transferituko mezu edo multimedia-eduki zaharrik lotutako gailura</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">\"%1$s\" gailuari lotura kendu nahi diozu?</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">Azken babeskopia: %1$s (%2$s).</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">Azken babeskopia: %1$s (%2$s). Babeskopiaren tamaina: %3$s.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">Babeskopiaren xehetasunak lortzen…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">Saltatu leheneratzeko aukera</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">Nahi dut aipamenak jakinaraztea</string>
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">%1$s erabiltzailearekin txateatzen jarraitu, eta zure izena eta argazkia harekin partekatu nahi dituzu?</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Talde honetara sartu eta zure izena eta argazkia kideekin partekatu nahi duzu? Zuk onartu arte, ez dute jakingo beraien mezuak ikusi duzunik.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">Talde honetan sartu, eta izena eta argazkia bertako kideekin partekatu nahi duzu? Onartu arte, ez duzu haien mezurik ikusiko.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Talde honetara sartu nahi duzu? Onartu arte ez dute jakingo beraien mezuak ikusi dituzunik.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Talde hau desblokeatu nahi duzu, eta zure izena eta argazkia hartako kideekin partekatu? Desblokeatu arte, ez duzu mezurik jasoko.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">Sakatu hau bideoa aktibatzeko</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">%1$s erabiltzaileari deitzeko, Signalek zure kamera erabiltzeko baimena behar du</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">Signal %1$s</string>
<string name="WebRtcCallActivity__calling">Deitzen…</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">Signal-ek tonua joko du (%1$d)</item>
<item quantity="other">Signal-ek tonua joko du (%1$d)</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">Signal-ek jakinarazpen bat bidaliko du (%1$d)</item>
<item quantity="other">Signal-ek jakinarazpen bat bidaliko du (%1$d)</item>
@@ -2481,7 +2489,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">Saiakera gehiegi egin dituzu zenbaki hau erregistratzeko. Saiatu berriro denbora hau igarotakoan: %1$s.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">Ezin da zerbitzura konektatu. Egiaztatu sarearen ezarpenak eta saiatu berriz.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">SMS hornitzaileak arazoren bat du, eta Signal-ek ezin izan dizu bidali SMS bidezko koderik. Saiatu berriro ordu batzuen buruan.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">Ezin izan dizugu egiaztapen-koderik bidali SMS bidez. Horren ordez, saiatu kodea ahots-dei bidez jasotzen.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Taldea eguneratu da</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">Taldea utzi duzu</string>
<string name="ThreadRecord_secure_session_reset">Saio segurua berrezarri da.</string>
<string name="ThreadRecord_draft">Zirriborroa:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$d txat</item>
<item quantity="other">%1$d txat</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">Berriena %1$s-(e)tik</string>
<string name="MessageNotifier_locked_message">Mezua blokeatuta</string>
<string name="MessageNotifier_message_delivery_failed">Mezuaren banaketak huts egin du.</string>
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">Partekatu hautatutako pertsonekin bakarrik</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">Pertsona %1$d</item>
<item quantity="other">%1$d pertsona</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">Aukeratu nork ikus dezakeen istorioa. Aldaketek ez dute eraginik izango lehenago bidalitako istorioetan.</string>
@@ -7509,9 +7519,9 @@
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">Babeskopiak huts egin du</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">Errore bat gertatu da eta ezin izan da osatu babeskopia. Ziurtatu Signal-en bertsio berriena duzula eta saiatu berriro. Arazoak bere horretan jarraitzen badu, jarri harremanetan laguntza-zerbitzuarekin.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">Egiaztatu eguneratzerik dagoen</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">Utzi deskarga bertan behera</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">%1$s/%2$s deskargatzen (%% %3$s)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">Ez dago nahikoa tokirik babeskopia deskargatzeko. Aurrera egiteko, utzi %1$s libre.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">Saltatu deskarga</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">Ezin izan da osatu azken babeskopia. Ziurtatu telefonoa wifira konektatuta dagoela eta, berriro saiatzeko, sakatu Egin babeskopia orain.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">Ezin izan da osatu azken babeskopia. Ziurtatu Signal-en bertsio berriena duzula eta saiatu berriro.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">Informazio gehiago</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">Ordainketen historia</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">Babeskopiaren xehetasunak</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">Babeskopiaren tamaina</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">Informazio gehiago</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">Babeskopia prozesatzen</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">%1$s-ko babeskopia bat duzu gailu honetatik kanpo. Harpidetza %2$d egun barru amaitzean, babeskopia ezabatu egingo da.</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">Ezin izan dira desaktibatu eta ezabatu babeskopiak</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">Sareko errore bat gertatu da. Egiaztatu Internetera konektatuta zaudela eta saiatu berriro.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">Gailu zaharrean eskaneatu da</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">Berriro saiatu</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">Transferitu kontua</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">Ados</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">Ez dago leheneratzeko babeskopiarik</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">iPhone-tik Android-era aldatzen ari zarenez, gailu zaharrean Signal-en babeskopiak gaitzea da mezuak eta multimedia-edukia transferitzeko modu bakarra.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">Ireki Signal gailu zaharrean</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">Sakatu Ezarpenak &gt; Babeskopiak</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">Gaitu babeskopiak eta itxaron babeskopiak osatu arte</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">Saltatu leheneratzeko aukera</string>
+60 -33
View File
@@ -817,7 +817,7 @@
<!-- RestoreBackupFragment -->
<string name="RestoreBackupFragment__restore_complete">بازگردانی کامل شد</string>
<string name="RestoreBackupFragment__to_continue_using_backups_please_choose_a_folder">برای ادامهٔ استفاده از پشتیبان‌ها، لطفاً یک پوشه انتخاب کنید. پشتیبان‌های جدید در این محل ذخیره خواهند شد.</string>
<string name="RestoreBackupFragment__to_continue_using_backups_please_choose_a_folder">برای ادامهٔ استفاده از نسخه‌های پشتیبان، لطفاً یک پوشه انتخاب کنید. نسخه‌های پشتیبانی جدید در این محل ذخیره خواهند شد.</string>
<string name="RestoreBackupFragment__choose_folder">انتخاب پوشه</string>
<string name="RestoreBackupFragment__not_now">حالا نه</string>
<!-- Couldn\'t find the selected backup -->
@@ -873,8 +873,8 @@
<string name="RestoreMediaReminder__Skip_restore">رد کردن بازیابی</string>
<!-- BackupsPreferenceFragment -->
<string name="BackupsPreferenceFragment__chat_backups">پشتیبان‌های گفتگو</string>
<string name="BackupsPreferenceFragment__backups_are_encrypted_with_a_passphrase">پشتیبان‌ها با یک گذرواژه رمزگذاری شده و روی دستگاه شما ذخیره می‌شوند.</string>
<string name="BackupsPreferenceFragment__chat_backups">نسخه‌های پشتیبان گفتگو</string>
<string name="BackupsPreferenceFragment__backups_are_encrypted_with_a_passphrase">نسخه‌های پشتیبان با یک گذرواژه رمزگذاری شده و روی دستگاه شما ذخیره می‌شوند.</string>
<string name="BackupsPreferenceFragment__create_backup">ایجاد پشتیبان</string>
<string name="BackupsPreferenceFragment__last_backup">آخرین پشتیبان‌گیری: %1$s</string>
<string name="BackupsPreferenceFragment__backup_folder">پوشهٔ پشتیبان</string>
@@ -889,6 +889,7 @@
<string name="BackupsPreferenceFragment__in_progress">در حال پیشرفت…</string>
<!-- Status text shown in backup preferences when verifying a backup -->
<string name="BackupsPreferenceFragment__verifying_backup">در حال تایید پشتیبان…</string>
<!-- Progress of backup where %d is the number of files completed so far -->
<string name="BackupsPreferenceFragment__d_so_far">%1$d تا بحال…</string>
<!-- Show percentage of completion of backup -->
<string name="BackupsPreferenceFragment__s_so_far">%1$s%% تا کنون…</string>
@@ -1016,15 +1017,15 @@
<!-- EditDeviceNameFragment -->
<!-- App bar title when editing the name of a device -->
<string name="EditDeviceNameFragment__edit">Edit device name</string>
<string name="EditDeviceNameFragment__edit">ویرایش نام دستگاه</string>
<!-- Text hint shown when entering in a new device name -->
<string name="EditDeviceNameFragment__device_name">نام دستگاه</string>
<!-- Button to save name change -->
<string name="EditDeviceNameFragment__save">ذخیره</string>
<!-- Toast message shown when a device name was successfully changed -->
<string name="EditDeviceNameFragment__device_name_updated">Device name updated</string>
<string name="EditDeviceNameFragment__device_name_updated">نام دستگاه به‌روزرسانی شد</string>
<!-- Toast message shown when a device name could not be changed and to try again later -->
<string name="EditDeviceNameFragment__unable_to_change">Unable to change device name. Try again later.</string>
<string name="EditDeviceNameFragment__unable_to_change">تغییر نام دستگاه امکان‌پذیر نیست. بعداً دوباره امتحان کنید.</string>
<!-- AddLinkDeviceFragment -->
<!-- Description text shown on the QR code scanner when linking a device -->
@@ -1052,11 +1053,11 @@
<!-- Option in bottom sheet to transfer message history -->
<string name="LinkDeviceSyncBottomSheet_transfer">انتقال تاریخچه پیام</string>
<!-- Description in bottom sheet of what transferring message history will do -->
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">Transfer your text messages and recent media to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_transfer_your_text">پیامک‌ها و فایل‌های رسانه اخیرتان را به دستگاه متصل خود منتقل کنید</string>
<!-- Option in bottom sheet to not transfer message history -->
<string name="LinkDeviceSyncBottomSheet_dont_transfer">انتقال داده نشود</string>
<!-- Description in bottom sheet of what not transferring history will do -->
<string name="LinkDeviceSyncBottomSheet_no_old_messages">No old messages or media will be transferred to your linked device</string>
<string name="LinkDeviceSyncBottomSheet_no_old_messages">هیچ پیام یا فایل رسانه قدیمی‌ای به دستگاه متصل شما منتقل نخواهد شد</string>
<!-- DeviceListActivity -->
<string name="DeviceListActivity_unlink_s">قطع ارتباط با «%1$s»؟</string>
@@ -1376,11 +1377,15 @@
<!-- Displayed at restore time to tell the user when their last backup was made. %1$s is replaced with the date (e.g. March 5, 2024) and %2$s is replaced with the time (e.g. 9:00am) -->
<string name="RemoteRestoreActivity__backup_created_at">آخرین پشتیبان‌گیری در %1$s در %2$s انجام شده است.</string>
<!-- Displayed at restore time to tell the user when their last backup was made and size. %1$s is replaced with the date (e.g. March 5, 2024), %2$s is replaced with the time (e.g. 9:00am), %3$1 is replaced with size (e.g., 1.2GB) -->
<string name="RemoteRestoreActivity__backup_created_at_with_size">Your last backup was made on %1$s at %2$s. Your backup size is %3$s.</string>
<string name="RemoteRestoreActivity__backup_created_at_with_size">آخرین پشتیبان‌گیری در %1$s در %2$s انجام شده است. حجم نسخه پشتیبان شما %3$s است.</string>
<!-- Progress dialog label while fetching backup info if we don\'t already have it -->
<string name="RemoteRestoreActivity__fetching_backup_details">در حال دریافت جزئیات نسخه پشتیبان…</string>
<!-- Text label button to skip restore from remote -->
<string name="RemoteRestoreActivity__skip_restore">رد کردن بازیابی</string>
<!-- Dialog title displayed when remote restore failed -->
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
<!-- Dialog message displayed when remote restore failed -->
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldnt be transferred. Try again by choosing your transfer method.</string>
<!-- GroupMentionSettingDialog -->
<string name="GroupMentionSettingDialog_notify_me_for_mentions">برای اشاره‌ها من را آگاه کن</string>
@@ -1989,6 +1994,7 @@
<string name="MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo">می‌خواهید گفتگویتان با %1$s را ادامه دهید و نام و عکس خود را با او به اشتراک بگذارید؟</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">آیا می‌خواهید به این گروه بپیوندید و نام و عکس خود را با اعضای آن به اشتراک بگذارید؟ تا زمانی که نپذیرید آن‌ها متوجه نمی‌شوند که پیام‌شان را خوانده‌اید.</string>
<string name="MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages">به این گروه می‌پیوندید و نام و عکس خود را با اعضای آن به اشتراک می‌گذارید؟ تا زمانی که نپذیرید پیام‌های آنها را نخواهید دید.</string>
<!-- Shown when you are invited to a group and explains that until you accept the invitation to the group, members will not know that you have seen their messages. -->
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">به این گروه می‌پیوندید؟ تا زمانی که نپذیرید آن‌ها متوجه نخواهند شد که پیام‌شان را خوانده‌اید.</string>
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">می‌خواهید این گروه را رفع مسدودیت کنید و نام و عکستان را با اعضای آن به اشتراک بگذارید؟ تا وقتی آن را رفع مسدودیت نکنید هیچ پیامی دریافت نخواهید کرد.</string>
<!-- Removed by excludeNonTranslatables <string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string> -->
@@ -2174,6 +2180,7 @@
<!-- WebRtcCallActivity -->
<string name="WebRtcCallActivity__tap_here_to_turn_on_your_video">برای فعال‌سازی ویدئو اینجا ضربه بزنید</string>
<string name="WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera">برای تماس با%1$s، سیگنال به دسترسی دوربین شما نیاز دارد</string>
<!-- Subtitle shown at top of call where %s is the elapsed time of the call -->
<string name="WebRtcCallActivity__signal_s">%1$s سیگنال</string>
<string name="WebRtcCallActivity__calling">در حال برقراری تماس…</string>
<!-- Call status shown when an active call was disconnected (e.g., network hiccup) and is trying to reconnect -->
@@ -2420,6 +2427,7 @@
<item quantity="one">سیگنال زنگ خواهد زد (%1$d)</item>
<item quantity="other">سیگنال زنگ خواهد زد (%1$d)</item>
</plurals>
<!-- Header text shown in a bottom sheet that indicates how many people will be notified about an outgoing call -->
<plurals name="CallParticipantsListDialog__signal_will_notify">
<item quantity="one">سیگنال اعلان خواهد فرستاد (%1$d)</item>
<item quantity="other">سیگنال اعلان خواهد فرستاد (%1$d)</item>
@@ -2481,7 +2489,7 @@
<string name="RegistrationActivity_rate_limited_to_try_again">دفعات تلاش‌تان برای ثبت این شماره بیش از حد مجاز بوده است. لطفاً بعد از %1$s دوباره تلاش کنید.</string>
<string name="RegistrationActivity_unable_to_connect_to_service">اتصال به سرویس ممکن نیست. لطفاً اتصال شبکه را بررسی کرده و دوباره امتحان کنید.</string>
<!-- Dialog error message shown when attempting to register and the SMS provider fails to send an SMS -->
<string name="RegistrationActivity_sms_provider_error">Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours.</string>
<string name="RegistrationActivity_sms_provider_error">به‌دلیل اختلال در سرویس شرکت ارائه‌دهنده پیامک، سیگنال قادر به ارسال کد پیامکی نبود. چند ساعت بعد دوباره امتحان کنید.</string>
<!-- A description text for an alert dialog when the entered phone number is not eligible for a verification SMS. -->
<string name="RegistrationActivity_we_couldnt_send_you_a_verification_code">نتوانستیم کد تأییدی از طریق پیامک برایتان ارسال کنیم. به جای آن، دریافت کد از طریق تماس را امتحان کنید.</string>
<!-- Generic error when the app is unable to request an SMS code for an unknown reason. -->
@@ -2645,6 +2653,7 @@
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">گروه به‌روزرسانی شد</string>
<!-- Text shown you have left a group -->
<string name="ThreadRecord_left_the_group">گروه را ترک کرد</string>
<string name="ThreadRecord_secure_session_reset">بازنشانی نشست امن.</string>
<string name="ThreadRecord_draft">پیش‌نویس:</string>
@@ -2849,6 +2858,7 @@
<item quantity="one">%1$d گفتگو</item>
<item quantity="other">%1$d گفتگو</item>
</plurals>
<!-- Shown in a notification when you receive multiple chat messages. %s is the contact name of the most recent message -->
<string name="MessageNotifier_most_recent_from_s">جدیدترین از: %1$s</string>
<string name="MessageNotifier_locked_message">پیام قفل شده</string>
<string name="MessageNotifier_message_delivery_failed">تحویل پیام ناموفق بود.</string>
@@ -2900,7 +2910,7 @@
<string name="NotificationChannel_calls">تماس‌ها</string>
<!-- Notification channel name for notifications involving failures of some sort (eg donations, message sends, etc.) -->
<string name="NotificationChannel_failures">خرابی‌ها</string>
<string name="NotificationChannel_backups">پشتیبان‌ها</string>
<string name="NotificationChannel_backups">نسخه‌های پشتیبان</string>
<string name="NotificationChannel_locked_status">وضعیت قفل</string>
<string name="NotificationChannel_app_updates">به‌روزرسانی‌های برنامه</string>
<string name="NotificationChannel_other">سایر</string>
@@ -4331,7 +4341,7 @@
<string name="registration_activity__skip">رد کردن</string>
<!-- Text label button to skip restoring -->
<string name="registration_activity__skip_restore">رد کردن بازیابی</string>
<string name="preferences_chats__chat_backups">پشتیبان‌های گفتگو</string>
<string name="preferences_chats__chat_backups">نسخه‌های پشتیبان گفتگو</string>
<string name="preferences_chats__transfer_account">انتقال حساب کاربری</string>
<string name="preferences_chats__transfer_account_to_a_new_android_device">انتقال حساب کاربری به یک دستگاه اندروید‌ی جدید</string>
<!-- Title for dialog to enter in the backup passphrase -->
@@ -4348,12 +4358,12 @@
<string name="RegistrationActivity_backup_size_s">اندازهٔ پشتیبان: %1$s</string>
<string name="RegistrationActivity_backup_timestamp_s">برچسب زمان پشتیبان: %1$s</string>
<string name="BackupDialog_enable_local_backups">فعال‌سازی پشتیبان‌های محلی؟</string>
<string name="BackupDialog_enable_backups">فعال‌سازی پشتیبان‌ها</string>
<string name="BackupDialog_enable_backups">فعال‌سازی نسخه‌های پشتیبان</string>
<string name="BackupDialog_please_acknowledge_your_understanding_by_marking_the_confirmation_check_box">لطفاً تأیید خود را با علامت زدن جعبه نشان دهید.</string>
<string name="BackupDialog_delete_backups">پاک کردن پشتیبان‌ها؟</string>
<string name="BackupDialog_delete_backups">پاک کردن نسخه‌های پشتیبان؟</string>
<string name="BackupDialog_disable_and_delete_all_local_backups">غیرفعال و پاک کردن تمام پشتیبان‌های محلی؟</string>
<string name="BackupDialog_delete_backups_statement">پاک کردن پشتیبان‌ها</string>
<string name="BackupDialog_to_enable_backups_choose_a_folder">برای فعال کردن پشتیبان‌ها، یک پوشه انتخاب کنید. پشتیبان‌ها در این محل ذخیره خواهند شد.</string>
<string name="BackupDialog_delete_backups_statement">پاک کردن نسخه‌های پشتیبان</string>
<string name="BackupDialog_to_enable_backups_choose_a_folder">برای فعال کردن نسخه‌های پشتیبان، یک پوشه انتخاب کنید. نسخه‌های پشتیبان در این محل ذخیره خواهند شد.</string>
<string name="BackupDialog_choose_folder">انتخاب پوشه</string>
<string name="BackupDialog_copied_to_clipboard">روی کلیپ‌بورد کپی شد</string>
<string name="BackupDialog_no_file_picker_available">انتخاب کنندهٔ فایل در دسترس نیست.</string>
@@ -4372,7 +4382,7 @@
<string name="LocalBackupJobApi29_your_backup_could_not_be_verified">نسخه پشتیبان اخیر شما ایجاد و تأیید نشد. لطفا یک نسخه جدید ایجاد کنید.</string>
<!-- Error message shown if a very large attachment is encountered during the backup creation and causes the backup to fail -->
<string name="LocalBackupJobApi29_your_backup_contains_a_very_large_file">نسخه پشتیبان شما دارای یک فایل بسیار حجیم است که نمی‌توان از آن نسخه پشتیبان تهیه کرد. لطفا آن را پاک کنید و یک نسخه پشتیبان جدید ایجاد کنید.</string>
<string name="LocalBackupJobApi29_tap_to_manage_backups">برای مدیریت پشتیبان‌ها ضربه بزنید.</string>
<string name="LocalBackupJobApi29_tap_to_manage_backups">برای مدیریت نسخه‌های پشتیبان ضربه بزنید.</string>
<string name="RegistrationActivity_wrong_number">شماره اشتباه است؟</string>
<!-- Countdown to when the user can request a new code via phone call during registration.-->
<string name="RegistrationActivity_call_me_instead_available_in">با من تماس بگیر (%1$02d:%2$02d)</string>
@@ -4435,7 +4445,7 @@
<string name="RegistrationActivity_you_have_made_too_many_incorrect_registration_lock_pin_attempts_please_try_again_in_a_day">شما چندین بار پین قفل ثبت‌نام را اشتباه وارد کردید. لطفاً بعد از یک روز دوباره تلاش کنید.</string>
<string name="RegistrationActivity_you_have_made_too_many_attempts_please_try_again_later">تلاش‌های ناموفق بیش از حد زیاد بودند. لطفاً بعداً دوباره امتحان کنید.</string>
<string name="RegistrationActivity_error_connecting_to_service">خطا در برقراری ارتباط با سرویس</string>
<string name="preferences_chats__backups">پشتیبان‌ها</string>
<string name="preferences_chats__backups">نسخه‌های پشتیبان</string>
<!-- Title text shown when Signal is locked and needs to be unlocked -->
<string name="prompt_passphrase_activity__unlock_signal">باز کردن قفل سیگنال</string>
<!-- Description text explaining how to unlock Signal -->
@@ -6288,8 +6298,8 @@
<string name="MyStorySettingsFragment__only_share_with_selected_people">تنها با افراد انتخاب‌شده اشتراک‌گذاری شود</string>
<!-- Summary of clickable option displaying how many people you have included to send to in your story -->
<plurals name="MyStorySettingsFragment__d_people">
<item quantity="one">%1$d نفر</item>
<item quantity="other">%1$d نفر</item>
<item quantity="one">%1$d viewer</item>
<item quantity="other">%1$d viewers</item>
</plurals>
<!-- My story privacy fine print about what the privacy settings are for -->
<string name="MyStorySettingsFragment__choose_who_can_view_your_story">انتخاب کنید چه کسی می‌تواند استوری شما را مشاهده کند. تغییرات روی استوری‌هایی که قبلاً ارسال کرده‌اید تأثیر نمی‌گذارد.</string>
@@ -7509,9 +7519,9 @@
<!-- Dialog title when a backup fails to be created -->
<string name="BackupAlertBottomSheet__backup_failed">پشتیبان‌گیری ناموفق بود</string>
<!-- Dialog text for when a backup fails to be created and ways to fix it -->
<string name="BackupAlertBottomSheet__an_error_occurred">An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support.</string>
<string name="BackupAlertBottomSheet__an_error_occurred">خطایی رخ داد و پشتیبان‌گیری شما کامل نشد. مطمئن شوید که از جدیدترین نسخه سیگنال استفاده می‌کنید و سپس دوباره امتحان کنید. اگر مشکل همچنان ادامه یافت، با پشتیبانی تماس بگیرید.</string>
<!-- Dialog action button that will allow you to check for any Signal version updates -->
<string name="BackupAlertBottomSheet__check_for_update">Check for update</string>
<string name="BackupAlertBottomSheet__check_for_update">جستجوی نسخه جدید</string>
<!-- BackupStatus -->
<!-- Status title when user does not have enough free space to download their media. Placeholder is required disk space. -->
@@ -7538,15 +7548,21 @@
<!-- Content description for x icon at the end of the linear progress indicator -->
<string name="BackupStatusRow__cancel_download">لغو دانلود</string>
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
<string name="BackupStatusRow__downloading_s_of_s_s">در حال دانلود: %1$s از %2$s (%3$s%%)</string>
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
<!-- Text displayed under progress bar when restoring media pauses for internet -->
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
<string name="BackupStatusRow__not_enough_space">فضای کافی برای دانلود نسخه پشتیبان وجود ندارد. برای ادامه، %1$s فضا خالی کنید.</string>
<!-- Text row label to skip download -->
<string name="BackupStatusRow__skip_download">رد کردن دانلود</string>
<!-- Text displayed when a backup could not be completed -->
<string name="BackupStatusRow__your_last_backup">Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again.</string>
<string name="BackupStatusRow__your_last_backup">آخرین پشتیبان‌گیری شما انجام نشد. مطمئن شوید که تلفن شما به Wi-Fi متصل است و با ضربه زدن روی «اکنون پشتیبان‌گیری شود»، دوباره امتحان کنید.</string>
<!-- Text displayed when a backup could not be completed and to check that they are on the latest version of Signal -->
<string name="BackupStatusRow__your_last_backup_latest_version">Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again.</string>
<string name="BackupStatusRow__your_last_backup_latest_version">آخرین پشتیبان‌گیری شما انجام نشد. مطمئن شوید که از جدیدترین نسخه سیگنال استفاده می‌کنید و سپس دوباره امتحان کنید.</string>
<!-- Text displayed in a row to learn more about why a backup failed -->
<string name="BackupStatusRow__learn_more">اطلاعات بیشتر</string>
@@ -7647,6 +7663,8 @@
<string name="RemoteBackupsSettingsFragment__payment_history">تاریخچه پرداخت</string>
<!-- Section header for backup information -->
<string name="RemoteBackupsSettingsFragment__backup_details">جزئیات پشتیبان</string>
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
<!-- Row label for backup size -->
<string name="RemoteBackupsSettingsFragment__backup_size">اندازه پشتیبان</string>
<!-- Row label for backup frequency -->
@@ -7753,7 +7771,14 @@
<!-- Button label to learn more about why subscription disappeared -->
<string name="RemoteBackupsSettingsFragment__learn_more">اطلاعات بیشتر</string>
<!-- Linear progress dialog text shown when creating a backup -->
<string name="RemoteBackupsSettingsFragment__processing_backup">Processing backup</string>
<string name="RemoteBackupsSettingsFragment__processing_backup">در حال پردازش نسخه پشتیبان</string>
<!-- Linear progress dialog text shown when preparing a backup -->
<string name="RemoteBackupsSettingsFragment__preparing_backup">Preparing backup…</string>
<!-- Linear progress dialog text shown when processing messages for backup. First placeholder is completed count, second is approximate total count, third is percent completed. -->
<plurals name="RemoteBackupsSettingsFragment__processing_d_of_d_d_messages">
<item quantity="one">Processing %1$d of %2$d (%3$d%%) message</item>
<item quantity="other">Processing %1$d of %2$d (%3$d%%) messages</item>
</plurals>
<!-- Displayed in row when backup is available for download and users subscription has expired. First placeholder is data size e.g. 12MB, second is days before expiration -->
<plurals name="RemoteBackupsSettingsFragment__you_have_s_of_backup_data">
<item quantity="one">شما %1$s داده پشتیبان دارید که در این دستگاه وجود ندارد. پس از پایان اشتراکتان تا %2$d روز دیگر، نسخه پشتیبان شما حذف خواهد شد.</item>
@@ -7774,8 +7799,8 @@
<string name="RemoteBackupsSettingsFragment__couldnt_turn_off_and_delete_backups">پشتیبان‌گیری خاموش و حذف نشد</string>
<!-- Dialog body for network error while trying to disable backups -->
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">خطای شبکه رخ داد. لطفاً اتصال اینترنت خود را بررسی و دوباره امتحان کنید.</string>
<!-- Progress message when backup file is being uploaded -->
<string name="RemoteBackupsSettingsFragment__uploading_messages">Uploading messages…</string>
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
<!-- SubscriptionNotFoundBottomSheet -->
<!-- Displayed as a bottom sheet title -->
@@ -7961,6 +7986,8 @@
<string name="RestoreViaQr_qr_code_scanned">در دستگاه قدیمی اسکن شده است</string>
<!-- Error button text to retry getting a qr code -->
<string name="RestoreViaQr_retry">تلاش مجدد</string>
<!-- Error message shown when register after receiving the provisioning message failed -->
<string name="RestoreViaQr_registration_error">An error occurred and your account couldnt be transferred. Try again by scanning the QR code again.</string>
<!-- Old device Transfer account screen title -->
<string name="TransferAccount_title">انتقال حساب کاربری</string>
@@ -7991,15 +8018,15 @@
<string name="RestoreCompleteBottomSheet_button">تأیید</string>
<!-- No Backup to Restore screen title -->
<string name="NoBackupToRestore_title">No Backup to Restore</string>
<string name="NoBackupToRestore_title">هیچ نسخه پشتیبانی برای بازیابی وجود ندارد</string>
<!-- No Backup to Restore screen subtitle -->
<string name="NoBackupToRestore_subtitle">Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device.</string>
<string name="NoBackupToRestore_subtitle">چون در حال انتقال از آیفون به اندروید هستید، تنها راه انتقال پیام‌ها و فایل‌های رسانه‌تان این است که «پشتیبان‌های سیگنال» را در دستگاه قدیمی‌تان فعال کنید.</string>
<!-- No Backup to Restore step 1 to enable backups on old device -->
<string name="NoBackupToRestore_step1">سیگنال را در دستگاه قدیمی خود باز کنید</string>
<string name="NoBackupToRestore_step1">سیگنال را در دستگاه قدیمی‌تان باز کنید</string>
<!-- No Backup to Restore step 2 to enable backups on old device -->
<string name="NoBackupToRestore_step2">Tap Settings &gt; Backups</string>
<string name="NoBackupToRestore_step2">روی «تنظیمات &gt; نسخه‌های پشتیبان» ضربه بزنید</string>
<!-- No Backup to Restore step 3 to enable backups on old device -->
<string name="NoBackupToRestore_step3">Enable backups and wait until your backup is complete</string>
<string name="NoBackupToRestore_step3">نسخه‌های پشتیبان را فعال کنید و صبر کنید تا پشتیبان‌گیری تکمیل شود</string>
<!-- No backup to Restore tonal cta to skip restore -->
<string name="NoBackupToRestore_skip_restore">رد کردن بازیابی</string>

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