mirror of
https://github.com/mollyim/mollyim-insider-android.git
synced 2025-05-13 05:40:53 +01:00
Merge branch 'molly-7.0'
This commit is contained in:
commit
d470ac9500
106 changed files with 606 additions and 215 deletions
|
@ -19,8 +19,8 @@ apply {
|
|||
from("fix-profm.gradle")
|
||||
}
|
||||
|
||||
val canonicalVersionCode = 1395
|
||||
val canonicalVersionName = "7.0.1"
|
||||
val canonicalVersionCode = 1396
|
||||
val canonicalVersionName = "7.0.2"
|
||||
val mollyRevision = 1
|
||||
|
||||
val postFixSize = 100
|
||||
|
|
|
@ -788,6 +788,17 @@ class RecipientTableTest_getAndPossiblyMerge {
|
|||
expectChangeNumberEvent()
|
||||
}
|
||||
|
||||
test("merge, e164 follows pni+aci") {
|
||||
given(E164_A, PNI_A, null)
|
||||
given(null, null, ACI_A)
|
||||
|
||||
process(null, PNI_A, ACI_A, pniVerified = true)
|
||||
|
||||
expect(E164_A, PNI_A, ACI_A)
|
||||
expectThreadMergeEvent(E164_A)
|
||||
expectPniVerified()
|
||||
}
|
||||
|
||||
test("local user, local e164 and aci provided, changeSelf=false, leave e164 alone") {
|
||||
given(E164_SELF, null, ACI_SELF)
|
||||
given(null, null, ACI_A)
|
||||
|
|
|
@ -40,7 +40,7 @@ class AliceClient(val serviceId: ServiceId, val e164: String, val trustRoot: ECK
|
|||
ApplicationDependencies.getIncomingMessageObserver()
|
||||
.processEnvelope(bufferedStore, envelope, serverDeliveredTimestamp)
|
||||
?.mapNotNull { it.run() }
|
||||
?.forEach { ApplicationDependencies.getJobManager().add(it) }
|
||||
?.forEach { it.enqueue() }
|
||||
|
||||
bufferedStore.flushToDisk()
|
||||
val end = System.currentTimeMillis()
|
||||
|
|
|
@ -109,8 +109,6 @@ class AppSettingsActivity : DSLSettingsActivity() {
|
|||
override fun onWillFinish() {
|
||||
if (wasConfigurationUpdated) {
|
||||
setResult(MainActivity.RESULT_CONFIG_CHANGED)
|
||||
} else {
|
||||
setResult(RESULT_OK)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -174,6 +174,48 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
|
|||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Log dump PreKey ServiceId-KeyIds"),
|
||||
onClick = {
|
||||
logPreKeyIds()
|
||||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Retry all jobs now"),
|
||||
summary = DSLSettingsText.from("Clear backoff intervals, app will restart"),
|
||||
onClick = {
|
||||
SimpleTask.run({
|
||||
JobDatabase.getInstance(ApplicationDependencies.getApplication()).debugResetBackoffInterval()
|
||||
}) {
|
||||
AppUtil.restart(requireContext())
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Delete all prekeys"),
|
||||
summary = DSLSettingsText.from("Deletes all signed/last-resort/one-time prekeys for both ACI and PNI accounts. WILL cause problems."),
|
||||
onClick = {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("Delete all prekeys?")
|
||||
.setMessage("Are you sure? This will delete all prekeys for both ACI and PNI accounts. This WILL cause problems.")
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
SignalDatabase.signedPreKeys.debugDeleteAll()
|
||||
SignalDatabase.oneTimePreKeys.debugDeleteAll()
|
||||
SignalDatabase.kyberPreKeys.debugDeleteAll()
|
||||
|
||||
Toast.makeText(requireContext(), "All prekeys deleted!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
)
|
||||
|
||||
dividerPref()
|
||||
|
||||
sectionHeaderPref(DSLSettingsText.from("Logging"))
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Clear all logs"),
|
||||
onClick = {
|
||||
|
@ -215,21 +257,10 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
|
|||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Log dump PreKey ServiceId-KeyIds"),
|
||||
title = DSLSettingsText.from("Clear local metrics"),
|
||||
summary = DSLSettingsText.from("Click to clear all local metrics state."),
|
||||
onClick = {
|
||||
logPreKeyIds()
|
||||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Retry all jobs now"),
|
||||
summary = DSLSettingsText.from("Clear backoff intervals, app will restart"),
|
||||
onClick = {
|
||||
SimpleTask.run({
|
||||
JobDatabase.getInstance(ApplicationDependencies.getApplication()).debugResetBackoffInterval()
|
||||
}) {
|
||||
AppUtil.restart(requireContext())
|
||||
}
|
||||
clearAllLocalMetricsState()
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -424,18 +455,6 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
|
|||
|
||||
dividerPref()
|
||||
|
||||
sectionHeaderPref(DSLSettingsText.from("Local Metrics"))
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Clear local metrics"),
|
||||
summary = DSLSettingsText.from("Click to clear all local metrics state."),
|
||||
onClick = {
|
||||
clearAllLocalMetricsState()
|
||||
}
|
||||
)
|
||||
|
||||
dividerPref()
|
||||
|
||||
sectionHeaderPref(DSLSettingsText.from("Group call server"))
|
||||
|
||||
radioPref(
|
||||
|
|
|
@ -304,7 +304,7 @@ class UsernameLinkSettingsViewModel : ViewModel() {
|
|||
}
|
||||
|
||||
// Draw the username
|
||||
val usernamePaint = Paint().apply {
|
||||
val usernamePaint = TextPaint().apply {
|
||||
color = state.qrCodeColorScheme.textColor.toArgb()
|
||||
textSize = usernameTextSize
|
||||
typeface = if (Build.VERSION.SDK_INT < 26) {
|
||||
|
@ -316,10 +316,18 @@ class UsernameLinkSettingsViewModel : ViewModel() {
|
|||
.build()
|
||||
}
|
||||
}
|
||||
val usernameBounds = Rect()
|
||||
usernamePaint.getTextBounds(state.username, 0, state.username.length, usernameBounds)
|
||||
|
||||
androidCanvas.drawText(state.username, (width / 2f) - (usernameBounds.width() / 2f), usernameVerticalPad + usernameBounds.height(), usernamePaint)
|
||||
val usernameMaxWidth = qrBorderWidth - borderSizeX * 2f
|
||||
val usernameLayout = StaticLayout(state.username, usernamePaint, usernameMaxWidth.toInt(), Layout.Alignment.ALIGN_CENTER, 1f, 0f, true)
|
||||
val usernameVerticalOffset = when (usernameLayout.lineCount) {
|
||||
1 -> 0f
|
||||
2 -> usernameTextSize / 2f
|
||||
else -> usernameTextSize
|
||||
}
|
||||
|
||||
androidCanvas.withTranslation(x = backgroundPadHorizontal + borderSizeX, y = usernameVerticalPad - usernameVerticalOffset) {
|
||||
usernameLayout.draw(this)
|
||||
}
|
||||
|
||||
// Draw the help text
|
||||
val helpTextPaint = TextPaint().apply {
|
||||
|
|
|
@ -9,16 +9,15 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.geometry.CornerRadius
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.BlendMode
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.graphics.PathEffect
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
@ -46,13 +45,17 @@ fun UsernameQrScanScreen(
|
|||
onQrResultHandled: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val path = remember { Path() }
|
||||
|
||||
when (qrScanResult) {
|
||||
QrScanResult.InvalidData -> {
|
||||
QrScanResultDialog(stringResource(R.string.UsernameLinkSettings_qr_result_invalid), onDismiss = onQrResultHandled)
|
||||
}
|
||||
|
||||
QrScanResult.NetworkError -> {
|
||||
QrScanResultDialog(stringResource(R.string.UsernameLinkSettings_qr_result_network_error), onDismiss = onQrResultHandled)
|
||||
}
|
||||
|
||||
is QrScanResult.NotFound -> {
|
||||
if (qrScanResult.username != null) {
|
||||
QrScanResultDialog(stringResource(R.string.UsernameLinkSettings_qr_result_not_found, qrScanResult.username), onDismiss = onQrResultHandled)
|
||||
|
@ -60,10 +63,12 @@ fun UsernameQrScanScreen(
|
|||
QrScanResultDialog(stringResource(R.string.UsernameLinkSettings_qr_result_not_found_no_username), onDismiss = onQrResultHandled)
|
||||
}
|
||||
}
|
||||
|
||||
is QrScanResult.Success -> {
|
||||
CommunicationActions.startConversation(LocalContext.current, qrScanResult.recipient, null)
|
||||
onQrResultHandled()
|
||||
}
|
||||
|
||||
null -> {}
|
||||
}
|
||||
|
||||
|
@ -88,7 +93,7 @@ fun UsernameQrScanScreen(
|
|||
.weight(1f, true)
|
||||
.drawWithContent {
|
||||
drawContent()
|
||||
drawQrCrosshair()
|
||||
drawQrCrosshair(path)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -117,33 +122,39 @@ private fun QrScanResultDialog(message: String, onDismiss: () -> Unit) {
|
|||
)
|
||||
}
|
||||
|
||||
private fun DrawScope.drawQrCrosshair() {
|
||||
private fun DrawScope.drawQrCrosshair(path: Path) {
|
||||
val crosshairWidth: Float = size.minDimension * 0.6f
|
||||
val clearWidth: Float = crosshairWidth * 0.75f
|
||||
val crosshairLineLength = crosshairWidth * 0.125f
|
||||
|
||||
// Draw a full white rounded rect...
|
||||
drawRoundRect(
|
||||
color = Color.White,
|
||||
topLeft = center - Offset(crosshairWidth / 2, crosshairWidth / 2),
|
||||
style = Stroke(width = 3.dp.toPx()),
|
||||
size = Size(crosshairWidth, crosshairWidth),
|
||||
cornerRadius = CornerRadius(10.dp.toPx(), 10.dp.toPx())
|
||||
)
|
||||
val topLeft = center - Offset(crosshairWidth / 2, crosshairWidth / 2)
|
||||
val topRight = center + Offset(crosshairWidth / 2, -crosshairWidth / 2)
|
||||
val bottomRight = center + Offset(crosshairWidth / 2, crosshairWidth / 2)
|
||||
val bottomLeft = center + Offset(-crosshairWidth / 2, crosshairWidth / 2)
|
||||
|
||||
// ...then cut out the middle parts with BlendMode.Clear to leave us with just the corners
|
||||
drawRect(
|
||||
color = Color.White,
|
||||
topLeft = Offset(center.x - clearWidth / 2, 0f),
|
||||
style = Fill,
|
||||
size = Size(clearWidth, size.height),
|
||||
blendMode = BlendMode.Clear
|
||||
)
|
||||
path.reset()
|
||||
|
||||
drawRect(
|
||||
drawPath(
|
||||
path = path.apply {
|
||||
moveTo(topLeft.x, topLeft.y + crosshairLineLength)
|
||||
lineTo(topLeft.x, topLeft.y)
|
||||
lineTo(topLeft.x + crosshairLineLength, topLeft.y)
|
||||
|
||||
moveTo(topRight.x - crosshairLineLength, topRight.y)
|
||||
lineTo(topRight.x, topRight.y)
|
||||
lineTo(topRight.x, topRight.y + crosshairLineLength)
|
||||
|
||||
moveTo(bottomRight.x, bottomRight.y - crosshairLineLength)
|
||||
lineTo(bottomRight.x, bottomRight.y)
|
||||
lineTo(bottomRight.x - crosshairLineLength, bottomRight.y)
|
||||
|
||||
moveTo(bottomLeft.x + crosshairLineLength, bottomLeft.y)
|
||||
lineTo(bottomLeft.x, bottomLeft.y)
|
||||
lineTo(bottomLeft.x, bottomLeft.y - crosshairLineLength)
|
||||
},
|
||||
color = Color.White,
|
||||
topLeft = Offset(0f, center.y - clearWidth / 2),
|
||||
style = Fill,
|
||||
size = Size(size.width, clearWidth),
|
||||
blendMode = BlendMode.Clear
|
||||
style = Stroke(
|
||||
width = 3.dp.toPx(),
|
||||
pathEffect = PathEffect.cornerPathEffect(10.dp.toPx())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database
|
|||
|
||||
import android.content.Context
|
||||
import org.signal.core.util.delete
|
||||
import org.signal.core.util.deleteAll
|
||||
import org.signal.core.util.exists
|
||||
import org.signal.core.util.insertInto
|
||||
import org.signal.core.util.logging.Log
|
||||
|
@ -171,6 +172,10 @@ class KyberPreKeyTable(context: Context, databaseHelper: SignalDatabase) : Datab
|
|||
Log.i(TAG, "Deleted $count stale one-time EC prekeys.")
|
||||
}
|
||||
|
||||
fun debugDeleteAll() {
|
||||
writableDatabase.deleteAll(OneTimePreKeyTable.TABLE_NAME)
|
||||
}
|
||||
|
||||
data class KyberPreKey(
|
||||
val record: KyberPreKeyRecord,
|
||||
val lastResort: Boolean
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.core.content.contentValuesOf
|
|||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.SqlUtil
|
||||
import org.signal.core.util.delete
|
||||
import org.signal.core.util.deleteAll
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.requireNonNullString
|
||||
import org.signal.core.util.update
|
||||
|
@ -115,6 +116,10 @@ class OneTimePreKeyTable(context: Context, databaseHelper: SignalDatabase) : Dat
|
|||
Log.i(TAG, "Deleted $count stale one-time EC prekeys.")
|
||||
}
|
||||
|
||||
fun debugDeleteAll() {
|
||||
writableDatabase.deleteAll(TABLE_NAME)
|
||||
}
|
||||
|
||||
private fun ServiceId.toAccountId(): String {
|
||||
return when (this) {
|
||||
is ServiceId.ACI -> this.toString()
|
||||
|
|
|
@ -2942,10 +2942,10 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||
primaryId = data.byAci,
|
||||
secondaryId = data.byPni
|
||||
)
|
||||
} else if (data.pniRecord.aci == null && data.pniRecord.e164 == data.e164) {
|
||||
} else if (data.pniRecord.aci == null && (data.e164 == null || data.pniRecord.e164 == data.e164)) {
|
||||
// The PNI record also has the E164 on it with no ACI. We're going to be stealing all of it's fields,
|
||||
// so this is basically a merge with a little bit of extra prep.
|
||||
breadCrumbs += "PniRecordHasMatchingE164AndNoAci"
|
||||
breadCrumbs += "PniRecordHasNoAci"
|
||||
|
||||
if (data.aciRecord.pni != null) {
|
||||
operations += PnpOperation.RemovePni(data.byAci)
|
||||
|
@ -2970,10 +2970,10 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||
secondaryId = data.byPni
|
||||
)
|
||||
} else {
|
||||
// The PNI record either has an ACI or a non-matching e164, meaning we need to steal what we need and leave the rest behind
|
||||
// The PNI record has a different ACI, meaning we need to steal what we need and leave the rest behind
|
||||
|
||||
breadCrumbs += if (data.pniRecord.aci != null && data.pniRecord.e164 != data.e164) {
|
||||
"PniRecordHasAciAndNonMatchingE164"
|
||||
"PniRecordHasAci"
|
||||
} else if (data.pniRecord.aci != null) {
|
||||
"PniRecordHasAci"
|
||||
} else {
|
||||
|
@ -4054,7 +4054,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||
PROFILE_FAMILY_NAME to null,
|
||||
PROFILE_JOINED_NAME to null,
|
||||
LAST_PROFILE_FETCH to 0,
|
||||
PROFILE_AVATAR to null
|
||||
PROFILE_AVATAR to null,
|
||||
PROFILE_SHARING to 0
|
||||
)
|
||||
.run {
|
||||
if (recipientId == null) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import androidx.core.content.contentValuesOf
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.SqlUtil
|
||||
import org.signal.core.util.deleteAll
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.requireInt
|
||||
import org.signal.core.util.requireLong
|
||||
|
@ -103,6 +104,10 @@ class SignedPreKeyTable(context: Context, databaseHelper: SignalDatabase) : Data
|
|||
writableDatabase.delete(TABLE_NAME, "$ACCOUNT_ID = ? AND $KEY_ID = ?", SqlUtil.buildArgs(serviceId.toAccountId(), keyId))
|
||||
}
|
||||
|
||||
fun debugDeleteAll() {
|
||||
writableDatabase.deleteAll(OneTimePreKeyTable.TABLE_NAME)
|
||||
}
|
||||
|
||||
private fun ServiceId.toAccountId(): String {
|
||||
return when (this) {
|
||||
is ServiceId.ACI -> this.toString()
|
||||
|
|
|
@ -1541,7 +1541,7 @@ final class GroupsV2UpdateMessageProducer {
|
|||
String beforeChunk = template.substring(startIndex, nearestPosition);
|
||||
|
||||
builder.append(beforeChunk);
|
||||
builder.append(SpanUtil.clickable(Recipient.resolved(recipientId).getDisplayName(context), ContextCompat.getColor(context, R.color.conversation_item_update_text_color), v -> {
|
||||
builder.append(SpanUtil.clickable(Recipient.resolved(recipientId).getDisplayNameOrUsername(context), ContextCompat.getColor(context, R.color.conversation_item_update_text_color), v -> {
|
||||
if (!recipientId.isUnknown() && clickHandler != null) {
|
||||
clickHandler.accept(recipientId);
|
||||
}
|
||||
|
|
|
@ -76,6 +76,15 @@ class JobController {
|
|||
notifyAll();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
void submitNewJobChains(@NonNull List<List<List<Job>>> chains) {
|
||||
synchronized (this) {
|
||||
for (List<List<Job>> chain : chains) {
|
||||
submitNewJobChain(chain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
void submitNewJobChain(@NonNull List<List<Job>> chain) {
|
||||
synchronized (this) {
|
||||
|
|
|
@ -36,6 +36,7 @@ import java.util.concurrent.RejectedExecutionException;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Allows the scheduling of durable jobs that will be run as early as possible.
|
||||
|
@ -225,6 +226,24 @@ public class JobManager implements ConstraintObserver.Notifier {
|
|||
});
|
||||
}
|
||||
|
||||
public void addAllChains(@NonNull List<JobManager.Chain> chains) {
|
||||
if (chains.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Chain chain : chains) {
|
||||
for (List<Job> jobList : chain.getJobListChain()) {
|
||||
for (Job job : jobList) {
|
||||
jobTracker.onStateChange(job, JobTracker.JobState.PENDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runOnExecutor(() -> {
|
||||
jobController.submitNewJobChains(chains.stream().map(Chain::getJobListChain).collect(Collectors.toList()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins the creation of a job chain with a single job.
|
||||
* @see Chain
|
||||
|
|
|
@ -98,10 +98,11 @@ class PnpInitializeDevicesJob private constructor(parameters: Parameters) : Base
|
|||
|
||||
try {
|
||||
Log.i(TAG, "Initializing PNI for linked devices")
|
||||
initializeDevices(e164)
|
||||
val result: VerifyResponseWithoutKbs = initializeDevices(e164)
|
||||
.map(::VerifyResponseWithoutKbs)
|
||||
.safeBlockingGet()
|
||||
.resultOrThrow
|
||||
|
||||
result.error?.let { throw it }
|
||||
} catch (e: InterruptedException) {
|
||||
throw IOException("Retry", e)
|
||||
} catch (t: Throwable) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.jobs
|
|||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.roundedString
|
||||
import org.signal.libsignal.protocol.state.KyberPreKeyRecord
|
||||
import org.signal.libsignal.protocol.state.PreKeyRecord
|
||||
import org.signal.libsignal.protocol.state.SignalProtocolStore
|
||||
|
@ -11,7 +12,9 @@ import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore
|
|||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.jobs.protos.PreKeysSyncJobData
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountDataStore
|
||||
import org.whispersystems.signalservice.api.account.PreKeyUpload
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
|
@ -34,7 +37,10 @@ import kotlin.time.DurationUnit
|
|||
* It will also rotate/create last-resort kyber prekeys for both ACI and PNI identities, as well as ensure
|
||||
* that the user has a sufficient number of one-time kyber prekeys available on the service.
|
||||
*/
|
||||
class PreKeysSyncJob private constructor(parameters: Parameters) : BaseJob(parameters) {
|
||||
class PreKeysSyncJob private constructor(
|
||||
parameters: Parameters,
|
||||
private val forceRotationRequested: Boolean
|
||||
) : BaseJob(parameters) {
|
||||
|
||||
companion object {
|
||||
const val KEY = "PreKeysSyncJob"
|
||||
|
@ -52,9 +58,13 @@ class PreKeysSyncJob private constructor(parameters: Parameters) : BaseJob(param
|
|||
@JvmField
|
||||
val MAXIMUM_ALLOWED_SIGNED_PREKEY_AGE = 14.days.inWholeMilliseconds
|
||||
|
||||
/**
|
||||
* @param forceRotationRequested If true, this will force the rotation of all keys, provided we haven't already done a forced refresh recently.
|
||||
*/
|
||||
@JvmOverloads
|
||||
@JvmStatic
|
||||
fun create(): PreKeysSyncJob {
|
||||
return PreKeysSyncJob()
|
||||
fun create(forceRotationRequested: Boolean = false): PreKeysSyncJob {
|
||||
return PreKeysSyncJob(forceRotationRequested)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
@ -87,19 +97,22 @@ class PreKeysSyncJob private constructor(parameters: Parameters) : BaseJob(param
|
|||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
constructor() : this(
|
||||
constructor(forceRotation: Boolean = false) : this(
|
||||
Parameters.Builder()
|
||||
.setQueue("PreKeysSyncJob")
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setMaxInstancesForFactory(1)
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(30))
|
||||
.build()
|
||||
.build(),
|
||||
forceRotation
|
||||
)
|
||||
|
||||
override fun getFactoryKey(): String = KEY
|
||||
|
||||
override fun serialize(): ByteArray? = null
|
||||
override fun serialize(): ByteArray {
|
||||
return PreKeysSyncJobData(forceRotationRequested).encode()
|
||||
}
|
||||
|
||||
override fun onRun() {
|
||||
if (!SignalStore.account().isRegistered || SignalStore.account().aci == null || SignalStore.account().pni == null) {
|
||||
|
@ -107,12 +120,30 @@ class PreKeysSyncJob private constructor(parameters: Parameters) : BaseJob(param
|
|||
return
|
||||
}
|
||||
|
||||
syncPreKeys(ServiceIdType.ACI, SignalStore.account().aci, ApplicationDependencies.getProtocolStore().aci(), SignalStore.account().aciPreKeys)
|
||||
syncPreKeys(ServiceIdType.PNI, SignalStore.account().pni, ApplicationDependencies.getProtocolStore().pni(), SignalStore.account().pniPreKeys)
|
||||
val forceRotation = if (forceRotationRequested) {
|
||||
val timeSinceLastForcedRotation = System.currentTimeMillis() - SignalStore.misc().lastForcedPreKeyRefresh
|
||||
// We check < 0 in case someone changed their clock and had a bad value set
|
||||
timeSinceLastForcedRotation > FeatureFlags.preKeyForceRefreshInterval() || timeSinceLastForcedRotation < 0
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
if (forceRotation) {
|
||||
warn(TAG, "Forcing prekey rotation.")
|
||||
} else if (forceRotationRequested) {
|
||||
warn(TAG, "Forced prekey rotation was requested, but we already did a forced refresh ${System.currentTimeMillis() - SignalStore.misc().lastForcedPreKeyRefresh} ms ago. Ignoring.")
|
||||
}
|
||||
|
||||
syncPreKeys(ServiceIdType.ACI, SignalStore.account().aci, ApplicationDependencies.getProtocolStore().aci(), SignalStore.account().aciPreKeys, forceRotation)
|
||||
syncPreKeys(ServiceIdType.PNI, SignalStore.account().pni, ApplicationDependencies.getProtocolStore().pni(), SignalStore.account().pniPreKeys, forceRotation)
|
||||
SignalStore.misc().lastFullPrekeyRefreshTime = System.currentTimeMillis()
|
||||
|
||||
if (forceRotation) {
|
||||
SignalStore.misc().lastForcedPreKeyRefresh = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
|
||||
private fun syncPreKeys(serviceIdType: ServiceIdType, serviceId: ServiceId?, protocolStore: SignalServiceAccountDataStore, metadataStore: PreKeyMetadataStore) {
|
||||
private fun syncPreKeys(serviceIdType: ServiceIdType, serviceId: ServiceId?, protocolStore: SignalServiceAccountDataStore, metadataStore: PreKeyMetadataStore, forceRotation: Boolean) {
|
||||
if (serviceId == null) {
|
||||
warn(TAG, serviceIdType, "AccountId not set!")
|
||||
return
|
||||
|
@ -121,20 +152,20 @@ class PreKeysSyncJob private constructor(parameters: Parameters) : BaseJob(param
|
|||
val accountManager = ApplicationDependencies.getSignalServiceAccountManager()
|
||||
val availablePreKeyCounts: OneTimePreKeyCounts = accountManager.getPreKeyCounts(serviceIdType)
|
||||
|
||||
val signedPreKeyToUpload: SignedPreKeyRecord? = signedPreKeyUploadIfNeeded(serviceIdType, protocolStore, metadataStore)
|
||||
val signedPreKeyToUpload: SignedPreKeyRecord? = signedPreKeyUploadIfNeeded(serviceIdType, protocolStore, metadataStore, forceRotation)
|
||||
|
||||
val oneTimeEcPreKeysToUpload: List<PreKeyRecord>? = if (availablePreKeyCounts.ecCount < ONE_TIME_PREKEY_MINIMUM) {
|
||||
log(serviceIdType, "There are ${availablePreKeyCounts.ecCount} one-time EC prekeys available, which is less than our threshold. Need more.")
|
||||
val oneTimeEcPreKeysToUpload: List<PreKeyRecord>? = if (forceRotation || availablePreKeyCounts.ecCount < ONE_TIME_PREKEY_MINIMUM) {
|
||||
log(serviceIdType, "There are ${availablePreKeyCounts.ecCount} one-time EC prekeys available, which is less than our threshold. Need more. (Forced: $forceRotation)")
|
||||
PreKeyUtil.generateAndStoreOneTimeEcPreKeys(protocolStore, metadataStore)
|
||||
} else {
|
||||
log(serviceIdType, "There are ${availablePreKeyCounts.ecCount} one-time EC prekeys available, which is enough.")
|
||||
null
|
||||
}
|
||||
|
||||
val lastResortKyberPreKeyToUpload: KyberPreKeyRecord? = lastResortKyberPreKeyUploadIfNeeded(serviceIdType, protocolStore, metadataStore)
|
||||
val lastResortKyberPreKeyToUpload: KyberPreKeyRecord? = lastResortKyberPreKeyUploadIfNeeded(serviceIdType, protocolStore, metadataStore, forceRotation)
|
||||
|
||||
val oneTimeKyberPreKeysToUpload: List<KyberPreKeyRecord>? = if (availablePreKeyCounts.kyberCount < ONE_TIME_PREKEY_MINIMUM) {
|
||||
log(serviceIdType, "There are ${availablePreKeyCounts.kyberCount} one-time kyber prekeys available, which is less than our threshold. Need more.")
|
||||
val oneTimeKyberPreKeysToUpload: List<KyberPreKeyRecord>? = if (forceRotation || availablePreKeyCounts.kyberCount < ONE_TIME_PREKEY_MINIMUM) {
|
||||
log(serviceIdType, "There are ${availablePreKeyCounts.kyberCount} one-time kyber prekeys available, which is less than our threshold. Need more. (Forced: $forceRotation)")
|
||||
PreKeyUtil.generateAndStoreOneTimeKyberPreKeys(protocolStore, metadataStore)
|
||||
} else {
|
||||
log(serviceIdType, "There are ${availablePreKeyCounts.kyberCount} one-time kyber prekeys available, which is enough.")
|
||||
|
@ -183,28 +214,28 @@ class PreKeysSyncJob private constructor(parameters: Parameters) : BaseJob(param
|
|||
PreKeyUtil.cleanOneTimePreKeys(protocolStore)
|
||||
}
|
||||
|
||||
private fun signedPreKeyUploadIfNeeded(serviceIdType: ServiceIdType, protocolStore: SignalProtocolStore, metadataStore: PreKeyMetadataStore): SignedPreKeyRecord? {
|
||||
private fun signedPreKeyUploadIfNeeded(serviceIdType: ServiceIdType, protocolStore: SignalProtocolStore, metadataStore: PreKeyMetadataStore, forceRotation: Boolean): SignedPreKeyRecord? {
|
||||
val signedPreKeyRegistered = metadataStore.isSignedPreKeyRegistered && metadataStore.activeSignedPreKeyId >= 0
|
||||
val timeSinceLastSignedPreKeyRotation = System.currentTimeMillis() - metadataStore.lastSignedPreKeyRotationTime
|
||||
|
||||
return if (!signedPreKeyRegistered || timeSinceLastSignedPreKeyRotation >= REFRESH_INTERVAL || timeSinceLastSignedPreKeyRotation < 0) {
|
||||
log(serviceIdType, "Rotating signed prekey. SignedPreKeyRegistered: $signedPreKeyRegistered, TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS)} days)")
|
||||
return if (forceRotation || !signedPreKeyRegistered || timeSinceLastSignedPreKeyRotation >= REFRESH_INTERVAL || timeSinceLastSignedPreKeyRotation < 0) {
|
||||
log(serviceIdType, "Rotating signed prekey. ForceRotation: $forceRotation, SignedPreKeyRegistered: $signedPreKeyRegistered, TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS).roundedString(2)} days)")
|
||||
PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore)
|
||||
} else {
|
||||
log(serviceIdType, "No need to rotate signed prekey. TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS)} days)")
|
||||
log(serviceIdType, "No need to rotate signed prekey. TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS).roundedString(2)} days)")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun lastResortKyberPreKeyUploadIfNeeded(serviceIdType: ServiceIdType, protocolStore: SignalServiceAccountDataStore, metadataStore: PreKeyMetadataStore): KyberPreKeyRecord? {
|
||||
private fun lastResortKyberPreKeyUploadIfNeeded(serviceIdType: ServiceIdType, protocolStore: SignalServiceAccountDataStore, metadataStore: PreKeyMetadataStore, forceRotation: Boolean): KyberPreKeyRecord? {
|
||||
val lastResortRegistered = metadataStore.lastResortKyberPreKeyId >= 0
|
||||
val timeSinceLastSignedPreKeyRotation = System.currentTimeMillis() - metadataStore.lastResortKyberPreKeyRotationTime
|
||||
|
||||
return if (!lastResortRegistered || timeSinceLastSignedPreKeyRotation >= REFRESH_INTERVAL || timeSinceLastSignedPreKeyRotation < 0) {
|
||||
log(serviceIdType, "Rotating last-resort kyber prekey. TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS)} days)")
|
||||
return if (forceRotation || !lastResortRegistered || timeSinceLastSignedPreKeyRotation >= REFRESH_INTERVAL || timeSinceLastSignedPreKeyRotation < 0) {
|
||||
log(serviceIdType, "Rotating last-resort kyber prekey. ForceRotation: $forceRotation, TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS).roundedString(2)} days)")
|
||||
PreKeyUtil.generateAndStoreLastResortKyberPreKey(protocolStore, metadataStore)
|
||||
} else {
|
||||
log(serviceIdType, "No need to rotate last-resort kyber prekey. TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS)} days)")
|
||||
log(serviceIdType, "No need to rotate last-resort kyber prekey. TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS).roundedString(2)} days)")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
@ -225,7 +256,10 @@ class PreKeysSyncJob private constructor(parameters: Parameters) : BaseJob(param
|
|||
|
||||
class Factory : Job.Factory<PreKeysSyncJob> {
|
||||
override fun create(parameters: Parameters, serializedData: ByteArray?): PreKeysSyncJob {
|
||||
return PreKeysSyncJob(parameters)
|
||||
return serializedData?.let {
|
||||
val data = PreKeysSyncJobData.ADAPTER.decode(serializedData)
|
||||
PreKeysSyncJob(parameters, data.forceRefreshRequested)
|
||||
} ?: PreKeysSyncJob(parameters, forceRotationRequested = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -370,9 +370,12 @@ class RetrieveProfileJob private constructor(parameters: Parameters, private val
|
|||
}
|
||||
|
||||
if (writeChangeEvent || localDisplayName.isEmpty()) {
|
||||
ApplicationDependencies.getDatabaseObserver().notifyConversationListListeners()
|
||||
val threadId = SignalDatabase.threads.getThreadIdFor(recipient.id)
|
||||
if (threadId != null) {
|
||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, forConversation(threadId))
|
||||
SignalDatabase.runPostSuccessfulTransaction {
|
||||
ApplicationDependencies.getMessageNotifier().updateNotification(context, forConversation(threadId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ public final class MiscellaneousValues extends SignalStoreValues {
|
|||
private static final String SERVER_TIME_OFFSET = "misc.server_time_offset";
|
||||
private static final String LAST_SERVER_TIME_OFFSET_UPDATE = "misc.last_server_time_offset_update";
|
||||
private static final String NEEDS_USERNAME_RESTORE = "misc.needs_username_restore";
|
||||
private static final String LAST_FORCED_PREKEY_REFRESH = "misc.last_forced_prekey_refresh";
|
||||
|
||||
MiscellaneousValues(@NonNull KeyValueStore store) {
|
||||
super(store);
|
||||
|
@ -339,4 +340,18 @@ public final class MiscellaneousValues extends SignalStoreValues {
|
|||
public void setNeedsUsernameRestore(boolean value) {
|
||||
putBoolean(NEEDS_USERNAME_RESTORE, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last time we successfully completed a forced prekey refresh.
|
||||
*/
|
||||
public void setLastForcedPreKeyRefresh(long time) {
|
||||
putLong(LAST_FORCED_PREKEY_REFRESH, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last time we successfully completed a forced prekey refresh.
|
||||
*/
|
||||
public long getLastForcedPreKeyRefresh() {
|
||||
return getLong(LAST_FORCED_PREKEY_REFRESH, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId
|
|||
import org.thoughtcrime.securesms.service.KeyCachingService
|
||||
import org.thoughtcrime.securesms.util.AppForegroundObserver
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics
|
||||
import org.thoughtcrime.securesms.util.asChain
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
|
||||
import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException
|
||||
|
@ -309,7 +310,7 @@ class IncomingMessageObserver(private val context: Application) {
|
|||
is MessageDecryptor.Result.Success -> {
|
||||
val job = PushProcessMessageJob.processOrDefer(messageContentProcessor, result, localReceiveMetric)
|
||||
if (job != null) {
|
||||
return result.followUpOperations + FollowUpOperation { job }
|
||||
return result.followUpOperations + FollowUpOperation { job.asChain() }
|
||||
}
|
||||
}
|
||||
is MessageDecryptor.Result.Error -> {
|
||||
|
@ -318,7 +319,7 @@ class IncomingMessageObserver(private val context: Application) {
|
|||
result.toMessageState(),
|
||||
result.errorMetadata.toExceptionMetadata(),
|
||||
result.envelope.timestamp!!
|
||||
)
|
||||
).asChain()
|
||||
}
|
||||
}
|
||||
is MessageDecryptor.Result.Ignore -> {
|
||||
|
@ -419,7 +420,7 @@ class IncomingMessageObserver(private val context: Application) {
|
|||
if (followUpOperations != null) {
|
||||
Log.d(TAG, "Running ${followUpOperations.size} follow-up operations...")
|
||||
val jobs = followUpOperations.mapNotNull { it.run() }
|
||||
ApplicationDependencies.getJobManager().addAll(jobs)
|
||||
ApplicationDependencies.getJobManager().addAllChains(jobs)
|
||||
}
|
||||
|
||||
signalWebSocket.sendAck(response)
|
||||
|
|
|
@ -668,12 +668,16 @@ open class MessageContentProcessor(private val context: Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if (decryptionErrorMessage.ratchetKey.isPresent &&
|
||||
ratchetKeyMatches(requester, metadata.sourceDeviceId, decryptionErrorMessage.ratchetKey.get())
|
||||
) {
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-I] Ratchet key matches. Archiving the session.")
|
||||
ApplicationDependencies.getProtocolStore().aci().sessions().archiveSession(requester.requireServiceId(), metadata.sourceDeviceId)
|
||||
archivedSession = true
|
||||
if (decryptionErrorMessage.ratchetKey.isPresent) {
|
||||
if (ratchetKeyMatches(requester, metadata.sourceDeviceId, decryptionErrorMessage.ratchetKey.get())) {
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-I] Ratchet key matches. Archiving the session.")
|
||||
ApplicationDependencies.getProtocolStore().aci().sessions().archiveSession(requester.requireServiceId(), metadata.sourceDeviceId)
|
||||
archivedSession = true
|
||||
} else {
|
||||
log(envelope.timestamp!!, "[RetryReceipt-I] Ratchet key does not match. Leaving the session as-is.")
|
||||
}
|
||||
} else {
|
||||
warn(envelope.timestamp!!, "[RetryReceipt-I] Missing ratchet key! Can't archive session.")
|
||||
}
|
||||
|
||||
if (messageLogEntry != null) {
|
||||
|
|
|
@ -37,7 +37,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase
|
|||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.groups.BadGroupIdException
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager
|
||||
import org.thoughtcrime.securesms.jobs.AutomaticSessionResetJob
|
||||
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob
|
||||
import org.thoughtcrime.securesms.jobs.SendRetryReceiptJob
|
||||
|
@ -50,6 +50,8 @@ import org.thoughtcrime.securesms.notifications.NotificationIds
|
|||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.thoughtcrime.securesms.util.LRUCache
|
||||
import org.thoughtcrime.securesms.util.asChain
|
||||
import org.whispersystems.signalservice.api.InvalidMessageStructureException
|
||||
import org.whispersystems.signalservice.api.crypto.ContentHint
|
||||
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
|
||||
|
@ -77,6 +79,8 @@ object MessageDecryptor {
|
|||
|
||||
private val TAG = Log.tag(MessageDecryptor::class.java)
|
||||
|
||||
private val decryptionErrorCounts: MutableMap<RecipientId, DecryptionErrorCount> = LRUCache(100)
|
||||
|
||||
/**
|
||||
* Decrypts an envelope and provides a [Result]. This method has side effects, but all of them are limited to [SignalDatabase].
|
||||
* That means that this operation should be atomic when performed within a transaction.
|
||||
|
@ -125,8 +129,9 @@ object MessageDecryptor {
|
|||
val followUpOperations: MutableList<FollowUpOperation> = mutableListOf()
|
||||
|
||||
if (envelope.type == Envelope.Type.PREKEY_BUNDLE) {
|
||||
Log.i(TAG, "${logPrefix(envelope)} Prekey message. Scheduling a prekey sync job.")
|
||||
followUpOperations += FollowUpOperation {
|
||||
PreKeysSyncJob.create()
|
||||
PreKeysSyncJob.create().asChain()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +224,7 @@ object MessageDecryptor {
|
|||
|
||||
followUpOperations += FollowUpOperation {
|
||||
val sender: Recipient = Recipient.external(context, e.sender)
|
||||
AutomaticSessionResetJob(sender.id, e.senderDevice, envelope.timestamp!!)
|
||||
AutomaticSessionResetJob(sender.id, e.senderDevice, envelope.timestamp!!).asChain()
|
||||
}
|
||||
|
||||
Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList())
|
||||
|
@ -277,36 +282,70 @@ object MessageDecryptor {
|
|||
val senderDevice: Int = protocolException.senderDevice
|
||||
val receivedTimestamp: Long = System.currentTimeMillis()
|
||||
val sender: Recipient = Recipient.external(context, protocolException.sender)
|
||||
val senderServiceId: ServiceId? = ServiceId.parseOrNull(protocolException.sender)
|
||||
|
||||
if (sender.isSelf) {
|
||||
Log.w(TAG, "${logPrefix(envelope)} Decryption error for a sync message! Enqueuing a session reset job.")
|
||||
Log.w(TAG, "${logPrefix(envelope)} Decryption error for a sync message! Enqueuing a session reset job.", true)
|
||||
|
||||
followUpOperations += FollowUpOperation {
|
||||
AutomaticSessionResetJob(sender.id, senderDevice, envelope.timestamp!!)
|
||||
AutomaticSessionResetJob(sender.id, senderDevice, envelope.timestamp!!).asChain()
|
||||
}
|
||||
|
||||
return Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations)
|
||||
}
|
||||
|
||||
val errorCount: DecryptionErrorCount = decryptionErrorCounts.getOrPut(sender.id) { DecryptionErrorCount(count = 0, lastReceivedTime = 0) }
|
||||
val timeSinceLastError = receivedTimestamp - errorCount.lastReceivedTime
|
||||
if (timeSinceLastError > FeatureFlags.retryReceiptMaxCountResetAge() && errorCount.count > 0) {
|
||||
Log.i(TAG, "${logPrefix(envelope, senderServiceId)} Resetting decryption error count for ${sender.id} because it has been $timeSinceLastError ms since the last error.", true)
|
||||
errorCount.count = 0
|
||||
}
|
||||
|
||||
errorCount.count++
|
||||
errorCount.lastReceivedTime = receivedTimestamp
|
||||
|
||||
if (errorCount.count > FeatureFlags.retryReceiptMaxCount()) {
|
||||
Log.w(TAG, "${logPrefix(envelope, senderServiceId)} This is error number ${errorCount.count} from ${sender.id}, which is greater than the maximum of ${FeatureFlags.retryReceiptMaxCount()}. Ignoring.", true)
|
||||
|
||||
if (contentHint == ContentHint.IMPLICIT) {
|
||||
Log.w(TAG, "${logPrefix(envelope, senderServiceId)} The content hint is $contentHint, so no error message is needed.", true)
|
||||
Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations)
|
||||
} else {
|
||||
Log.w(TAG, "${logPrefix(envelope, senderServiceId)} The content hint is $contentHint, so we need to insert an error right away.", true)
|
||||
return Result.DecryptionError(envelope, serverDeliveredTimestamp, protocolException.toErrorMetadata(), followUpOperations.toUnmodifiableList())
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "${logPrefix(envelope, senderServiceId)} This is error number ${errorCount.count} from ${sender.id}.${if (errorCount.count > 1) " It has been $timeSinceLastError ms since the last error." else "" }", true)
|
||||
}
|
||||
|
||||
followUpOperations += FollowUpOperation {
|
||||
buildSendRetryReceiptJob(envelope, protocolException, sender)
|
||||
val retryJob = buildSendRetryReceiptJob(envelope, protocolException, sender)
|
||||
|
||||
// Note: if the message is sealed sender, it's envelope type will be UNIDENTIFIED_SENDER. The only way we can currently check if the error is
|
||||
// prekey-related in that situation is using a string match.
|
||||
if (envelope.type == Envelope.Type.PREKEY_BUNDLE || protocolException.message?.lowercase()?.contains("prekey") == true) {
|
||||
Log.w(TAG, "${logPrefix(envelope, senderServiceId)} Got a decryption error on a prekey message. Forcing a prekey rotation before requesting the retry.", true)
|
||||
PreKeysSyncJob.create(forceRotationRequested = true).asChain().then(retryJob)
|
||||
} else {
|
||||
retryJob.asChain()
|
||||
}
|
||||
}
|
||||
|
||||
return when (contentHint) {
|
||||
ContentHint.DEFAULT -> {
|
||||
Log.w(TAG, "${logPrefix(envelope)} The content hint is $contentHint, so we need to insert an error right away.", true)
|
||||
Log.w(TAG, "${logPrefix(envelope, senderServiceId)} The content hint is $contentHint, so we need to insert an error right away.", true)
|
||||
Result.DecryptionError(envelope, serverDeliveredTimestamp, protocolException.toErrorMetadata(), followUpOperations.toUnmodifiableList())
|
||||
}
|
||||
|
||||
ContentHint.RESENDABLE -> {
|
||||
Log.w(TAG, "${logPrefix(envelope)} The content hint is $contentHint, so we can try to resend the message.", true)
|
||||
Log.w(TAG, "${logPrefix(envelope, senderServiceId)} The content hint is $contentHint, so we can try to resend the message.", true)
|
||||
|
||||
followUpOperations += FollowUpOperation {
|
||||
val groupId: GroupId? = protocolException.parseGroupId(envelope)
|
||||
|
||||
val threadId: Long? = if (groupId != null) {
|
||||
if (SignalDatabase.groups.getGroup(groupId).isAbsent()) {
|
||||
Log.w(TAG, "${logPrefix(envelope)} No group found for $groupId! Not inserting a retry receipt.")
|
||||
Log.w(TAG, "${logPrefix(envelope, senderServiceId)} No group found for $groupId! Not inserting a retry receipt.")
|
||||
return@FollowUpOperation null
|
||||
}
|
||||
|
||||
|
@ -317,7 +356,7 @@ object MessageDecryptor {
|
|||
}
|
||||
|
||||
if (threadId == null) {
|
||||
Log.w(TAG, "${logPrefix(envelope)} Thread does not already exist for sender ${sender.id}! We will not create one just to show a retry receipt.")
|
||||
Log.w(TAG, "${logPrefix(envelope, senderServiceId)} Thread does not already exist for sender ${sender.id}! We will not create one just to show a retry receipt.")
|
||||
return@FollowUpOperation null
|
||||
}
|
||||
|
||||
|
@ -330,7 +369,7 @@ object MessageDecryptor {
|
|||
}
|
||||
|
||||
ContentHint.IMPLICIT -> {
|
||||
Log.w(TAG, "${logPrefix(envelope)} The content hint is $contentHint, so no error message is needed.", true)
|
||||
Log.w(TAG, "${logPrefix(envelope, senderServiceId)} The content hint is $contentHint, so no error message is needed.", true)
|
||||
Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations)
|
||||
}
|
||||
}
|
||||
|
@ -399,20 +438,24 @@ object MessageDecryptor {
|
|||
}
|
||||
|
||||
private fun logPrefix(envelope: Envelope): String {
|
||||
return logPrefix(envelope.timestamp!!, envelope.sourceServiceId ?: "<sealed>", envelope.sourceDevice)
|
||||
return logPrefix(envelope.timestamp!!, ServiceId.parseOrNull(envelope.sourceServiceId)?.logString() ?: "<sealed>", envelope.sourceDevice)
|
||||
}
|
||||
|
||||
private fun logPrefix(envelope: Envelope, sender: ServiceId): String {
|
||||
return logPrefix(envelope.timestamp!!, sender.toString(), envelope.sourceDevice)
|
||||
private fun logPrefix(envelope: Envelope, sender: ServiceId?): String {
|
||||
return logPrefix(envelope.timestamp!!, sender?.logString() ?: "?", envelope.sourceDevice)
|
||||
}
|
||||
|
||||
private fun logPrefix(envelope: Envelope, sender: String): String {
|
||||
return logPrefix(envelope.timestamp!!, ServiceId.parseOrNull(sender)?.logString() ?: "?", envelope.sourceDevice)
|
||||
}
|
||||
|
||||
private fun logPrefix(envelope: Envelope, cipherResult: SignalServiceCipherResult): String {
|
||||
return logPrefix(envelope.timestamp!!, cipherResult.metadata.sourceServiceId.toString(), cipherResult.metadata.sourceDeviceId)
|
||||
return logPrefix(envelope.timestamp!!, cipherResult.metadata.sourceServiceId.logString(), cipherResult.metadata.sourceDeviceId)
|
||||
}
|
||||
|
||||
private fun logPrefix(envelope: Envelope, exception: ProtocolException): String {
|
||||
return if (exception.sender != null) {
|
||||
logPrefix(envelope.timestamp!!, exception.sender, exception.senderDevice)
|
||||
logPrefix(envelope.timestamp!!, ServiceId.parseOrNull(exception.sender)?.logString() ?: "?", exception.senderDevice)
|
||||
} else {
|
||||
logPrefix(envelope.timestamp!!, envelope.sourceServiceId, envelope.sourceDevice)
|
||||
}
|
||||
|
@ -546,7 +589,12 @@ object MessageDecryptor {
|
|||
val groupId: GroupId?
|
||||
)
|
||||
|
||||
data class DecryptionErrorCount(
|
||||
var count: Int,
|
||||
var lastReceivedTime: Long
|
||||
)
|
||||
|
||||
fun interface FollowUpOperation {
|
||||
fun run(): Job?
|
||||
fun run(): JobManager.Chain?
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ public class UsernameEditFragment extends LoggingFragment {
|
|||
}
|
||||
|
||||
private void promptOrSubmitUsername() {
|
||||
if (args.getMode() == UsernameEditMode.RECOVERY) {
|
||||
if (viewModel.isSameUsernameRecovery()) {
|
||||
new MaterialAlertDialogBuilder(requireContext())
|
||||
.setMessage(R.string.UsernameEditFragment_recovery_dialog_confirmation)
|
||||
.setPositiveButton(android.R.string.ok, ((dialog, which) -> {
|
||||
|
@ -317,6 +317,9 @@ public class UsernameEditFragment extends LoggingFragment {
|
|||
case NETWORK_FAILURE:
|
||||
Toast.makeText(requireContext(), R.string.UsernameEditFragment_encountered_a_network_error, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case RATE_LIMIT_EXCEEDED:
|
||||
Toast.makeText(requireContext(), R.string.UsernameEditFragment_rate_limit_exceeded_error, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case SKIPPED:
|
||||
closeScreen();
|
||||
break;
|
||||
|
|
|
@ -34,9 +34,7 @@ import java.util.concurrent.TimeUnit
|
|||
* A note on naming conventions:
|
||||
* Usernames are made up of two discrete components, a nickname and a discriminator. They are formatted thusly:
|
||||
*
|
||||
* [nickname].[discriminator]
|
||||
*
|
||||
* The nickname is user-controlled, whereas the discriminator is controlled by the server.
|
||||
* nickname.discriminator
|
||||
*/
|
||||
internal class UsernameEditViewModel private constructor(private val mode: UsernameEditMode) : ViewModel() {
|
||||
private val events: PublishSubject<Event> = PublishSubject.create()
|
||||
|
@ -71,6 +69,7 @@ internal class UsernameEditViewModel private constructor(private val mode: Usern
|
|||
|
||||
if (mode == UsernameEditMode.RECOVERY) {
|
||||
onNicknameUpdated(SignalStore.account().username?.split(Usernames.DELIMITER)?.first() ?: "")
|
||||
onDiscriminatorUpdated(SignalStore.account().username?.split(Usernames.DELIMITER)?.last() ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,6 +128,13 @@ internal class UsernameEditViewModel private constructor(private val mode: Usern
|
|||
events.onNext(Event.SKIPPED)
|
||||
}
|
||||
|
||||
fun isSameUsernameRecovery(): Boolean {
|
||||
val usernameState = uiState.state.usernameState
|
||||
return mode == UsernameEditMode.RECOVERY &&
|
||||
usernameState is UsernameState.Reserved &&
|
||||
usernameState.requireUsername().username.lowercase() == SignalStore.account().username?.lowercase()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userConfirmedResetOk True if the user is submitting this after confirming that they're ok with resetting their username via [Event.NEEDS_CONFIRM_RESET].
|
||||
*/
|
||||
|
@ -201,6 +207,11 @@ internal class UsernameEditViewModel private constructor(private val mode: Usern
|
|||
uiState.update { State(ButtonState.SUBMIT, UsernameStatus.NONE, it.usernameState) }
|
||||
events.onNext(Event.NETWORK_FAILURE)
|
||||
}
|
||||
|
||||
UsernameSetResult.RATE_LIMIT_ERROR -> {
|
||||
uiState.update { State(ButtonState.SUBMIT, UsernameStatus.NONE, it.usernameState) }
|
||||
events.onNext(Event.RATE_LIMIT_EXCEEDED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -337,6 +348,11 @@ internal class UsernameEditViewModel private constructor(private val mode: Usern
|
|||
events.onNext(Event.NETWORK_FAILURE)
|
||||
}
|
||||
|
||||
UsernameSetResult.RATE_LIMIT_ERROR -> {
|
||||
uiState.update { State(ButtonState.SUBMIT, UsernameStatus.NONE, UsernameState.NoUsername) }
|
||||
events.onNext(Event.RATE_LIMIT_EXCEEDED)
|
||||
}
|
||||
|
||||
UsernameSetResult.CANDIDATE_GENERATION_ERROR -> {
|
||||
// TODO -- Retry
|
||||
uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.TAKEN, UsernameState.NoUsername) }
|
||||
|
@ -374,7 +390,7 @@ internal class UsernameEditViewModel private constructor(private val mode: Usern
|
|||
}
|
||||
|
||||
enum class Event {
|
||||
NETWORK_FAILURE, SUBMIT_SUCCESS, DELETE_SUCCESS, SUBMIT_FAIL_INVALID, SUBMIT_FAIL_TAKEN, SKIPPED, NEEDS_CONFIRM_RESET
|
||||
NETWORK_FAILURE, SUBMIT_SUCCESS, DELETE_SUCCESS, SUBMIT_FAIL_INVALID, SUBMIT_FAIL_TAKEN, SKIPPED, NEEDS_CONFIRM_RESET, RATE_LIMIT_EXCEEDED
|
||||
}
|
||||
|
||||
class Factory(private val mode: UsernameEditMode) : ViewModelProvider.Factory {
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager
|
|||
import org.whispersystems.signalservice.api.push.ServiceId.ACI
|
||||
import org.whispersystems.signalservice.api.push.UsernameLinkComponents
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotAssociatedWithAnAccountException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotReservedException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.UsernameMalformedException
|
||||
|
@ -379,6 +380,9 @@ object UsernameRepository {
|
|||
} catch (e: UsernameMalformedException) {
|
||||
Log.w(TAG, "[reserveUsername] Username malformed.")
|
||||
failure(UsernameSetResult.USERNAME_INVALID)
|
||||
} catch (e: RateLimitException) {
|
||||
Log.w(TAG, "[reserveUsername] Rate limit exceeded.")
|
||||
failure(UsernameSetResult.RATE_LIMIT_ERROR)
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, "[reserveUsername] Generic network exception.", e)
|
||||
failure(UsernameSetResult.NETWORK_ERROR)
|
||||
|
@ -501,7 +505,7 @@ object UsernameRepository {
|
|||
}
|
||||
|
||||
enum class UsernameSetResult {
|
||||
SUCCESS, USERNAME_UNAVAILABLE, USERNAME_INVALID, NETWORK_ERROR, CANDIDATE_GENERATION_ERROR
|
||||
SUCCESS, USERNAME_UNAVAILABLE, USERNAME_INVALID, NETWORK_ERROR, CANDIDATE_GENERATION_ERROR, RATE_LIMIT_ERROR
|
||||
}
|
||||
|
||||
enum class UsernameReclaimResult {
|
||||
|
|
|
@ -653,7 +653,7 @@ public class Recipient {
|
|||
String name = Util.getFirstNonEmpty(getGroupName(context),
|
||||
getSystemProfileName().getGivenName(),
|
||||
getProfileName().getGivenName(),
|
||||
getE164().orElse(null),
|
||||
shouldShowE164() ? getE164().orElse(null) : null,
|
||||
getUsername().orElse(null),
|
||||
getDisplayName(context));
|
||||
|
||||
|
|
|
@ -89,8 +89,8 @@ class AboutSheet : ComposeBottomSheetDialogFragment() {
|
|||
model = AboutModel(
|
||||
isSelf = recipient.get().isSelf,
|
||||
hasAvatar = recipient.get().profileAvatarFileDetails.hasFile(),
|
||||
displayName = recipient.get().getDisplayName(requireContext()),
|
||||
shortName = recipient.get().getShortDisplayName(requireContext()),
|
||||
displayName = recipient.get().getDisplayNameOrUsername(requireContext()),
|
||||
shortName = recipient.get().getShortDisplayNameIncludingUsername(requireContext()),
|
||||
about = recipient.get().about,
|
||||
verified = verified,
|
||||
recipientForAvatar = recipient.get(),
|
||||
|
|
|
@ -183,7 +183,7 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
|||
}
|
||||
|
||||
String name = recipient.isSelf() ? requireContext().getString(R.string.note_to_self)
|
||||
: recipient.getDisplayName(requireContext());
|
||||
: recipient.getDisplayNameOrUsername(requireContext());
|
||||
fullName.setVisibility(TextUtils.isEmpty(name) ? View.GONE : View.VISIBLE);
|
||||
SpannableStringBuilder nameBuilder = new SpannableStringBuilder(name);
|
||||
if (recipient.showVerified()) {
|
||||
|
|
|
@ -119,6 +119,9 @@ public final class FeatureFlags {
|
|||
private static final String GIF_SEARCH = "global.gifSearch";
|
||||
private static final String AUDIO_REMUXING = "android.media.audioRemux.1";
|
||||
private static final String VIDEO_RECORD_1X_ZOOM = "android.media.videoCaptureDefaultZoom";
|
||||
private static final String RETRY_RECEIPT_MAX_COUNT = "android.retryReceipt.maxCount";
|
||||
private static final String RETRY_RECEIPT_MAX_COUNT_RESET_AGE = "android.retryReceipt.maxCountResetAge";
|
||||
private static final String PREKEY_FORCE_REFRESH_INTERVAL = "android.prekeyForceRefreshInterval";
|
||||
|
||||
/**
|
||||
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
||||
|
@ -190,7 +193,10 @@ public final class FeatureFlags {
|
|||
USE_ACTIVE_CALL_MANAGER,
|
||||
GIF_SEARCH,
|
||||
AUDIO_REMUXING,
|
||||
VIDEO_RECORD_1X_ZOOM
|
||||
VIDEO_RECORD_1X_ZOOM,
|
||||
RETRY_RECEIPT_MAX_COUNT,
|
||||
RETRY_RECEIPT_MAX_COUNT_RESET_AGE,
|
||||
PREKEY_FORCE_REFRESH_INTERVAL
|
||||
);
|
||||
|
||||
@VisibleForTesting
|
||||
|
@ -259,7 +265,10 @@ public final class FeatureFlags {
|
|||
CALLING_REACTIONS,
|
||||
NOTIFICATION_THUMBNAIL_BLOCKLIST,
|
||||
CALLING_RAISE_HAND,
|
||||
VIDEO_RECORD_1X_ZOOM
|
||||
VIDEO_RECORD_1X_ZOOM,
|
||||
RETRY_RECEIPT_MAX_COUNT,
|
||||
RETRY_RECEIPT_MAX_COUNT_RESET_AGE,
|
||||
PREKEY_FORCE_REFRESH_INTERVAL
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -439,6 +448,20 @@ public final class FeatureFlags {
|
|||
return getLong(RETRY_RESPOND_MAX_AGE, TimeUnit.DAYS.toMillis(14));
|
||||
}
|
||||
|
||||
/**
|
||||
* The max number of retry receipts sends we allow (within @link{#retryReceiptMaxCountResetAge()}) before we consider the volume too large and stop responding.
|
||||
*/
|
||||
public static long retryReceiptMaxCount() {
|
||||
return getLong(RETRY_RECEIPT_MAX_COUNT, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the last retry receipt send was older than this, then we reset the retry receipt sent count. (For use with @link{#retryReceiptMaxCount()})
|
||||
*/
|
||||
public static long retryReceiptMaxCountResetAge() {
|
||||
return getLong(RETRY_RECEIPT_MAX_COUNT_RESET_AGE, TimeUnit.HOURS.toMillis(3));
|
||||
}
|
||||
|
||||
/** How long a sender key can live before it needs to be rotated. */
|
||||
public static long senderKeyMaxAge() {
|
||||
return Math.min(getLong(SENDER_KEY_MAX_AGE, TimeUnit.DAYS.toMillis(14)), TimeUnit.DAYS.toMillis(90));
|
||||
|
@ -595,6 +618,11 @@ public final class FeatureFlags {
|
|||
return getBoolean(VIDEO_RECORD_1X_ZOOM, false);
|
||||
}
|
||||
|
||||
/** How often we allow a forced prekey refresh. */
|
||||
public static long preKeyForceRefreshInterval() {
|
||||
return getLong(PREKEY_FORCE_REFRESH_INTERVAL, TimeUnit.HOURS.toMillis(1));
|
||||
}
|
||||
|
||||
/** Only for rendering debug info. */
|
||||
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
||||
return new TreeMap<>(REMOTE_VALUES);
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.util
|
||||
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager
|
||||
|
||||
/** Starts a new chain with this job. */
|
||||
fun Job.asChain(): JobManager.Chain {
|
||||
return ApplicationDependencies.getJobManager().startChain(this)
|
||||
}
|
|
@ -36,4 +36,8 @@ message AttachmentUploadJobData {
|
|||
uint64 attachmentId = 1;
|
||||
reserved /*attachmentUniqueId*/ 2;
|
||||
optional ResumableUpload uploadSpec = 3;
|
||||
}
|
||||
|
||||
message PreKeysSyncJobData {
|
||||
bool forceRefreshRequested = 1;
|
||||
}
|
|
@ -38,6 +38,7 @@
|
|||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="36dp"
|
||||
android:hint="@string/CreditCardFragment__card_number"
|
||||
android:theme="@style/Signal.ThemeOverlay.TextInputLayout"
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintTop_toBottomOf="@id/description">
|
||||
|
||||
|
@ -65,6 +66,7 @@
|
|||
android:layout_marginTop="18dp"
|
||||
android:hint="@string/CreditCardFragment__mm_yy"
|
||||
android:paddingEnd="18dp"
|
||||
android:theme="@style/Signal.ThemeOverlay.TextInputLayout"
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintEnd_toStartOf="@id/card_cvv_wrapper"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
@ -92,6 +94,7 @@
|
|||
android:layout_marginEnd="24dp"
|
||||
android:hint="@string/CreditCardFragment__cvv"
|
||||
android:paddingStart="18dp"
|
||||
android:theme="@style/Signal.ThemeOverlay.TextInputLayout"
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/card_expiry_wrapper"
|
||||
|
|
|
@ -401,6 +401,7 @@
|
|||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:src="@drawable/symbol_share_android_24"
|
||||
app:tint="@color/signal_colorOnPrimaryContainer"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
|
|
@ -39,11 +39,11 @@
|
|||
android:id="@+id/username_box_fill"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@drawable/username_edit_box_fill"
|
||||
android:layout_marginRight="@dimen/dsl_settings_gutter"
|
||||
android:background="@drawable/username_edit_box_fill"
|
||||
app:layout_constraintBottom_toBottomOf="@id/username_text_wrapper"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="@id/username_text_wrapper"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/username_text_wrapper" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
|
@ -54,14 +54,15 @@
|
|||
android:layout_marginLeft="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:theme="@style/Signal.ThemeOverlay.TextInputLayout"
|
||||
app:boxStrokeColor="@color/signal_colorPrimary"
|
||||
app:boxStrokeWidth="0dp"
|
||||
app:boxStrokeWidthFocused="0dp"
|
||||
app:errorTextAppearance="@style/Signal.Text.Zero"
|
||||
app:expandedHintEnabled="false"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintRight_toLeftOf="@id/suffix_progress"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="@id/suffix_progress"
|
||||
app:layout_constraintTop_toBottomOf="@id/summary"
|
||||
app:suffixTextColor="@color/signal_colorOnSurface">
|
||||
|
||||
|
@ -74,9 +75,9 @@
|
|||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:layoutDirection="ltr"
|
||||
android:maxLines="1"
|
||||
android:minHeight="56dp"
|
||||
android:layoutDirection="ltr">
|
||||
android:minHeight="56dp">
|
||||
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
|
@ -130,8 +131,8 @@
|
|||
android:layout_height="2dp"
|
||||
android:background="@color/signal_colorPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="@id/username_text_wrapper"
|
||||
app:layout_constraintRight_toRightOf="@id/discriminator_text"
|
||||
app:layout_constraintLeft_toLeftOf="@id/username_text_wrapper" />
|
||||
app:layout_constraintLeft_toLeftOf="@id/username_text_wrapper"
|
||||
app:layout_constraintRight_toRightOf="@id/discriminator_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username_error"
|
||||
|
@ -142,8 +143,8 @@
|
|||
android:layout_marginTop="4dp"
|
||||
android:textColor="@color/signal_colorError"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintRight_toRightOf="@id/username_description"
|
||||
app:layout_constraintLeft_toLeftOf="@id/username_description"
|
||||
app:layout_constraintRight_toRightOf="@id/username_description"
|
||||
app:layout_constraintTop_toBottomOf="@id/username_text_wrapper"
|
||||
tools:text="Error something bad happened. Very super long error message that wraps"
|
||||
tools:visibility="visible" />
|
||||
|
@ -159,8 +160,8 @@
|
|||
android:text="@string/UsernameEditFragment__choose_your_username"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/icon" />
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Skrap</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Gebruikersnaam suksesvol verwyder.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Netwerkfout teëgekom.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Hierdie gebruikersnaam is reeds in gebruik.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Gebruikersname kan slegs a–Z, 0–9 en onderstrepings bevat.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Gebruikersname kan nie met \'n syfer begin nie.</string>
|
||||
|
@ -3152,7 +3154,7 @@
|
|||
<string name="preferences__dark_theme">Donker</string>
|
||||
<string name="preferences__appearance">Voorkoms</string>
|
||||
<string name="preferences__theme">Tema</string>
|
||||
<string name="preferences__chat_color_and_wallpaper">Kletskleur & amp; muurpapier</string>
|
||||
<string name="preferences__chat_color_and_wallpaper">Kletskleur & muurpapier</string>
|
||||
<!-- Clickable settings text allowing the user to change the icon visible on their phone\'s home screen. -->
|
||||
<string name="preferences__app_icon">Toepassingikoon</string>
|
||||
<!-- Approval for changing the app icon. -->
|
||||
|
|
|
@ -2615,6 +2615,8 @@
|
|||
<string name="UsernameEditFragment_delete">حذف</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">تم حذف اسم المُستخدم بنجاح.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">حدث خطأ فى الشبكة</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">اسم المُستخدم هذا غير متوفّر.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">أسماء المُستخدمين يجب أن تحتوي فقط على الحروف الأبجدية، 0-9، أو \"_\".</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">اسم المُستخدم لا يمكن أن يبدأ برقم.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Sil</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">İstifadəçi adı uğurla çıxarıldı.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Şəbəkə xətası ilə qarşılaşıldı.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Bu istifadəçi adı götürülüb.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">İstifadəçi adları yalnız a–Z, 0–9 və altdan xətt ehtiva edə bilər.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">İstifadəçi adı nömrə ilə başlaya bilməz.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Изтриване</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Успешно премахнато потребителско име.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Възникна мрежова грешка.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Това потребителско име е взето.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Потребителските имена могат да включват само a–Z, 0–9 и долни черти.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Потребителските имена не могат да започват с цифра.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">মুছে ফেলুন</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">সফলভাবে ব্যবহারকারীর নাম মুছে ফেলা হয়েছে।</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">একটি নেটওয়ার্ক ত্রুটি সম্মুখীন।</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">এই ব্যবহারকারীর নাম ব্যবহৃত হয়েছে।</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">ব্যবহারকারীর নামে থাকতে পারবে শুধুমাত্র a–Z, 0–9, ও আন্ডারস্কোর।</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">ব্যবহারকারীর নাম সংখ্যা দিয়ে শুরু করতে পারবে না।</string>
|
||||
|
|
|
@ -2455,6 +2455,8 @@
|
|||
<string name="UsernameEditFragment_delete">Izbriši</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Uspješno je uklonjeno korisničko ime.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Došlo je do greške u mreži.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Ovo korisničko ime već je zauzeto.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Korisničko ime može sadržavati samo slova bez kvačica, brojeve i donju crtu.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Korisničko ime ne može početi brojem.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Suprimeix</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">L\'àlies s\'ha eliminat correctament.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Hi ha hagut un error de xarxa.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Aquest àlies ja està agafat.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Els àlies només poden incloure a-z, 0-9 i _.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Els àlies no poden començar amb un número.</string>
|
||||
|
|
|
@ -2455,6 +2455,8 @@
|
|||
<string name="UsernameEditFragment_delete">Odstranit</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Uživatelské jméno bylo úspěšně odstraněno.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Došlo k chybě v síti.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Toto uživatelské jméno je obsazené.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Uživatelská jména mohou obsahovat pouze a–Z, 0–9 a podtržítka.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Uživatelská jména nesmí začínat číslicí.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Slet</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Brugernavn slettet.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Registreret en netværksfejl</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Brugernavnet er optaget.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Brugernavne må kun indeholde a-Z, 0-9 og underscores.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Brugernavne kan ikke starte med et tal.</string>
|
||||
|
|
|
@ -1967,7 +1967,7 @@
|
|||
<!-- Displayed in a sheet row describing that the user has marked this contact as \'verified\' from within the app -->
|
||||
<string name="AboutSheet__verified">Verifiziert</string>
|
||||
<!-- Displayed in bottom sheet describing that the user has no direct messages with this person. The placeholder is a person\'s name. -->
|
||||
<string name="AboutSheet__no_direct_message">Keine Direktnachrichten mit %1$s</string>
|
||||
<string name="AboutSheet__no_direct_message">Keine Einzelchats mit %1$s</string>
|
||||
<!-- Explains that the given user (placeholder is short name) is in the users system contact -->
|
||||
<string name="AboutSheet__s_is_in_your_system_contacts">%1$s ist in deinen Telefonkontakten</string>
|
||||
<!-- Notice in a row when user has no groups in common -->
|
||||
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Löschen</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Nutzername erfolgreich entfernt.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Ein Netzfehler ist aufgetreten.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Dieser Nutzername ist vergeben.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Nutzernamen dürfen nur a–Z, 0–9 und Unterstriche beinhalten.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Nutzernamen dürfen nicht mit einer Ziffer beginnen.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Διαγραφή</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Το όνομα χρήστη αφαιρέθηκε με επιτυχία.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Υπήρξε σφάλμα δικτύου.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Αυτό το όνομα χρήστη χρησιμοποιείται ήδη.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Τα ονόματα χρήστη μπορούν να περιέχουν μόνο λατινικούς χαρακτήρες a-Z, αριθμούς 0-9 και κάτω παύλες.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Τα ονόματα χρήστη δεν μπορούν να αρχίζουν με αριθμό</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Eliminar</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Alias eliminado con éxito.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Fallo en la conexión de red.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Alias en uso por otra persona.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Tu alias solo puede contener a–Z, 0–9 y _.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">El alias no puede comenzar por un número.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Kustuta</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Kasutajanimi edukalt eemaldatud.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Esines võrgutõrge.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">See kasutajanimi on võetud.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Kasutajanimed võivad sisaldada ainult a–Z, 0–9 ja alakriipse.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Kasutajanimed ei tohi alata numbriga.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Ezabatu</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Kendu da erabiltzaile-izena.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Sare errore bat izan da.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Erabiltzaile-izen hau hartuta dago.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Erabiltzaile-izenetan bakarrik ondokoak daude onartuta: a-Z, 0-9 eta azpimarrak.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Erabiltzaile-izenak ezin dira zenbaki batez hasi.</string>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<!-- Removed by excludeNonTranslatables <string name="donate_faq_url" translatable="false">https://support.signal.org/hc/articles/360031949872#donate</string> -->
|
||||
|
||||
<string name="yes">بله</string>
|
||||
<string name="no">خیر</string>
|
||||
<string name="no">نه</string>
|
||||
<string name="delete">پاک کردن</string>
|
||||
<string name="please_wait">لطفاً صبر کنید…</string>
|
||||
<string name="save">ذخیره</string>
|
||||
|
@ -186,11 +186,11 @@
|
|||
<!-- Dialog button to report as spam only -->
|
||||
<string name="BlockUnblockDialog_report_spam">بهعنوان هرزنامه گزارش شود</string>
|
||||
<!-- Dialog message when reporting spam of an individual (1:1 conversation) -->
|
||||
<string name="BlockUnblockDialog_report_spam_description">سیگنال مطلع خواهد شد که این فرد ممکن است در حال ارسال هرزنامه باشد. سیگنال نمیتواند محتوای گفتگوها را ببیند.</string>
|
||||
<string name="BlockUnblockDialog_report_spam_description">سیگنال مطلع خواهد شد که این فرد ممکن است در حال ارسال هرزنامه باشد. سیگنال نمیتواند محتوای هیچ گفتگویی را ببیند.</string>
|
||||
<!-- Dialog message when reporting spam of a group and we can determine the group member that invited you, placeholder is a name -->
|
||||
<string name="BlockUnblockDialog_report_spam_group_named_adder">سیگنال مطلع خواهد شد که %1$s که شما را به این گروه دعوت کرده ممکن است در حال ارسال هرزنامه باشد. سیگنال نمیتواند محتوای گفتگوها را ببیند.</string>
|
||||
<string name="BlockUnblockDialog_report_spam_group_named_adder">سیگنال مطلع خواهد شد که %1$s که شما را به این گروه دعوت کرده ممکن است در حال ارسال هرزنامه باشد. سیگنال نمیتواند محتوای هیچ گفتگویی را ببیند.</string>
|
||||
<!-- Dialog message when reporting spam of a group and we cannot determine the group member that invited you -->
|
||||
<string name="BlockUnblockDialog_report_spam_group_unknown_adder">سیگنال مطلع خواهد شد که فردی که شما را به این گروه دعوت کرده ممکن است در حال ارسال هرزنامه باشد. سیگنال نمیتواند محتوای گفتگوها را ببیند.</string>
|
||||
<string name="BlockUnblockDialog_report_spam_group_unknown_adder">سیگنال مطلع خواهد شد که فردی که شما را به این گروه دعوت کرده ممکن است در حال ارسال هرزنامه باشد. سیگنال نمیتواند محتوای هیچ گفتگویی را ببیند.</string>
|
||||
|
||||
<!-- BucketedThreadMedia -->
|
||||
<string name="BucketedThreadMedia_Today">امروز</string>
|
||||
|
@ -530,7 +530,7 @@
|
|||
<!-- Menu otpion to accept a message request in a conversation -->
|
||||
<string name="ConversationFragment_accept">پذیرفتن</string>
|
||||
<!-- Menu option to delete an entire chat in a conversation -->
|
||||
<string name="ConversationFragment_delete_chat">حذف گفتگو</string>
|
||||
<string name="ConversationFragment_delete_chat">پاک کردن گفتگو</string>
|
||||
<!-- Menu option to unblock in a conversation -->
|
||||
<string name="ConversationFragment_unblock">رفع مسدودیت</string>
|
||||
<!-- Dialog title shown after reporting spam and tapping the conversation item -->
|
||||
|
@ -551,21 +551,21 @@
|
|||
<!-- Dialog subtitle when showing tips for a group conversation -->
|
||||
<string name="SafetyTips_subtitle_group">این درخواست را بادقت مرور کنید. هیچیک از مخاطبان شما یا کسانی که با آنها گفتگو میکنید در این گروه نیستند. چند چیز که باید در مورد آنها احتیاط کنید:</string>
|
||||
<!-- Button text to move to the previous tip-->
|
||||
<string name="SafetyTips_previous_tip">نکته قبلی</string>
|
||||
<string name="SafetyTips_previous_tip">نکتۀ قبلی</string>
|
||||
<!-- Button text to move to the next tip -->
|
||||
<string name="SafetyTips_next_tip">نکته بعدی</string>
|
||||
<string name="SafetyTips_next_tip">نکتۀ بعدی</string>
|
||||
<!-- Title of tip 1 -->
|
||||
<string name="SafetyTips_tip1_title">هرزنامههای پولی یا رمزارز</string>
|
||||
<string name="SafetyTips_tip1_title">کلاهبرداریهای پولی یا رمزارز</string>
|
||||
<!-- Message of tip 1 -->
|
||||
<string name="SafetyTips_tip1_message">If someone you don’t know messages about cryptocurrency (like Bitcoin) or a financial opportunity, be careful—it’s likely spam.</string>
|
||||
<!-- Title of tip 2 -->
|
||||
<string name="SafetyTips_tip2_title">پیامهای مبهم یا نامرتبط</string>
|
||||
<!-- Message of tip 2 -->
|
||||
<string name="SafetyTips_tip2_message">ارسالکنندگان هرزنامه معمولاً با پیام سادهای مثل «سلام» شروع میکنند تا شما را درگیر کنند. اگر پاسخ دهید ممکن است درگیر ادامه ماجرا شوید.</string>
|
||||
<string name="SafetyTips_tip2_message">ارسالکنندگان هرزنامه معمولاً با پیام سادهای مثل «سلام» شروع میکنند تا شما را درگیر کنند. اگر پاسخ دهید ممکن است گفتگو را بیشتر پیش ببرند تا شما را درگیر کنند.</string>
|
||||
<!-- Title of tip 3 -->
|
||||
<string name="SafetyTips_tip3_title">پیامهای دارای پیوند</string>
|
||||
<!-- Message of tip 3 -->
|
||||
<string name="SafetyTips_tip3_message">در مورد پیامهایی که از افراد ناشناس دریافت میکنید و داخل آنها پیوند به وبسایتها وجود دارد احتیاط کنید. هیچوقت پیوندی که از افراد غیرقابلاعتماد دریافت کردهاید را باز نکنید.</string>
|
||||
<string name="SafetyTips_tip3_message">در مورد پیامهایی که از افراد ناشناس دریافت میکنید و داخل آنها پیوند به وبسایتی وجود دارد احتیاط کنید. هیچوقت پیوندی که از افراد غیرقابلاعتماد دریافت کردهاید را باز نکنید.</string>
|
||||
<!-- Title of tip 4 -->
|
||||
<string name="SafetyTips_tip4_title">کسبوکارها و سازمانهای جعلی</string>
|
||||
<!-- Message of tip 4 -->
|
||||
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">پاک کردن</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">نام کاربری با موفقیت حذف شد.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">خطای شبکه رخ داد.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">این نام کاربری گرفته شده است.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">نامهای کاربری فقط میتوانند حاوی حروف A تا Z، اعداد ۰ تا ۹ و _ باشند.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">نامهای کاربری نمیتوانند با عدد آغاز شوند.</string>
|
||||
|
@ -6565,7 +6567,7 @@
|
|||
<!-- Entry placeholder for find by phone number -->
|
||||
<string name="FindByActivity__phone_number">شماره تلفن</string>
|
||||
<!-- Help text under user entry for find by username -->
|
||||
<string name="FindByActivity__enter_username_description">نام کاربری و سپس نقطه و مجموعه اعداد آن را وارد کنید.</string>
|
||||
<string name="FindByActivity__enter_username_description">یک نام کاربری و سپس یک نقطه و مجموعۀ اعداد آن را وارد کنید.</string>
|
||||
<!-- Content description for next action button -->
|
||||
<string name="FindByActivity__next">بعدی</string>
|
||||
<!-- Placeholder text for search input for selecting country code -->
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Poista</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Käyttäjänimen poisto onnistui.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Verkkovirhe.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Tämä käyttäjänimi on varattu.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Käyttäjänimet voivat sisältää vain merkkejä a–z, 0–9 ja alaviivoja.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Käyttäjänimi ei voi alkaa numerolla.</string>
|
||||
|
|
|
@ -1601,7 +1601,7 @@
|
|||
<!-- Update message shown in chat after reporting it as spam -->
|
||||
<string name="MessageRecord_reported_as_spam">Signalé comme spam</string>
|
||||
<!-- Update message shown in chat after accept a message request -->
|
||||
<string name="MessageRecord_you_accepted_the_message_request">Vous avez accepté la demande de message</string>
|
||||
<string name="MessageRecord_you_accepted_the_message_request">Vous avez accepté l’invitation par message</string>
|
||||
|
||||
<!-- MessageRequestBottomView -->
|
||||
<string name="MessageRequestBottomView_accept">Accepter</string>
|
||||
|
@ -2207,7 +2207,7 @@
|
|||
<string name="SupportEmailUtil_device_info">Info sur l’appareil :</string>
|
||||
<string name="SupportEmailUtil_android_version">Version d’Android :</string>
|
||||
<string name="SupportEmailUtil_signal_version">Version de Molly :</string>
|
||||
<string name="SupportEmailUtil_signal_package">Paquet Molly :</string>
|
||||
<string name="SupportEmailUtil_signal_package">Package Molly :</string>
|
||||
<string name="SupportEmailUtil_registration_lock">Blocage de l’inscription :</string>
|
||||
<string name="SupportEmailUtil_locale">Paramètres régionaux :</string>
|
||||
|
||||
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Supprimer</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Le nom d’utilisateur a été effacé avec succès.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Une erreur réseau est survenue.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Ce nom d’utilisateur existe déjà.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Les noms d’utilisateur ne peuvent inclure que des caractères de A à Z, 0 à 9 et des tirets bas.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Un nom d’utilisateur ne peut pas commencer par un chiffre.</string>
|
||||
|
@ -2588,7 +2590,7 @@
|
|||
|
||||
<!-- WebRtcAudioOutputToggle -->
|
||||
<!-- Label for a dialog asking the user to switch the audio output device during a call -->
|
||||
<string name="WebRtcAudioOutputToggle__audio_output">Sortie son</string>
|
||||
<string name="WebRtcAudioOutputToggle__audio_output">Sortie audio</string>
|
||||
<!-- Audio output option referring to the earpiece built into the phone -->
|
||||
<string name="WebRtcAudioOutputToggle__phone_earpiece">Écouteur de téléphone</string>
|
||||
<!-- Audio output option referring to the louder speaker built into the phone -->
|
||||
|
@ -2677,8 +2679,8 @@
|
|||
<string name="conversation_activity__emoji_toggle_description">Afficher/masquer le clavier des émojis</string>
|
||||
<string name="conversation_activity__attachment_thumbnail">Imagette de fichiers joints</string>
|
||||
<string name="conversation_activity__quick_attachment_drawer_toggle_camera_description">Afficher/masquer le tiroir de l’appareil photo à basse résolution</string>
|
||||
<string name="conversation_activity__quick_attachment_drawer_record_and_send_audio_description">Enregistrer et envoyer une fichier son joint</string>
|
||||
<string name="conversation_activity__quick_attachment_drawer_lock_record_description">Verrouiller l’enregistrement de fichiers son joints</string>
|
||||
<string name="conversation_activity__quick_attachment_drawer_record_and_send_audio_description">Enregistrer et envoyer un message vocal en pièce jointe</string>
|
||||
<string name="conversation_activity__quick_attachment_drawer_lock_record_description">Verrouiller l’enregistrement de pièces jointes au format audio</string>
|
||||
<string name="conversation_activity__message_could_not_be_sent">Le message n’a pas pu être envoyé. Veuillez vérifier votre connexion et réessayer.</string>
|
||||
|
||||
<!-- conversation_input_panel -->
|
||||
|
|
|
@ -2535,6 +2535,8 @@
|
|||
<string name="UsernameEditFragment_delete">Scrios</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">D\'éirigh le hainm úsáideora a bhaint.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Tharla earráid líonra</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Tá an t-ainm úsáideora sin gafa cheana.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Ní cheadaítear ach a-Z, 0-9, agus fostríoca in ainmneacha úsáideora.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Ní cheadaítear uimhir ag tús ainm úsáideora.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Eliminar</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Nome de usuario eliminado satisfactoriamente.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Houbo un fallo na rede.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Este nome de usuario xa está en uso.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Os nomes de usuario só poden incluír a-Z, 0-9 e guións baixos.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Os nomes de usuario non poden comezar por un número.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">ડિલીટ કરો</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">યુઝરનેમ સફળતાપૂર્વક દૂર કર્યું.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">નેટવર્ક ભૂલ મળી.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">આ યુઝરનેમ લેવાઈ ગયું છે.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">યુઝરનેમમાં માત્ર a–Z, 0–9 અને અન્ડરસ્કોર શામેલ હોઈ શકે છે.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">યુઝરનેમ સંખ્યા સાથે શરૂ થઈ શકતા નથી.</string>
|
||||
|
@ -3152,7 +3154,7 @@
|
|||
<string name="preferences__dark_theme">ડાર્ક</string>
|
||||
<string name="preferences__appearance">દેખાવ</string>
|
||||
<string name="preferences__theme">થીમ</string>
|
||||
<string name="preferences__chat_color_and_wallpaper">ચેટ કલર & amp; વૉલપેપર</string>
|
||||
<string name="preferences__chat_color_and_wallpaper">ચેટ કલર & વૉલપેપર</string>
|
||||
<!-- Clickable settings text allowing the user to change the icon visible on their phone\'s home screen. -->
|
||||
<string name="preferences__app_icon">ઍપ આઇકન</string>
|
||||
<!-- Approval for changing the app icon. -->
|
||||
|
|
|
@ -536,7 +536,7 @@
|
|||
<!-- Dialog title shown after reporting spam and tapping the conversation item -->
|
||||
<string name="ConversationFragment_reported_spam">रिपोर्ट किया गया स्पैम</string>
|
||||
<!-- Dialog message shown after reporting spam and tapping the conversation item -->
|
||||
<string name="ConversationFragment_reported_spam_message">Signal has been notified that this person may be sending spam. Signal can’t see the content of any chats.</string>
|
||||
<string name="ConversationFragment_reported_spam_message">Signal को सूचित किया गया है कि यह व्यक्ति स्पैम भेज सकता है। Signal किसी भी चैट की सामग्री नहीं देख सकता।</string>
|
||||
<!-- Toast shown after reporting spam and tapping the conversation item -->
|
||||
<string name="ConversationFragment_reported_as_spam">स्पैम के रूप में रिपोर्ट किया गया</string>
|
||||
<!-- Toast shown after reporting and blocking a conversation -->
|
||||
|
@ -557,7 +557,7 @@
|
|||
<!-- Title of tip 1 -->
|
||||
<string name="SafetyTips_tip1_title">क्रिप्टो या मनी स्कैम</string>
|
||||
<!-- Message of tip 1 -->
|
||||
<string name="SafetyTips_tip1_message">If someone you don’t know messages about cryptocurrency (like Bitcoin) or a financial opportunity, be careful—it’s likely spam.</string>
|
||||
<string name="SafetyTips_tip1_message">यदि कोई व्यक्ति जिसे आप नहीं जानते हैं, क्रिप्टोकरेंसी (जैसे बिटकॉइन) या वित्तीय अवसर के बारे में संदेश भेजता है, तो सावधान रहें - यह संभावित रूप से स्पैम है।</string>
|
||||
<!-- Title of tip 2 -->
|
||||
<string name="SafetyTips_tip2_title">अस्पष्ट या अप्रासंगिक संदेश</string>
|
||||
<!-- Message of tip 2 -->
|
||||
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">डिलीट करें</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">सफलतापूर्वक यूज़रनेम हटा दिया गया।</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">एक नेटवर्क त्रुटि का सामना करना पड़ा|</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">यह यूज़रनेम ले लिया गया है।</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">यूज़रनेम में केवल a–Z, 0–9, और अंडरस्कोर शामिल हो सकते हैं।</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">यूज़रनेम एक संख्या से शुरू नहीं हो सकते हैं।</string>
|
||||
|
@ -2318,7 +2320,7 @@
|
|||
<!-- Displayed when the chosen discriminator is 00 -->
|
||||
<string name="UsernameEditFragment__this_number_cant_be_00">यह संख्या 00 नहीं हो सकती। 1-9 के बीच का कोई अंक दर्ज करें</string>
|
||||
<!-- Displayed when the chosen discriminator starts with 0 and has a length > 2 -->
|
||||
<string name="UsernameEditFragment__this_number_cant_start_with_0">Numbers with more than 2 digits can\'t start with 0</string>
|
||||
<string name="UsernameEditFragment__this_number_cant_start_with_0">2 से अधिक अंकों वाली संखाएं 0 से शुरू नहीं हो सकतीं</string>
|
||||
<!-- The body of an alert dialog asking the user to confirm that they want to recover their username -->
|
||||
<string name="UsernameEditFragment_recovery_dialog_confirmation">आपके यूज़रनेम को रिकवर करने से आपका मौजूदा QR कोड और लिंक फिर से स्थापित हो जाएगा। क्या आपको यकीन है?</string>
|
||||
<!-- The body of an alert dialog asking the user to confirm that they want to change their username, even if it resets their link -->
|
||||
|
|
|
@ -2455,6 +2455,8 @@
|
|||
<string name="UsernameEditFragment_delete">Izbriši</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Korisničko ime uspješno je uklonjeno.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Došlo je do mrežne pogreške.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Korisničko ime je zauzeto.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Korisnička imena smiju sadržavati samo a-Z, 0-9 i podvlake.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Korisnička imena ne mogu započinjati s brojem.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Törlés</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">A felhasználónév eltávolítása sikeres volt.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Hálózati hiba történt.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">A felhasználónév foglalt.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">A felhasználónevek csak a-Z, 0-9 és alsóvonás karaktereket tartalmazhatnak.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">A felhasználónevek nem kezdődhetnek számmal.</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">Hapus</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Nama pengguna berhasil dihapus.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Terjadi galat jaringan.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Nama pengguna telah digunakan.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Nama pengguna hanya boleh berisi a–Z, 0–9, dan garisbawah.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Nama pengguna tidak boleh dimulai dengan angka.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Elimina</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Nome utente rimosso con successo.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Si è verificato un errore di rete.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Questo nome utente è già in uso.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">I nomi utente possono includere solo a–Z, 0–9 e trattini bassi.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">I nomi utente non possono iniziare con un numero.</string>
|
||||
|
|
|
@ -2455,6 +2455,8 @@
|
|||
<string name="UsernameEditFragment_delete">מחיקה</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">שם משתמש הוסר בהצלחה.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">היישום נתקל בשגיאת רשת.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">שם משתמש זה תפוס.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">שמות משתמש יכולים להכיל רק a–Z, 0–9, וקווים תחתונים.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">שמות משתמש אינם יכולים להתחיל במספר.</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">消去する</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">ユーザーネームを削除しました。</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">ネットワークエラーが発生しました。</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">このユーザーネームは既に使用されています。</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">ユーザーネームには半角の英数字とアンダーバーのみ使用できます。</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">ユーザーネームの先頭に数字は使用できません。</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">წაშლა</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">მომხმარებლის სახელი წარმატებით წაიშალა.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">ქსელის ხარვეზს გადავაწყდით.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">ეს მომხმარებლის სახელი დაკავებულია.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">მომხმარებლის სახელი შეიძლება შეიცავდეს მხოლოდ a–Z, 0–9 და ქვედა ტირეს.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">მომხმარებლის სახელი ციფრით ვერ დაიწყება.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Жою</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Пайдаланушы аты өшірілді.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Желі қатесі анықталды.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Бұл пайдаланушы аты бос емес.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Пайдаланушы аттарында a–Z, 0–9 және астыңғы сызықтар бола алады.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Пайдаланушы аттары саннан басталмауы керек.</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">លុប</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">លុបឈ្មោះអ្នកប្រើបានជោគជ័យ។</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">បានជួបប្រទះកំហុសបណ្តាញ។</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">ឈ្មោះអ្នកប្រើនេះត្រូវបានយកហើយ។</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">ឈ្មោះអ្នកប្រើអាចរួមបញ្ចូលតែ a–Z, 0–9, និងបន្ទាត់ពីក្រោមប៉ុណ្ណោះ។</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">ឈ្មោះអ្នកប្រើមិនអាចចាប់ផ្តើមដោយលេខបានទេ។</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">ಅಳಿಸಿ</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">ಬಳಕೆದಾರ ಹೆಸರನ್ನು ಯಶಸ್ವಿಯಾಗಿ ತೆಗೆದುಹಾಕಲಾಗಿದೆ.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">ನೆಟ್ವರ್ಕ್ ದೋಷವನ್ನು ಎದುರಿಸಿದೆ.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">ಈ ಯೂಸರ್ ನೇಮ್ ತೆಗೆದುಕೊಳ್ಳಲಾಗಿದೆ.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">ಯೂಸರ್ ನೇಮ್ ಗಳು a-Z, 0-9, ಮತ್ತು ಅಡಿಗೆರೆಗಳನ್ನು ಮಾತ್ರ ಒಳಗೊಂಡಿರಬಹುದು.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">ಯೂಸರ್ ನೇಮ್ ಗಳು ಸಂಖ್ಯೆಯಿಂದ ಪ್ರಾರಂಭವಾಗುವುದಿಲ್ಲ.</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">삭제</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">사용자 이름이 성공적으로 삭제되었습니다.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">네트워크 오류가 발생했습니다.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">사용 중인 사용자 이름입니다.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">사용자 이름에는 a~Z, 0~9 및 밑줄만 포함할 수 있습니다.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">사용자 이름은 숫자로 시작할 수 없습니다.</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">Өчүрүү</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Колдонуучу аты өчүрүлдү.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Тармакта ката кетти.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Мындай ат бар.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Колдонуучунун аты a-Z, 0-9 деген тамгалар менен сандардан жана ылдыйкы сызыктан турат.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Колдонуучунун аты сандан башталбайт.</string>
|
||||
|
|
|
@ -2455,6 +2455,8 @@
|
|||
<string name="UsernameEditFragment_delete">Ištrinti</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Naudotojo vardas sėkmingai pašalintas.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Susidurta su tinklo klaida.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Šis naudotojo vardas užimtas.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Naudotojo varduose gali būti tik simboliai a–Z, 0–9 ir pabraukimo brūkšniai.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Naudotojo vardai negali prasidėti skaitmenimi.</string>
|
||||
|
|
|
@ -2375,6 +2375,8 @@
|
|||
<string name="UsernameEditFragment_delete">Dzēst</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Lietotājvārds ir sekmīgi noņemts.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Tīkla kļūda.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Šis lietotājvārds ir aizņemts.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Lietotājvārdi var ietvert tikai a–Z, 0–9 un pasvītrojuma zīmes.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Lietotājvārdi nevar sākties ar skaitli.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Избриши</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Успешно отстрането корисничко име.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Се случи мрежна грешка.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Корисничкото име е веќе во употреба.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Корисничките имиња можат да се состојат од а-Ш, 0-9 и долни цртички.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Корисничкото име не може да започне со број.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">ഇല്ലാതാക്കൂ</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">ഉപയോക്തൃനാമം നീക്കം ചെയ്തു.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">ഒരു നെറ്റ്വർക്ക് പിശക് നേരിട്ടു.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">ഈ ഉപയോക്തൃനാമം എടുത്തിട്ടുണ്ട്.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">ഉപയോക്തൃനാമങ്ങളിൽ a–Z, 0–9, അണ്ടർ സ്കോറുകൾ എന്നിവ മാത്രമേ ഉൾപ്പെടുത്താനാകൂ.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">ഉപയോക്തൃനാമം ഒരു നമ്പറിൽ ആരംഭിക്കാൻ കഴിയില്ല.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">हटवा</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">वापरकर्तानाव यशस्वीरीत्या काढून टाकले.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">एक नेटवर्क त्रुटी आढळली.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">हे वापरकर्तानाव घेतलेले आहे.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">वापरकर्तानावांमध्ये फक्त a–Z, 0–9, आणि अंडरस्कोर याचा समावेश असू शकतो.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">वापरकर्तानाव अंकांसोबत सोबत चालू होऊ शकत नाही.</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">Padam</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Berjaya mengalih keluar nama pengguna.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Menghadapi ralat rangkaian.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Nama pengguna ini telah diambil.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Nama pengguna hanya boleh mengandungi a–Z, 0–9 dan garis bawah.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Nama pengguna tidak boleh bermula dengan nombor.</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">ဖျက်ရန်</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">အသုံးပြုသူအမည်ကို အောင်မြင်စွာ ဖယ်ရှားခဲ့ပြီး။</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">ကွန်ယက်အမှားတစ်ခုနှင့် ကြုံတွေ့ခဲ့ရသည်။</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">ဤသုံးစွဲသူအမည်ကို ယူထားပြီးပါပြီ။</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">သုံးစွဲသူအမည်များတွင် A–Z၊ 0-9 နှင့် \"_\" သင်္ကေတများသာ ပါဝင်နိုင်ပါသည်။</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">သုံးစွဲသူအမည်များသည် နံပါတ်နှင့် စတင်၍ မရပါ။</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Slett</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Brukernavnet er fjernet.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Det oppstod en nettverksfeil.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Dette brukernavnet er tatt.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Brukernavn kan bare inneholde a–Z, 0–9 og understrek.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Brukernavn kan ikke begynne med et tall.</string>
|
||||
|
|
|
@ -1698,7 +1698,7 @@
|
|||
<string name="PlacePickerActivity_title">Kaart</string>
|
||||
|
||||
<string name="PlacePickerActivity_drop_pin">Aanwijzer</string>
|
||||
<string name="PlacePickerActivity_accept_address">Adres accepteren</string>
|
||||
<string name="PlacePickerActivity_accept_address">Adres bevestigen</string>
|
||||
|
||||
<!-- PlayServicesProblemFragment -->
|
||||
<string name="PlayServicesProblemFragment_the_version_of_google_play_services_you_have_installed_is_not_functioning">De versie van Google Play Services die je in gebruik hebt werkt niet zoals het hoort. Installeer Google Play Services opnieuw en probeer het opnieuw.</string>
|
||||
|
@ -1967,7 +1967,7 @@
|
|||
<!-- Displayed in a sheet row describing that the user has marked this contact as \'verified\' from within the app -->
|
||||
<string name="AboutSheet__verified">Geverifieerd</string>
|
||||
<!-- Displayed in bottom sheet describing that the user has no direct messages with this person. The placeholder is a person\'s name. -->
|
||||
<string name="AboutSheet__no_direct_message">Geen directe berichten met %1$s</string>
|
||||
<string name="AboutSheet__no_direct_message">Geen individuele chat met %1$s</string>
|
||||
<!-- Explains that the given user (placeholder is short name) is in the users system contact -->
|
||||
<string name="AboutSheet__s_is_in_your_system_contacts">%1$s staat in je systeemcontactenlijst</string>
|
||||
<!-- Notice in a row when user has no groups in common -->
|
||||
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Verwijderen</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Gebruikersnaam succesvol verwijderd.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Er is een netwerkfout opgetreden.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Deze gebruikersnaam is al in gebruik.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Gebruikersnamen mogen alleen uit a-z, 0-9 en _ bestaan.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Gebruikersnamen mogen niet met een cijfer beginnen.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">ਮਿਟਾਓ</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">ਵਰਤੋਂਕਾਰ-ਨਾਂ ਸਫਲਤਾਪੂਰਵਕ ਹਟਾਇਆ ਗਿਆ।</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">ਨੈੱਟਵਰਕ ਗਲਤੀ ਆਈ ਹੈ।</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">ਇਹ ਵਰਤੋਂਕਾਰ ਨਾਂ ਕੋਈ ਹੋਰ ਵਰਤ ਰਿਹਾ ਹੈ।</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">ਵਰਤੋਂਕਾਰ ਨਾਂ ਵਿੱਚ ਸਿਰਫ਼ a–Z, 0–9 ਅਤੇ ਅੰਡਰਸਕੋਰ ਸ਼ਾਮਲ ਹੋ ਸਕਦੇ ਹਨ।</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">ਵਰਤੋਂਕਾਰ ਨਾਂ ਅੰਕ ਨਾਲ ਸ਼ੁਰੂ ਨਹੀਂ ਹੋ ਸਕਦਾ ਹੈ।</string>
|
||||
|
|
|
@ -2455,6 +2455,8 @@
|
|||
<string name="UsernameEditFragment_delete">Usuń</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Pomyślnie usunięto nazwę użytkownika.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Wystąpił błąd sieci.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Nazwa użytkownika jest zajęta.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Nazwy użytkowników mogą zawierać wyłącznie a–Z, 0–9 i podkreślenia (_).</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Nazwy użytkowników nie mogą zaczynać się od cyfry.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Apagar</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Usuário excluído com sucesso</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Foi encontrado um erro de rede.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Este nome de usuário já está sendo utilizado.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Os nomes de usuário só podem incluir a–Z, 0–9, e sublinhado.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Os nomes de usuários não podem começar com um número.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Eliminar</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Nome de utilizador removido com sucesso.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Encontrado um erro de rede.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Este nome de utilizador já se encontra em utilização.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Os nomes de utilizadores apenas podem incluir a-Z, 0-9 e underscores.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Os nomes de utilizadores não podem começar com um número.</string>
|
||||
|
|
|
@ -2375,6 +2375,8 @@
|
|||
<string name="UsernameEditFragment_delete">Șterge</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Numele de utilizator a fost eliminat cu succes.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">A apărut o eroare de rețea.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Acest nume de utilizator este luat.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Numele de utilizatori pot conține doar a-Z, 0-9, și _.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Numele de utilizator nu pot începe cu un număr.</string>
|
||||
|
|
|
@ -2455,6 +2455,8 @@
|
|||
<string name="UsernameEditFragment_delete">Удалить</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Имя пользователя успешно удалено.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Обнаружена ошибка сети.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Это имя пользователя занято.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Имена пользователей могут содержать только a–Z, 0–9 и нижние подчёркивания.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Имена пользователей не могут начинаться с цифр.</string>
|
||||
|
|
|
@ -2455,6 +2455,8 @@
|
|||
<string name="UsernameEditFragment_delete">Vymazať</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Používateľské meno bolo úspešne odstránené.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Vyskytla sa chyba siete.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Toto používateľské meno je obsadené.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Používateľské mená môžu obsahovať iba znaky a-Z, 0-9 a podčiarkovníky.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Používateľské mená nemôžu začínať číslicou.</string>
|
||||
|
|
|
@ -2455,6 +2455,8 @@
|
|||
<string name="UsernameEditFragment_delete">Izbriši</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Uspešno ste izbrisali uporabniško ime.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Prišlo je do napake na omrežju.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Uporabniško ime je že zasedeno.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Uporabniška imena lahko vsebujejo samo znake a-Z, 0-9 in podčrtaje.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Uporabniška imena se ne smejo začeti s števikami.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Fshije</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Emri i përdoruesit u hoq me sukses.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">U has një gabim rrjeti.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Ky emër përdoruesi është i zënë.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Emrat e përdoruesve mund të përmbajnë vetëm a-Z, 0-9 dhe nënvija.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Emrat e përdoruesve nuk mund të fillojnë me numër.</string>
|
||||
|
@ -3150,7 +3152,7 @@
|
|||
<string name="preferences__linked_devices">Pajisje të lidhura</string>
|
||||
<string name="preferences__light_theme">E çelët</string>
|
||||
<string name="preferences__dark_theme">E errët</string>
|
||||
<string name="preferences__appearance">Dukje</string>
|
||||
<string name="preferences__appearance">Paraqitja</string>
|
||||
<string name="preferences__theme">Temë</string>
|
||||
<string name="preferences__chat_color_and_wallpaper">Ngjyra e sfondit të bisedës & letra e murit</string>
|
||||
<!-- Clickable settings text allowing the user to change the icon visible on their phone\'s home screen. -->
|
||||
|
|
|
@ -536,7 +536,7 @@
|
|||
<!-- Dialog title shown after reporting spam and tapping the conversation item -->
|
||||
<string name="ConversationFragment_reported_spam">Спам је пријављен</string>
|
||||
<!-- Dialog message shown after reporting spam and tapping the conversation item -->
|
||||
<string name="ConversationFragment_reported_spam_message">Signal has been notified that this person may be sending spam. Signal can’t see the content of any chats.</string>
|
||||
<string name="ConversationFragment_reported_spam_message">Signal је обавештен да ова особа можда шаље спам. Signal не може да види садржај ниједног ћаскања.</string>
|
||||
<!-- Toast shown after reporting spam and tapping the conversation item -->
|
||||
<string name="ConversationFragment_reported_as_spam">Пријављено је као спам</string>
|
||||
<!-- Toast shown after reporting and blocking a conversation -->
|
||||
|
@ -557,7 +557,7 @@
|
|||
<!-- Title of tip 1 -->
|
||||
<string name="SafetyTips_tip1_title">Крипто или новчане преваре</string>
|
||||
<!-- Message of tip 1 -->
|
||||
<string name="SafetyTips_tip1_message">If someone you don’t know messages about cryptocurrency (like Bitcoin) or a financial opportunity, be careful—it’s likely spam.</string>
|
||||
<string name="SafetyTips_tip1_message">Ако вам неко кога не познајете шаље поруке о криптовалути (као што је Bitcoin) или некој финансијској прилици, будите опрезни – вероватно је у питању спам.</string>
|
||||
<!-- Title of tip 2 -->
|
||||
<string name="SafetyTips_tip2_title">Нејасне или неповезане поруке</string>
|
||||
<!-- Message of tip 2 -->
|
||||
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Обриши</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Корисничко име уклоњено.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Дошло је до грешке мреже.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Ово корисничко име је заузето.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Корисничко име може да садржи a-Z, 0-9 и _</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Корисничко име не може започети бројем.</string>
|
||||
|
@ -2318,7 +2320,7 @@
|
|||
<!-- Displayed when the chosen discriminator is 00 -->
|
||||
<string name="UsernameEditFragment__this_number_cant_be_00">Овај број не може бити 00. Унесите цифру од 1 до 9</string>
|
||||
<!-- Displayed when the chosen discriminator starts with 0 and has a length > 2 -->
|
||||
<string name="UsernameEditFragment__this_number_cant_start_with_0">Numbers with more than 2 digits can\'t start with 0</string>
|
||||
<string name="UsernameEditFragment__this_number_cant_start_with_0">Бројеви са више од 2 цифре не могу да почињу са 0</string>
|
||||
<!-- The body of an alert dialog asking the user to confirm that they want to recover their username -->
|
||||
<string name="UsernameEditFragment_recovery_dialog_confirmation">Ако вратите корисничко име, ресетоваће вам се постојећи QR код и линк. Да ли сте сигурни?</string>
|
||||
<!-- The body of an alert dialog asking the user to confirm that they want to change their username, even if it resets their link -->
|
||||
|
|
|
@ -536,7 +536,7 @@
|
|||
<!-- Dialog title shown after reporting spam and tapping the conversation item -->
|
||||
<string name="ConversationFragment_reported_spam">Rapporterade skräppost</string>
|
||||
<!-- Dialog message shown after reporting spam and tapping the conversation item -->
|
||||
<string name="ConversationFragment_reported_spam_message">Signal has been notified that this person may be sending spam. Signal can’t see the content of any chats.</string>
|
||||
<string name="ConversationFragment_reported_spam_message">Signal har meddelats att denna person kanske skickar skräppost. Signal kan inte se innehållet i några chattar.</string>
|
||||
<!-- Toast shown after reporting spam and tapping the conversation item -->
|
||||
<string name="ConversationFragment_reported_as_spam">Rapporterad som skräppost</string>
|
||||
<!-- Toast shown after reporting and blocking a conversation -->
|
||||
|
@ -557,7 +557,7 @@
|
|||
<!-- Title of tip 1 -->
|
||||
<string name="SafetyTips_tip1_title">Krypto- eller penningbedrägerier</string>
|
||||
<!-- Message of tip 1 -->
|
||||
<string name="SafetyTips_tip1_message">If someone you don’t know messages about cryptocurrency (like Bitcoin) or a financial opportunity, be careful—it’s likely spam.</string>
|
||||
<string name="SafetyTips_tip1_message">Om någon du inte känner skickar meddelanden om kryptovalutor (som Bitcoin) eller en finansiell möjlighet ska du vara försiktig – det är sannolikt skräppost.</string>
|
||||
<!-- Title of tip 2 -->
|
||||
<string name="SafetyTips_tip2_title">Vaga eller irrelevanta meddelanden</string>
|
||||
<!-- Message of tip 2 -->
|
||||
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Ta bort</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Användarnamn borttaget.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Stött på ett nätverksfel.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Detta användarnamn är taget.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Användarnamn kan bara innehålla a–Z, 0–9 och understreck.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Användarnamn kan inte börja med en siffra.</string>
|
||||
|
@ -2318,7 +2320,7 @@
|
|||
<!-- Displayed when the chosen discriminator is 00 -->
|
||||
<string name="UsernameEditFragment__this_number_cant_be_00">Det här numret kan inte vara 00. Ange en siffra mellan 1–9</string>
|
||||
<!-- Displayed when the chosen discriminator starts with 0 and has a length > 2 -->
|
||||
<string name="UsernameEditFragment__this_number_cant_start_with_0">Numbers with more than 2 digits can\'t start with 0</string>
|
||||
<string name="UsernameEditFragment__this_number_cant_start_with_0">Nummer med fler än 2 siffror kan inte börja med 0</string>
|
||||
<!-- The body of an alert dialog asking the user to confirm that they want to recover their username -->
|
||||
<string name="UsernameEditFragment_recovery_dialog_confirmation">Om du återställer ditt användarnamn återställs din befintliga QR-kod och länk. Är du säker?</string>
|
||||
<!-- The body of an alert dialog asking the user to confirm that they want to change their username, even if it resets their link -->
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Futa</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Jina la mtumiaji limeondolewa kwa mafanikio.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Imekumbana na hitilafu ya mtandao.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Jina hili la mtumiaji limechukuliwa.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Majina ya watumiaji yanaweza kujumuisha tu herufi za A hadi Z, nambari za 0 hadi 9, na vistari-chini.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Majina ya watumiaji hayawezi kuanza kwa nambari.</string>
|
||||
|
|
|
@ -182,15 +182,15 @@
|
|||
<!-- Dialog button label to report as spam and block the person -->
|
||||
<string name="BlockUnblockDialog_report_spam_and_block">புகாரளித்து தடு</string>
|
||||
<!-- Dialog title for reporting spam -->
|
||||
<string name="BlockUnblockDialog_report_spam_title">ஸ்பேம் எனப் புகாரளிக்கவா?</string>
|
||||
<string name="BlockUnblockDialog_report_spam_title">ஸ்பேம் எனப் புகாரளிப்பதா?</string>
|
||||
<!-- Dialog button to report as spam only -->
|
||||
<string name="BlockUnblockDialog_report_spam">ஸ்பேம் எனப் புகாரளி</string>
|
||||
<!-- Dialog message when reporting spam of an individual (1:1 conversation) -->
|
||||
<string name="BlockUnblockDialog_report_spam_description">இந்த நபர் உங்களுக்கு ஸ்பேம் அனுப்பியிருக்கலாம் என்பதை சிக்னல் உங்களுக்குத் தெரிவிக்கிறது. சாட்களின் உள்ளடக்கம் எதையும் சிக்னலால் பார்க்க முடியவில்லை.</string>
|
||||
<string name="BlockUnblockDialog_report_spam_description">இந்த நபர் உங்களுக்கு ஸ்பேம் அனுப்புவதாக சிக்னலுக்குத் தெரிவிக்கப்படும். எந்தவொரு சாட்களின் உள்ளடக்கத்தையும் சிக்னலால் பார்க்க முடியாது.</string>
|
||||
<!-- Dialog message when reporting spam of a group and we can determine the group member that invited you, placeholder is a name -->
|
||||
<string name="BlockUnblockDialog_report_spam_group_named_adder">உங்களை இந்தக் குழுவிற்கு அழைத்த %1$s, ஸ்பேமை அனுப்பியிருக்கலாம் என்பதை சிக்னல் உங்களுக்குத் தெரிவிக்கிறது. சாட்களின் உள்ளடக்கம் எதையும் சிக்னலால் பார்க்க முடியவில்லை.</string>
|
||||
<string name="BlockUnblockDialog_report_spam_group_named_adder">உங்களை இந்தக் குழுவிற்கு அழைத்த %1$s, ஸ்பேமை அனுப்பியிருக்கலாம் என்பதை சிக்னலுக்குத் தெரிவிக்கப்படும். எந்தவொரு சாட்களின் உள்ளடக்கத்தையும் சிக்னலால் பார்க்க முடியாது.</string>
|
||||
<!-- Dialog message when reporting spam of a group and we cannot determine the group member that invited you -->
|
||||
<string name="BlockUnblockDialog_report_spam_group_unknown_adder">உங்களை இந்தக் குழுவிற்கு அழைத்த நபர், ஸ்பேமை அனுப்பியிருக்கலாம் என்பதை சிக்னல் உங்களுக்குத் தெரிவிக்கிறது. சாட்களின் உள்ளடக்கம் எதையும் சிக்னலால் பார்க்க முடியவில்லை.</string>
|
||||
<string name="BlockUnblockDialog_report_spam_group_unknown_adder">உங்களை இந்தக் குழுவிற்கு அழைத்த நபர், ஸ்பேமை அனுப்பியிருக்கலாம் என்பதை சிக்னலுக்குத் தெரிவிக்கப்படும். எந்தவொரு சாட்களின் உள்ளடக்கத்தையும் சிக்னலால் பார்க்க முடியாது.</string>
|
||||
|
||||
<!-- BucketedThreadMedia -->
|
||||
<string name="BucketedThreadMedia_Today">இன்று</string>
|
||||
|
@ -495,7 +495,7 @@
|
|||
<string name="ConversationFragment__d_group_members_have_the_same_name">%1$dகுழு உறுப்பினர்களுக்கு ஒரே பெயர் உண்டு.</string>
|
||||
<string name="ConversationFragment__tap_to_review">மதிப்பாய்வு செய்ய தட்டவும்</string>
|
||||
<!-- The body of a banner that can show up at the top of a chat, letting the user know that you have two contacts with the same name -->
|
||||
<string name="ConversationFragment__review_banner_body">இந்த நபரின் பெயரில் உங்களிடம் இன்னொரு தொடர்பு உள்ளது</string>
|
||||
<string name="ConversationFragment__review_banner_body">இந்த நபருக்கு மற்றொரு தொடர்பின் அதே பெயர் உள்ளது</string>
|
||||
<string name="ConversationFragment_contact_us">எங்களை தொடர்பு கொள்ள</string>
|
||||
<string name="ConversationFragment_verify">சரிபார்க்கவும்</string>
|
||||
<string name="ConversationFragment_not_now">இப்போது இல்லை</string>
|
||||
|
@ -526,13 +526,13 @@
|
|||
<!-- Menu option to report spam in a conversation -->
|
||||
<string name="ConversationFragment_report_spam">ஸ்பேம் எனப் புகாரளி</string>
|
||||
<!-- Menu option to block in a conversation -->
|
||||
<string name="ConversationFragment_block">தடைசெய்</string>
|
||||
<string name="ConversationFragment_block">தடை செய்</string>
|
||||
<!-- Menu otpion to accept a message request in a conversation -->
|
||||
<string name="ConversationFragment_accept">ஒப்புக்கொள்</string>
|
||||
<!-- Menu option to delete an entire chat in a conversation -->
|
||||
<string name="ConversationFragment_delete_chat">சாட்டை அழி</string>
|
||||
<!-- Menu option to unblock in a conversation -->
|
||||
<string name="ConversationFragment_unblock">தடைநீக்கு</string>
|
||||
<string name="ConversationFragment_unblock">தடை நீக்கு</string>
|
||||
<!-- Dialog title shown after reporting spam and tapping the conversation item -->
|
||||
<string name="ConversationFragment_reported_spam">ஸ்பேம் எனப் புகாரளிக்கப்பட்டது</string>
|
||||
<!-- Dialog message shown after reporting spam and tapping the conversation item -->
|
||||
|
@ -542,12 +542,12 @@
|
|||
<!-- Toast shown after reporting and blocking a conversation -->
|
||||
<string name="ConversationFragment_reported_as_spam_and_blocked">ஸ்பேம் எனப் புகாரளிக்கப்பட்டு தடுக்கப்பட்டது</string>
|
||||
<!-- Dialog message shown after accepting a message request and tapping on options from the conversation event -->
|
||||
<string name="ConversationFragment_you_accepted_a_message_request_from_s">%1$sஇடமிருந்து பெற்ற மெசேஜ் கோரிக்கையை நீங்கள் ஏற்றுக்கொண்டீர்கள். இது தவறுதலாக நடைபெற்றிருந்தால், கீழே கொடுக்கப்பட்ட நடவடிக்கையிலிருந்து நீங்கள் தேர்ந்தெடுக்கலாம்.</string>
|
||||
<string name="ConversationFragment_you_accepted_a_message_request_from_s">%1$sஇடமிருந்து பெற்ற மெசேஜ் கோரிக்கையை நீங்கள் ஏற்றுக்கொண்டீர்கள். இது தவறுதலாக நடைபெற்றிருந்தால், கீழே கொடுக்கப்பட்ட நடவடிக்கையிலிருந்து நீங்கள் ஒன்றைத் தேர்ந்தெடுக்கலாம்.</string>
|
||||
|
||||
<!-- Title of Safety Tips bottom sheet dialog -->
|
||||
<string name="SafetyTips_title">பாதுகாப்பு உதவிக்குறிப்புகள்</string>
|
||||
<!-- Dialog subtitle when showign tips for a 1:1 conversation -->
|
||||
<string name="SafetyTips_subtitle_individual">உங்களுக்கு பரீட்சயமற்றவரிடமிருந்து பெறப்படும் மெசேஜ் கோரிக்கைகளை ஏற்கும்போது கவனமாக இருங்கள். கவனமாக இருங்கள்:</string>
|
||||
<string name="SafetyTips_subtitle_individual">உங்களுக்கு தெரியாத நபர்களிடமிருந்து பெறப்படும் மெசேஜ் கோரிக்கைகளை ஏற்கும்போது கவனமாக இருங்கள். கவனமாக இருங்கள்:</string>
|
||||
<!-- Dialog subtitle when showing tips for a group conversation -->
|
||||
<string name="SafetyTips_subtitle_group">கோரிக்கையை கவனமாக மதிப்பாய்வு செய்க. உங்கள் தொடர்புகள் அல்லது நீங்கள் சாட் செய்பவர்கள் யாரும் இந்தக் குழுவில் இல்லை. நீங்கள் கவனிக்க வேண்டிய சில விஷயங்கள் இங்கே கொடுக்கப்பட்டுள்ளன:</string>
|
||||
<!-- Button text to move to the previous tip-->
|
||||
|
@ -561,11 +561,11 @@
|
|||
<!-- Title of tip 2 -->
|
||||
<string name="SafetyTips_tip2_title">தெளிவற்ற அல்லது பொருத்தமற்ற மெசேஜ்கள்</string>
|
||||
<!-- Message of tip 2 -->
|
||||
<string name="SafetyTips_tip2_message">ஸ்பேமர்கள் உங்கள் கவனத்தை ஈர்க்க \"ஹாய்\" போன்ற வழக்கமான மெசேஜ் உடன் தொடங்குவார்கள். அதற்கு நீங்கள் பதிலளித்தால் அவர்கள் உங்களை மேற்கொண்டு ஈடுபடுத்தலாம்.</string>
|
||||
<string name="SafetyTips_tip2_message">ஸ்பேமர்கள் உங்கள் கவனத்தை ஈர்க்க \"ஹாய்\" போன்ற வழக்கமான மெசேஜ் உடன் தொடங்குவார்கள். அதற்கு நீங்கள் பதிலளித்தால் அவர்கள் உங்களை மேற்கொண்டு உரையாடலைத் தொடரலாம்.</string>
|
||||
<!-- Title of tip 3 -->
|
||||
<string name="SafetyTips_tip3_title">இணைப்புகளுடன் கூடிய மெசேஜ்கள்</string>
|
||||
<!-- Message of tip 3 -->
|
||||
<string name="SafetyTips_tip3_message">உங்களுக்குப் பரீட்சயமற்ற நபர்களிடமிருந்து இணையதளங்களுக்கான இணைப்புகளுடன் கூடிய மெசேஜ்களைப் பெறும்போது கவனமாக இருங்கள். உங்களுக்கு நம்பிக்கையற்றவரிடமிருந்து இணைப்புகளைப் பெறும்போது ஒருபோதும் பார்வையிடாதீர்கள்.</string>
|
||||
<string name="SafetyTips_tip3_message">உங்களுக்கு தெரியாத நபர்களிடமிருந்து இணையதளங்களுக்கான இணைப்புகளுடன் கூடிய மெசேஜ்களைப் பெறும்போது கவனமாக இருங்கள். உங்களுக்கு நம்பிக்கையற்றவர்கள் அனுப்பும் இணைப்புகளை ஒருபோதும் பார்வையிடாதீர்கள்.</string>
|
||||
<!-- Title of tip 4 -->
|
||||
<string name="SafetyTips_tip4_title">போலி நிறுவனங்கள் மற்றும் அமைப்புகள்</string>
|
||||
<!-- Message of tip 4 -->
|
||||
|
@ -1967,7 +1967,7 @@
|
|||
<!-- Displayed in a sheet row describing that the user has marked this contact as \'verified\' from within the app -->
|
||||
<string name="AboutSheet__verified">சரிபார்க்கப்பட்டது</string>
|
||||
<!-- Displayed in bottom sheet describing that the user has no direct messages with this person. The placeholder is a person\'s name. -->
|
||||
<string name="AboutSheet__no_direct_message">உங்களுக்கும் %1$sக்கும் இடையில் எந்த மெசேஜ்களும் இல்லை</string>
|
||||
<string name="AboutSheet__no_direct_message">உங்களுக்கும் %1$sக்கும் இடையில் எந்த நேரடி மெசேஜ்களும் இல்லை</string>
|
||||
<!-- Explains that the given user (placeholder is short name) is in the users system contact -->
|
||||
<string name="AboutSheet__s_is_in_your_system_contacts">உங்கள் சிஸ்டம் தொடர்புகளில் %1$s உள்ளார்</string>
|
||||
<!-- Notice in a row when user has no groups in common -->
|
||||
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">நீக்கு</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">பயனர் பெயர் வெற்றிகரமாக அகற்றப்பட்டது.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">பிணைய பிழையை எதிர்கொண்டது.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">இந்த பயனர் பெயர் எடுக்கப்பட்டது.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">பயனர் பெயர்களில் அ - ஃ, a–Z, 0–9 மற்றும் அடிக்கோடிட்டுக் காட்டலாம்.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">பயனர் பெயர் எண்ணுடன் தொடங்க முடியாது.</string>
|
||||
|
@ -4125,9 +4127,9 @@
|
|||
|
||||
<!-- ReviewCardDialogFragment -->
|
||||
<!-- Title of a screen where the user will be prompted to review group members with the same name -->
|
||||
<string name="ReviewCardDialogFragment__review_members">மறுஆய்வு கோரிக்கை</string>
|
||||
<string name="ReviewCardDialogFragment__review_members">மதிப்பாய்வு கோரிக்கை</string>
|
||||
<!-- Title of a screen where the user will be prompted to review a message request matching the name of someone they already know -->
|
||||
<string name="ReviewCardDialogFragment__review_request">மறுஆய்வு கோரிக்கை</string>
|
||||
<string name="ReviewCardDialogFragment__review_request">மதிப்பாய்வு கோரிக்கை</string>
|
||||
<string name="ReviewCardDialogFragment__d_group_members_have_the_same_name">%1$dகுழு உறுப்பினர்களுக்கு ஒரே பெயர் உள்ளது, கீழே உள்ள உறுப்பினர்களை மதிப்பாய்வு செய்து நடவடிக்கை எடுக்க முடிவு செய்யுங்கள்</string>
|
||||
<string name="ReviewCardDialogFragment__if_youre_not_sure">உங்களுக்கு யார் கோரிக்கையை அனுப்பினார்கள் என்பது உங்களுக்குத் தெரியாவிட்டால், கீழே உள்ள தொடர்புகளை மதிப்பாய்வு செய்து நடவடிக்கை எடுக்கவும்.</string>
|
||||
<string name="ReviewCardDialogFragment__no_other_groups_in_common">பொதுவான குழுக்கள் இல்லை</string>
|
||||
|
@ -6565,7 +6567,7 @@
|
|||
<!-- Entry placeholder for find by phone number -->
|
||||
<string name="FindByActivity__phone_number">தொலைபேசி எண்</string>
|
||||
<!-- Help text under user entry for find by username -->
|
||||
<string name="FindByActivity__enter_username_description">புள்ளி மற்றும் அதன் எண்களின் தொகுப்பைத் தொடர்ந்து பயனர்பெயரை உள்ளிடவும்.</string>
|
||||
<string name="FindByActivity__enter_username_description">ஒரு புள்ளி மற்றும் அதன் எண்களின் தொகுப்பைத் தொடர்ந்து பயனர் பெயரை உள்ளிடவும்.</string>
|
||||
<!-- Content description for next action button -->
|
||||
<string name="FindByActivity__next">அடுத்து</string>
|
||||
<!-- Placeholder text for search input for selecting country code -->
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">తొలగించండి</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">యూజర్నేమ్ను విజయవంతంగా తొలగించాము.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">నెట్వర్క్ లోపాన్ని ఎదుర్కొంది.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">ఈ వినియోగదారు పేరు తీసుకోబడింది.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">వినియోగదారు పేర్లు a-Z, 0-9 మరియు అండర్ స్కోర్లను మాత్రమే కలిగి ఉంటాయి.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">వినియోగదారు పేర్లు సంఖ్యతో ప్రారంభం కావు.</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">ลบ</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">ลบชื่อผู้ใช้สำเร็จแล้ว</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">พบความผิดพลาดของเครือข่าย</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">มีผู้ใช้ชื่อนี้แล้ว</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">ชื่อผู้ใช้จะมีได้เฉพาะ a-Z, 0-9 และ _</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">ชื่อผู้ใช้ไม่สามารถเริ่มต้นด้วยตัวเลข</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Burahin</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Matagumpay na natanggal ang username.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Nakatagpo ng error sa network.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Nakuha na ang username na ito.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Ang usernames ay pwede lang mag-include ng a–Z, 0–9, at underscores.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Ang mga username ay hindi puwedeng magsimula sa numero.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">Sil</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Kullanıcı adı başarıyla kaldırıldı.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Bir ağ hatası ile karşılaşıldı.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Bu kullanıcı adı alınmış.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Kullanıcı adları yalnızca a-Z, 0-9 ve alt çizgi içerebilir.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Kullanıcı adları rakam ile başlayamaz.</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">ئۆچۈر</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">ئىشلەتكۈچى ئىسمى مۇۋەپپەقىيەتلىك چىقىرىۋېتىلدى.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">تور خاتالىقىغا يولۇقتى.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">ئىشلەتكۈچى ئىسمى ئىشلىتىلگەن</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">ئىشلەتكۈچى ئىسمى پەقەت a–Z، 0–9 ۋە ئاستى سىزىقتىن تۈزۈلىدۇ.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">ئىشلەتكۈچى ئىسمى ساندىن باشلانمايدۇ.</string>
|
||||
|
|
|
@ -2455,6 +2455,8 @@
|
|||
<string name="UsernameEditFragment_delete">Видалити</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Ім\'я користувача видалено.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Виникла помилка мережі.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Це ім\'я користувача вже використовується.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Імена користувачів можуть мати лише такі символи: a–Z, 0–9 та нижнє підкреслювання.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Ім\'я користувача не може починатися з цифри.</string>
|
||||
|
|
|
@ -2295,6 +2295,8 @@
|
|||
<string name="UsernameEditFragment_delete">حذف کریں</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">کامیابی کے ساتھ یوزر نیم ہٹا دیا گیا۔</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">نیٹ ورک کی خرابی کا سامنا کرنا پڑا۔</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">یہ یوزر نیم لے لیا گیا ہے۔</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">یوزر نیمز میں صرف – Z-A ، 0–9 ، اور انڈر سکور شامل ہوسکتے ہیں۔</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">یوزر نیمز کسی نمبر کے ساتھ شروع نہیں ہوسکتے ہیں۔</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">Xóa</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">Xóa tên người dùng thành công.</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">Lỗi mạng.</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">Tên người dùng này đã được sử dụng.</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">Tên người dùng chỉ có thể chứa a–Z, 0–9 và dấu gạch dưới.</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">Tên người dùng không thể bắt đầu bằng một chữ số.</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">刪除</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">成功移除咗用戶名稱。</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">網絡有問題。</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">呢個用戶名稱已經畀人用咗喇。</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">用戶名稱只可以用 a–Z、0–9 同底線符號。</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">用戶名稱冇得用數目字開頭。</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">删除</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">成功移除用户名。</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">网络出错。</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">该用户名已有人使用。</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">用户名仅能包含字母、数字和下划线。</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">用户名不能以数字开头。</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">刪除</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">已成功移除用戶名稱。</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">遇到網絡問題。</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">此用戶名稱已有人使用。</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">用戶名稱僅可包含 a–Z、0–9 以及底線。</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">用戶名稱不得以數字開首。</string>
|
||||
|
|
|
@ -2215,6 +2215,8 @@
|
|||
<string name="UsernameEditFragment_delete">刪除</string>
|
||||
<string name="UsernameEditFragment_successfully_removed_username">成功移除使用者名稱。</string>
|
||||
<string name="UsernameEditFragment_encountered_a_network_error">網路連接錯誤。</string>
|
||||
<!-- Toast message shown if user exceeds the rate limit for reserving usernames -->
|
||||
<string name="UsernameEditFragment_rate_limit_exceeded_error">Too many attempts made, please try again later.</string>
|
||||
<string name="UsernameEditFragment_this_username_is_taken">這個使用者名稱已經被使用。</string>
|
||||
<string name="UsernameEditFragment_usernames_can_only_include">使用者名稱只能包含a–Z,0–9和底線。</string>
|
||||
<string name="UsernameEditFragment_usernames_cannot_begin_with_a_number">使用者名稱不可以以數字開頭。</string>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue