diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0c71b94c..8d51136a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -80,11 +80,21 @@
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
+ android:localeConfig="@xml/locales_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.BitchatAndroid"
- tools:targetApi="31">
+ tools:targetApi="33">
+
+
+
+
,
+ selectedLanguageTag: String,
+ onLanguageSelected: (String) -> Unit,
+ onDismiss: () -> Unit,
+) {
+ AlertDialog(
+ onDismissRequest = onDismiss,
+ title = { Text(stringResource(R.string.about_select_language)) },
+ text = {
+ LazyColumn(modifier = Modifier.fillMaxWidth()) {
+ item(key = "system") {
+ LanguageOptionRow(
+ label = stringResource(R.string.about_system_default),
+ selected = selectedLanguageTag.isEmpty(),
+ onClick = { onLanguageSelected("") },
+ )
+ }
+ items(
+ count = languages.size,
+ key = { index -> languages[index].languageTag },
+ ) { index ->
+ val language = languages[index]
+ LanguageOptionRow(
+ label = language.endonym,
+ selected = selectedLanguageTag == language.languageTag,
+ onClick = { onLanguageSelected(language.languageTag) },
+ )
+ }
+ }
+ },
+ confirmButton = {
+ TextButton(onClick = onDismiss) {
+ Text(stringResource(android.R.string.cancel))
+ }
+ },
+ )
+}
+
+@Composable
+private fun LanguageOptionRow(
+ label: String,
+ selected: Boolean,
+ onClick: () -> Unit,
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable(onClick = onClick)
+ .padding(vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ RadioButton(
+ selected = selected,
+ onClick = null,
+ )
+ Spacer(modifier = Modifier.width(12.dp))
+ Text(
+ text = label,
+ style = MaterialTheme.typography.bodyLarge,
+ )
+ }
+}
+
+@Composable
+private fun LanguageSettingsRow(
+ selectedLanguageName: String,
+ onClick: () -> Unit,
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable(onClick = onClick)
+ .padding(horizontal = 16.dp, vertical = 13.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ imageVector = Icons.Filled.Language,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.primary,
+ modifier = Modifier.size(22.dp),
+ )
+ Spacer(modifier = Modifier.width(16.dp))
+ Text(
+ text = stringResource(R.string.about_language),
+ style = MaterialTheme.typography.bodyMedium,
+ fontWeight = FontWeight.Medium,
+ modifier = Modifier.weight(1f),
+ )
+ Text(
+ text = selectedLanguageName,
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.weight(1f, fill = false),
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Icon(
+ imageVector = Icons.Filled.ChevronRight,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
+ modifier = Modifier.size(20.dp),
+ )
+ }
+}
+
/**
* Unified settings toggle row with icon, title, subtitle, and switch
* Apple-like design with proper spacing
@@ -259,6 +369,28 @@ fun AboutSheet(
val colorScheme = MaterialTheme.colorScheme
val palette = LocalBitchatPalette.current
var selectedTab by remember { mutableStateOf(AboutTab.Info) }
+ val supportedLanguages = remember(context) {
+ LanguagePreferenceManager.supportedLanguages(context)
+ }
+ var selectedLanguageTag by remember {
+ mutableStateOf(LanguagePreferenceManager.currentLanguageTag())
+ }
+ var showLanguagePicker by remember { mutableStateOf(false) }
+
+ if (showLanguagePicker) {
+ LanguagePickerDialog(
+ languages = supportedLanguages,
+ selectedLanguageTag = selectedLanguageTag,
+ onLanguageSelected = { languageTag ->
+ showLanguagePicker = false
+ if (languageTag != selectedLanguageTag) {
+ selectedLanguageTag = languageTag
+ LanguagePreferenceManager.setLanguage(languageTag)
+ }
+ },
+ onDismiss = { showLanguagePicker = false },
+ )
+ }
if (isPresented) {
BitchatBottomSheet(
@@ -342,6 +474,29 @@ fun AboutSheet(
}
}
+ item(key = "language") {
+ val selectedLanguageName = supportedLanguages
+ .firstOrNull { it.languageTag == selectedLanguageTag }
+ ?.endonym
+ ?: stringResource(R.string.about_system_default)
+
+ Column {
+ AboutSectionLabel(text = stringResource(R.string.about_language))
+ Surface(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = AboutHorizontalPadding),
+ color = colorScheme.surface,
+ shape = AboutCardShape,
+ ) {
+ LanguageSettingsRow(
+ selectedLanguageName = selectedLanguageName,
+ onClick = { showLanguagePicker = true },
+ )
+ }
+ }
+ }
+
// Settings Section - Unified Card with Toggles
item(key = "settings") {
LaunchedEffect(Unit) { PoWPreferenceManager.init(context) }
diff --git a/app/src/main/java/com/bitchat/android/ui/LanguagePreferenceManager.kt b/app/src/main/java/com/bitchat/android/ui/LanguagePreferenceManager.kt
new file mode 100644
index 00000000..91d2719e
--- /dev/null
+++ b/app/src/main/java/com/bitchat/android/ui/LanguagePreferenceManager.kt
@@ -0,0 +1,61 @@
+package com.bitchat.android.ui
+
+import android.content.Context
+import androidx.annotation.XmlRes
+import androidx.appcompat.app.AppCompatDelegate
+import androidx.core.os.LocaleListCompat
+import com.bitchat.android.R
+import org.xmlpull.v1.XmlPullParser
+import java.util.Locale
+
+data class AppLanguage(
+ val languageTag: String,
+ val endonym: String,
+)
+
+object LanguagePreferenceManager {
+ fun currentLanguageTag(): String =
+ AppCompatDelegate.getApplicationLocales().toLanguageTags()
+
+ fun setLanguage(languageTag: String) {
+ AppCompatDelegate.setApplicationLocales(
+ LocaleListCompat.forLanguageTags(languageTag)
+ )
+ }
+
+ fun supportedLanguages(
+ context: Context,
+ @XmlRes localeConfig: Int = R.xml.locales_config,
+ ): List = readLanguageTags(context, localeConfig)
+ .map { languageTag ->
+ val locale = Locale.forLanguageTag(languageTag)
+ AppLanguage(
+ languageTag = languageTag,
+ endonym = locale.getDisplayName(locale)
+ .replaceFirstChar { first ->
+ if (first.isLowerCase()) first.titlecase(locale) else first.toString()
+ },
+ )
+ }
+ .sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.endonym })
+
+ internal fun readLanguageTags(
+ context: Context,
+ @XmlRes localeConfig: Int,
+ ): List {
+ val parser = context.resources.getXml(localeConfig)
+ return buildList {
+ var event = parser.eventType
+ while (event != XmlPullParser.END_DOCUMENT) {
+ if (event == XmlPullParser.START_TAG && parser.name == "locale") {
+ parser.getAttributeValue(ANDROID_NAMESPACE, "name")
+ ?.takeIf(String::isNotBlank)
+ ?.let(::add)
+ }
+ event = parser.next()
+ }
+ }
+ }
+
+ private const val ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android"
+}
diff --git a/app/src/main/java/com/bitchat/android/ui/OrientationAwareActivity.kt b/app/src/main/java/com/bitchat/android/ui/OrientationAwareActivity.kt
index 82d8feaa..a3914c90 100644
--- a/app/src/main/java/com/bitchat/android/ui/OrientationAwareActivity.kt
+++ b/app/src/main/java/com/bitchat/android/ui/OrientationAwareActivity.kt
@@ -2,14 +2,14 @@ package com.bitchat.android.ui
import android.content.pm.ActivityInfo
import android.os.Bundle
-import androidx.activity.ComponentActivity
+import androidx.appcompat.app.AppCompatActivity
import com.bitchat.android.utils.DeviceUtils
/**
* Base activity that automatically sets orientation based on device type.
* Tablets can rotate to landscape, phones are locked to portrait.
*/
-abstract class OrientationAwareActivity : ComponentActivity() {
+abstract class OrientationAwareActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 13c8d51c..5ff9e58b 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -389,4 +389,7 @@
تحقّق من الملاحظات المتروكة هنا
تُركت ملاحظة واحدة هنا — انقر للقراءة
تُركت %d ملاحظات هنا — انقر للقراءة
+ اللغة
+ إعداد النظام الافتراضي
+ اختر اللغة
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index 7c2617a4..6a68b222 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -376,4 +376,7 @@
এখানে রাখা নোট আছে কি না দেখুন
এখানে 1টি নোট রাখা আছে — পড়তে ট্যাপ করুন
এখানে %dটি নোট রাখা আছে — পড়তে ট্যাপ করুন
+ ভাষা
+ সিস্টেম ডিফল্ট
+ ভাষা নির্বাচন করুন
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index b13fb001..eb613160 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -390,4 +390,7 @@
nachsehen, ob hier notizen hinterlassen wurden
1 notiz hier hinterlassen — tippen zum lesen
%d notizen hier hinterlassen — tippen zum lesen
+ Sprache
+ Systemstandard
+ Sprache auswählen
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index e1ac2c2b..fe5a9cc7 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -389,4 +389,7 @@
buscar notas dejadas aquí
1 nota dejada aquí — toca para leer
%d notas dejadas aquí — toca para leer
+ Idioma
+ Predeterminado del sistema
+ Seleccionar idioma
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index a3e81d29..e15eea5b 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -376,4 +376,7 @@
یادداشتهای باقیمانده در اینجا را بررسی کنید
۱ یادداشت اینجا باقی مانده — برای خواندن ضربه بزنید
%d یادداشت اینجا باقی مانده — برای خواندن ضربه بزنید
+ زبان
+ پیشفرض سیستم
+ انتخاب زبان
diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml
index 1da03f06..dba9aea1 100644
--- a/app/src/main/res/values-fil/strings.xml
+++ b/app/src/main/res/values-fil/strings.xml
@@ -393,4 +393,7 @@
tingnan kung may mga note na naiwan dito
1 note ang naiwan dito — i-tap para basahin
%d note ang naiwan dito — i-tap para basahin
+ Wika
+ Default ng system
+ Pumili ng wika
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index e632bdb8..4c53c77f 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -407,4 +407,7 @@
vérifier s\'il y a des notes laissées ici
1 note laissée ici — appuyez pour lire
%d notes laissées ici — appuyez pour lire
+ Langue
+ Langue du système
+ Choisir la langue
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index 3793942b..54712e1b 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -57,4 +57,7 @@
בדיקה אם הושארו כאן פתקים
פתק אחד הושאר כאן — הקש לקריאה
%d פתקים הושארו כאן — הקש לקריאה
+ שפה
+ ברירת המחדל של המערכת
+ בחירת שפה
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index 1a393f38..13a40c68 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -389,4 +389,7 @@
देखें कि यहाँ नोट छोड़े गए हैं या नहीं
यहाँ 1 नोट छोड़ा गया है — पढ़ने के लिए टैप करें
यहाँ %d नोट छोड़े गए हैं — पढ़ने के लिए टैप करें
+ भाषा
+ सिस्टम डिफ़ॉल्ट
+ भाषा चुनें
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
index 934556e4..962af5b0 100644
--- a/app/src/main/res/values-id/strings.xml
+++ b/app/src/main/res/values-id/strings.xml
@@ -389,4 +389,7 @@
periksa catatan yang ditinggalkan di sini
1 catatan ditinggalkan di sini — ketuk untuk membaca
%d catatan ditinggalkan di sini — ketuk untuk membaca
+ Bahasa
+ Default sistem
+ Pilih bahasa
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 7e67de6d..393a7f1c 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -422,4 +422,7 @@
controlla se ci sono note lasciate qui
1 nota lasciata qui — tocca per leggere
%d note lasciate qui — tocca per leggere
+ Lingua
+ Predefinita di sistema
+ Seleziona lingua
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 1e7da71d..8d7ed0e6 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -389,4 +389,7 @@
ここに残されたメモを確認
ここに1件のメモがあります — タップして読む
ここに%d件のメモがあります — タップして読む
+ 言語
+ システムのデフォルト
+ 言語を選択
diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml
index 4994f9cc..18753531 100644
--- a/app/src/main/res/values-ka/strings.xml
+++ b/app/src/main/res/values-ka/strings.xml
@@ -376,4 +376,7 @@
აქ დატოვებული ჩანაწერების შემოწმება
აქ 1 ჩანაწერია დატოვებული — წასაკითხად შეეხეთ
აქ %d ჩანაწერია დატოვებული — წასაკითხად შეეხეთ
+ ენა
+ სისტემის ნაგულისხმევი
+ ენის არჩევა
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 7327257a..435691fa 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -389,4 +389,7 @@
여기 남겨진 쪽지 확인
여기 남겨진 쪽지 1개 — 탭하여 읽기
여기 남겨진 쪽지 %d개 — 탭하여 읽기
+ 언어
+ 시스템 기본값
+ 언어 선택
diff --git a/app/src/main/res/values-mg/strings.xml b/app/src/main/res/values-mg/strings.xml
index 168eebe7..1dade6f0 100644
--- a/app/src/main/res/values-mg/strings.xml
+++ b/app/src/main/res/values-mg/strings.xml
@@ -402,4 +402,7 @@
hizaha raha misy naoty navela teto
naoty 1 no navela teto — tsindrio raha hamaky
naoty %d no navela teto — tsindrio raha hamaky
+ Fiteny
+ Fitenin’ny rafitra
+ Safidio ny fiteny
diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml
index add7b467..0e673a37 100644
--- a/app/src/main/res/values-ms/strings.xml
+++ b/app/src/main/res/values-ms/strings.xml
@@ -44,4 +44,7 @@
semak nota yang ditinggalkan di sini
1 nota ditinggalkan di sini — ketik untuk baca
%d nota ditinggalkan di sini — ketik untuk baca
+ Bahasa
+ Lalai sistem
+ Pilih bahasa
diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml
index 48a7afbd..be59a559 100644
--- a/app/src/main/res/values-ne/strings.xml
+++ b/app/src/main/res/values-ne/strings.xml
@@ -393,4 +393,7 @@
यहाँ छोडिएका नोटहरू छन् कि हेर्नुहोस्
यहाँ 1 नोट छोडिएको छ — पढ्न ट्याप गर्नुहोस्
यहाँ %d नोटहरू छोडिएका छन् — पढ्न ट्याप गर्नुहोस्
+ भाषा
+ प्रणालीको पूर्वनिर्धारित
+ भाषा छान्नुहोस्
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 0eeaec55..ff35eae6 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -420,4 +420,7 @@
kijk of hier notities zijn achtergelaten
1 notitie hier achtergelaten — tik om te lezen
%d notities hier achtergelaten — tik om te lezen
+ Taal
+ Systeemstandaard
+ Taal selecteren
diff --git a/app/src/main/res/values-pa-rPK/strings.xml b/app/src/main/res/values-pa-rPK/strings.xml
index 5f5f5f75..8d9ee0db 100644
--- a/app/src/main/res/values-pa-rPK/strings.xml
+++ b/app/src/main/res/values-pa-rPK/strings.xml
@@ -376,4 +376,7 @@
ایتھے چھڈے نوٹس ویکھو
ایتھے 1 نوٹ چھڈیا گیا — پڑھݨ لئی ٹیپ کرو
ایتھے %d نوٹس چھڈے گئے — پڑھݨ لئی ٹیپ کرو
+ بولی
+ سسٹم دی ڈیفالٹ
+ بولی چݨو
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index f1a67349..bde25680 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -57,4 +57,7 @@
sprawdź, czy zostawiono tutaj notatki
1 notatka zostawiona tutaj — stuknij, aby przeczytać
%d notatek zostawionych tutaj — stuknij, aby przeczytać
+ Język
+ Domyślny systemu
+ Wybierz język
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index fd4901ef..3c2008b3 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -388,4 +388,7 @@
ver se há notas deixadas aqui
1 nota deixada aqui — toque para ler
%d notas deixadas aqui — toque para ler
+ Idioma
+ Padrão do sistema
+ Selecionar idioma
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 5247916e..76adeff2 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -389,4 +389,7 @@
ver se há notas deixadas aqui
1 nota deixada aqui — toque para ler
%d notas deixadas aqui — toque para ler
+ Idioma
+ Predefinição do sistema
+ Selecionar idioma
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 4dc08928..77f9c1ff 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -383,4 +383,7 @@
проверить, есть ли здесь заметки
здесь оставлена 1 заметка — нажмите, чтобы прочитать
здесь оставлено заметок: %d — нажмите, чтобы прочитать
+ Язык
+ Системный язык
+ Выбрать язык
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index dcfda208..7b21fd60 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -381,4 +381,7 @@
kolla om anteckningar lämnats här
1 anteckning lämnad här — tryck för att läsa
%d anteckningar lämnade här — tryck för att läsa
+ Språk
+ Systemstandard
+ Välj språk
diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml
index 05bd4750..5a303157 100644
--- a/app/src/main/res/values-ta/strings.xml
+++ b/app/src/main/res/values-ta/strings.xml
@@ -44,4 +44,7 @@
இங்கே விடப்பட்ட குறிப்புகள் உள்ளதா எனப் பார்க்கவும்
இங்கே 1 குறிப்பு விடப்பட்டுள்ளது — படிக்க தட்டவும்
இங்கே %d குறிப்புகள் விடப்பட்டுள்ளன — படிக்க தட்டவும்
+ மொழி
+ கணினி இயல்புநிலை
+ மொழியைத் தேர்ந்தெடுக்கவும்
diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml
index 7e1fc05a..7af338eb 100644
--- a/app/src/main/res/values-th/strings.xml
+++ b/app/src/main/res/values-th/strings.xml
@@ -376,4 +376,7 @@
ดูว่ามีโน้ตทิ้งไว้ที่นี่หรือไม่
มี 1 โน้ตทิ้งไว้ที่นี่ — แตะเพื่ออ่าน
มี %d โน้ตทิ้งไว้ที่นี่ — แตะเพื่ออ่าน
+ ภาษา
+ ค่าเริ่มต้นของระบบ
+ เลือกภาษา
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index b81fdd08..6158fb08 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -381,4 +381,7 @@
buraya bırakılan notlara bak
buraya 1 not bırakıldı — okumak için dokun
buraya %d not bırakıldı — okumak için dokun
+ Dil
+ Sistem varsayılanı
+ Dil seçin
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 9ca2d2d0..662890c0 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -44,4 +44,7 @@
перевірити, чи залишено тут нотатки
тут залишено 1 нотатку — торкніться, щоб прочитати
тут залишено %d нотаток — торкніться, щоб прочитати
+ Мова
+ Системна мова
+ Вибрати мову
diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml
index 36c5265d..9cc214de 100644
--- a/app/src/main/res/values-ur/strings.xml
+++ b/app/src/main/res/values-ur/strings.xml
@@ -389,4 +389,7 @@
دیکھیں کہ یہاں نوٹ چھوڑے گئے ہیں یا نہیں
یہاں 1 نوٹ چھوڑا گیا ہے — پڑھنے کے لیے تھپتھپائیں
یہاں %d نوٹ چھوڑے گئے ہیں — پڑھنے کے لیے تھپتھپائیں
+ زبان
+ سسٹم ڈیفالٹ
+ زبان منتخب کریں
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 20028f38..226eae82 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -376,4 +376,7 @@
kiểm tra ghi chú để lại ở đây
có 1 ghi chú để lại ở đây — chạm để đọc
có %d ghi chú để lại ở đây — chạm để đọc
+ Ngôn ngữ
+ Mặc định hệ thống
+ Chọn ngôn ngữ
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 438131b0..3a51c135 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -56,4 +56,7 @@
查看这里留下的留言
这里留有 1 条留言 — 点按阅读
这里留有 %d 条留言 — 点按阅读
+ 语言
+ 跟随系统
+ 选择语言
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 328696a0..ce778247 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -56,4 +56,7 @@
查看這裡留下的留言
這裡留有 1 則留言 — 點按閱讀
這裡留有 %d 則留言 — 點按閱讀
+ 語言
+ 跟隨系統
+ 選擇語言
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index d6eada8c..b142e9d2 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -406,4 +406,7 @@
查看这里留下的留言
这里留有 1 条留言 — 点按阅读
这里留有 %d 条留言 — 点按阅读
+ 语言
+ 跟随系统
+ 选择语言
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a12482ca..c415a569 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -567,4 +567,7 @@
Bluetooth Recommended
+ Language
+ System default
+ Select language
diff --git a/app/src/main/res/xml/locales_config.xml b/app/src/main/res/xml/locales_config.xml
new file mode 100644
index 00000000..4bfd7251
--- /dev/null
+++ b/app/src/main/res/xml/locales_config.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/test/kotlin/com/bitchat/android/ui/LanguageCatalogContractTest.kt b/app/src/test/kotlin/com/bitchat/android/ui/LanguageCatalogContractTest.kt
new file mode 100644
index 00000000..1fa49ea8
--- /dev/null
+++ b/app/src/test/kotlin/com/bitchat/android/ui/LanguageCatalogContractTest.kt
@@ -0,0 +1,71 @@
+package com.bitchat.android.ui
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import java.io.File
+
+class LanguageCatalogContractTest {
+ private val resourcesDirectory = File("src/main/res")
+
+ @Test
+ fun `locale config contains every shipped locale exactly once`() {
+ val shippedLanguageTags = resourcesDirectory
+ .listFiles()
+ .orEmpty()
+ .filter { it.isDirectory && it.name.startsWith("values-") }
+ .mapNotNull { directory -> directory.name.toLanguageTagOrNull() }
+ .toSet()
+
+ val configuredLanguageTags = Regex("""android:name="([^"]+)"""")
+ .findAll(File(resourcesDirectory, "xml/locales_config.xml").readText())
+ .map { match -> match.groupValues[1] }
+ .toList()
+
+ assertEquals(
+ "Locale configuration must not contain duplicate entries",
+ configuredLanguageTags.toSet().size,
+ configuredLanguageTags.size,
+ )
+ assertEquals(shippedLanguageTags, configuredLanguageTags.toSet())
+ }
+
+ @Test
+ fun `language picker strings exist in every locale pack`() {
+ val requiredKeys = setOf(
+ "about_language",
+ "about_system_default",
+ "about_select_language",
+ )
+
+ resourcesDirectory
+ .listFiles()
+ .orEmpty()
+ .filter { directory ->
+ directory.isDirectory &&
+ (directory.name == "values" || directory.name.toLanguageTagOrNull() != null)
+ }
+ .forEach { directory ->
+ val strings = File(directory, "strings.xml").readText()
+ requiredKeys.forEach { key ->
+ assertTrue(
+ "$key is missing from ${directory.name}/strings.xml",
+ """name="$key""" in strings,
+ )
+ }
+ }
+ }
+
+ private fun String.toLanguageTagOrNull(): String? {
+ if (!startsWith("values-") || this == "values-night") return null
+ return removePrefix("values-")
+ .split("-")
+ .joinToString("-") { qualifier ->
+ if (qualifier.startsWith("r") && qualifier.length == 3) {
+ qualifier.drop(1)
+ } else {
+ qualifier
+ }
+ }
+ }
+}