Merge pull request #400 from HDDen/dev

Feature Request: alternative compression by replace some 2-byte symbols by 1-byte latin analogs (named Cyr2Lat)
This commit is contained in:
zjs81
2026-05-09 17:45:31 -07:00
committed by GitHub
47 changed files with 2366 additions and 14 deletions
+152 -4
View File
@@ -16,6 +16,7 @@ import '../models/message.dart';
import '../models/path_selection.dart';
import '../models/translation_support.dart';
import '../helpers/reaction_helper.dart';
import '../helpers/cyr2lat.dart';
import '../helpers/smaz.dart';
import '../services/app_debug_log_service.dart';
import '../services/ble_debug_log_service.dart';
@@ -290,9 +291,13 @@ class MeshCoreConnector extends ChangeNotifier {
final UnreadStore _unreadStore = UnreadStore();
List<Channel> _cachedChannels = [];
final Map<int, bool> _channelSmazEnabled = {};
final Map<int, bool> _channelCyr2LatEnabled = {};
final Map<int, String?> _channelCyr2LatProfileId = {};
bool _lastSentWasCliCommand =
false; // Track if last sent message was a CLI command
final Map<String, bool> _contactSmazEnabled = {};
final Map<String, bool> _contactCyr2LatEnabled = {};
final Map<String, String?> _contactCyr2LatProfileId = {};
final Set<String> _knownContactKeys = {};
final Map<String, int> _contactUnreadCount = {};
final Map<String, RepeaterBatterySnapshot> _repeaterBatterySnapshots = {};
@@ -625,6 +630,20 @@ class MeshCoreConnector extends ChangeNotifier {
_ensureContactSmazSettingLoaded(contactKeyHex);
}
bool isChannelCyr2LatEnabled(int channelIndex) {
_ensureChannelCyr2LatSettingLoaded(channelIndex);
return _channelCyr2LatEnabled[channelIndex] ?? false;
}
bool isContactCyr2LatEnabled(String contactKeyHex) {
_ensureContactCyr2LatSettingLoaded(contactKeyHex);
return _contactCyr2LatEnabled[contactKeyHex] ?? false;
}
void ensureContactCyr2LatSettingLoaded(String contactKeyHex) {
_ensureContactCyr2LatSettingLoaded(contactKeyHex);
}
Future<void> loadUnreadState() async {
_contactUnreadCount
..clear()
@@ -721,6 +740,10 @@ class MeshCoreConnector extends ChangeNotifier {
Future<void> setChannelSmazEnabled(int channelIndex, bool enabled) async {
_channelSmazEnabled[channelIndex] = enabled;
if (enabled) {
_channelCyr2LatEnabled[channelIndex] = false;
await _channelSettingsStore.saveCyr2LatEnabled(channelIndex, false);
}
await _channelSettingsStore.saveSmazEnabled(channelIndex, enabled);
notifyListeners();
}
@@ -731,6 +754,25 @@ class MeshCoreConnector extends ChangeNotifier {
notifyListeners();
}
Future<void> setChannelCyr2LatEnabled(int channelIndex, bool enabled) async {
_channelCyr2LatEnabled[channelIndex] = enabled;
if (enabled) {
_channelSmazEnabled[channelIndex] = false;
await _channelSettingsStore.saveSmazEnabled(channelIndex, false);
}
await _channelSettingsStore.saveCyr2LatEnabled(channelIndex, enabled);
notifyListeners();
}
Future<void> setContactCyr2LatEnabled(
String contactKeyHex,
bool enabled,
) async {
_contactCyr2LatEnabled[contactKeyHex] = enabled;
await _contactSettingsStore.saveCyr2LatEnabled(contactKeyHex, enabled);
notifyListeners();
}
Future<void> _loadChannelOrder() async {
_channelOrder = await _channelOrderStore.loadChannelOrder();
_applyChannelOrder();
@@ -867,6 +909,7 @@ class MeshCoreConnector extends ChangeNotifier {
..addAll(cached);
for (final contact in cached) {
_ensureContactSmazSettingLoaded(contact.publicKeyHex);
_ensureContactCyr2LatSettingLoaded(contact.publicKeyHex);
}
}
@@ -879,9 +922,12 @@ class MeshCoreConnector extends ChangeNotifier {
Future<void> loadChannelSettings({int? maxChannels}) async {
_channelSmazEnabled.clear();
_channelCyr2LatEnabled.clear();
final channelCount = maxChannels ?? _maxChannels;
for (int i = 0; i < channelCount; i++) {
_channelSmazEnabled[i] = await _channelSettingsStore.loadSmazEnabled(i);
_channelCyr2LatEnabled[i] = await _channelSettingsStore
.loadCyr2LatEnabled(i);
}
}
@@ -4507,6 +4553,72 @@ class MeshCoreConnector extends ChangeNotifier {
});
}
void _ensureContactCyr2LatSettingLoaded(String contactKeyHex) {
if (_contactCyr2LatEnabled.containsKey(contactKeyHex)) return;
_contactSettingsStore.loadCyr2LatEnabled(contactKeyHex).then((enabled) {
if (_contactCyr2LatEnabled[contactKeyHex] == enabled) return;
_contactCyr2LatEnabled[contactKeyHex] = enabled;
notifyListeners();
});
}
void _ensureContactCyr2LatProfileLoaded(String contactKeyHex) {
if (_contactCyr2LatProfileId.containsKey(contactKeyHex)) return;
_contactSettingsStore.loadCyr2LatProfileId(contactKeyHex).then((profileId) {
if (_contactCyr2LatProfileId[contactKeyHex] == profileId) return;
_contactCyr2LatProfileId[contactKeyHex] = profileId;
notifyListeners();
});
}
void _ensureChannelCyr2LatSettingLoaded(int channelIndex) {
if (_channelCyr2LatEnabled.containsKey(channelIndex)) return;
_channelSettingsStore.loadCyr2LatEnabled(channelIndex).then((enabled) {
if (_channelCyr2LatEnabled[channelIndex] == enabled) return;
_channelCyr2LatEnabled[channelIndex] = enabled;
notifyListeners();
});
}
void _ensureChannelCyr2LatProfileLoaded(int channelIndex) {
if (_channelCyr2LatProfileId.containsKey(channelIndex)) return;
_channelSettingsStore.loadCyr2LatProfileId(channelIndex).then((profileId) {
if (_channelCyr2LatProfileId[channelIndex] == profileId) return;
_channelCyr2LatProfileId[channelIndex] = profileId;
notifyListeners();
});
}
String? getChannelCyr2LatProfileId(int channelIndex) {
_ensureChannelCyr2LatProfileLoaded(channelIndex);
return _channelCyr2LatProfileId[channelIndex];
}
Future<void> setChannelCyr2LatProfileId(
int channelIndex,
String? profileId,
) async {
if (_channelCyr2LatProfileId[channelIndex] == profileId) return;
_channelCyr2LatProfileId[channelIndex] = profileId;
await _channelSettingsStore.saveCyr2LatProfileId(channelIndex, profileId);
notifyListeners();
}
String? getContactCyr2LatProfileId(String contactKeyHex) {
_ensureContactCyr2LatProfileLoaded(contactKeyHex);
return _contactCyr2LatProfileId[contactKeyHex];
}
Future<void> setContactCyr2LatProfileId(
String contactKeyHex,
String? profileId,
) async {
if (_contactCyr2LatProfileId[contactKeyHex] == profileId) return;
_contactCyr2LatProfileId[contactKeyHex] = profileId;
await _contactSettingsStore.saveCyr2LatProfileId(contactKeyHex, profileId);
notifyListeners();
}
/// Prepares contact outbound text by applying SMAZ encoding if enabled.
/// This should be used to transform text before computing ACK hashes.
String prepareContactOutboundText(Contact contact, String text) {
@@ -4515,8 +4627,26 @@ class MeshCoreConnector extends ChangeNotifier {
trimmed.startsWith('g:') ||
trimmed.startsWith('m:') ||
trimmed.startsWith('V1|');
if (!isStructuredPayload && isContactSmazEnabled(contact.publicKeyHex)) {
return Smaz.encodeIfSmaller(text);
if (!isStructuredPayload) {
if (isContactSmazEnabled(contact.publicKeyHex)) {
return Smaz.encodeIfSmaller(text);
} else if (isContactCyr2LatEnabled(contact.publicKeyHex)) {
final profileId = getContactCyr2LatProfileId(contact.publicKeyHex);
final profile = profileId != null && _appSettingsService != null
? _appSettingsService!.getCyr2LatProfileById(profileId)
: null;
if (profile != null) {
Cyr2Lat.setCharMap(profile.charMap);
} else {
// Use global profile
final globalProfile = _appSettingsService
?.getSelectedCyr2LatProfile();
if (globalProfile != null) {
Cyr2Lat.setCharMap(globalProfile.charMap);
}
}
return Cyr2Lat.encode(text);
}
}
return text;
}
@@ -4525,8 +4655,26 @@ class MeshCoreConnector extends ChangeNotifier {
final trimmed = text.trim();
final isStructuredPayload =
trimmed.startsWith('g:') || trimmed.startsWith('m:');
if (!isStructuredPayload && isChannelSmazEnabled(channelIndex)) {
return Smaz.encodeIfSmaller(text);
if (!isStructuredPayload) {
if (isChannelSmazEnabled(channelIndex)) {
return Smaz.encodeIfSmaller(text);
} else if (isChannelCyr2LatEnabled(channelIndex)) {
final profileId = getChannelCyr2LatProfileId(channelIndex);
final profile = profileId != null && _appSettingsService != null
? _appSettingsService!.getCyr2LatProfileById(profileId)
: null;
if (profile != null) {
Cyr2Lat.setCharMap(profile.charMap);
} else {
// Use global profile
final globalProfile = _appSettingsService
?.getSelectedCyr2LatProfile();
if (globalProfile != null) {
Cyr2Lat.setCharMap(globalProfile.charMap);
}
}
return Cyr2Lat.encode(text);
}
}
return text;
}
+63
View File
@@ -0,0 +1,63 @@
class Cyr2Lat {
static Map<String, String> _charMap = {
'А': 'A',
'В': 'B',
'Е': 'E',
'Ё': 'E',
'З': '3',
'К': 'K',
'М': 'M',
'Н': 'H',
'О': 'O',
'Р': 'P',
'С': 'C',
'Т': 'T',
'Х': 'X',
'Ь': 'b',
'а': 'a',
'е': 'e',
'ё': 'e',
'о': 'o',
'р': 'p',
'с': 'c',
'у': 'y',
'х': 'x',
};
static final RegExp _prefixRegExp = RegExp(r'\@\[[\S\s]+\] ');
static void setCharMap(Map<String, String> charMap) {
_charMap = Map.from(charMap);
}
static String encode(String text) {
if (text.isEmpty) return text;
final buffer = StringBuffer();
final senderName = extractSenderName(text);
final msgText = removeSenderName(text);
for (final rune in msgText.runes) {
final char = String.fromCharCode(rune);
buffer.write(_charMap[char] ?? char);
}
return senderName + buffer.toString();
}
static String removeSenderName(String text) {
final match = _prefixRegExp.matchAsPrefix(text);
if (match != null) {
return text.substring(match.end);
}
return text;
}
static String extractSenderName(String text) {
final match = _prefixRegExp.matchAsPrefix(text);
if (match != null) {
return match.group(0) ?? '';
}
return '';
}
}
+16
View File
@@ -387,6 +387,22 @@
}
},
"channels_smazCompression": "Компресия SMAZ",
"channels_cyr2latCompression": "Компресия Cyr2Lat",
"channels_cyr2latCompressionDscr": "Заменя някои кирилични символи с латиница при изпращане.",
"channels_cyr2latSettingsHeading": "Настройки на Cyr2Lat",
"channels_cyr2latSettingsSubheading": "Списък със замествания",
"channels_cyr2latSettingsDscr": "Редактиране на JSON конфигурацията за заместване на символи",
"channels_cyr2latSettingsDialogHint": "JSON карта за замествания",
"channels_cyr2latSettingsDialogWrongJSON": "Неправилен JSON: {error}",
"settings_cyr2latProfileAdd": "Добавяне на профил Cyr2Lat",
"settings_cyr2latProfileName": "Име на профила",
"settings_cyr2latProfileNameEmpty": "Името на профила не може да бъде празно",
"settings_cyr2latProfileAdded": "Профилът е добавен успешно",
"settings_cyr2latProfileUpdated": "Профилът е актуализиран успешно",
"settings_cyr2latProfileEdit": "Редактиране на Cyr2Lat профил",
"settings_cyr2latProfileDelete": "Изтриване на профил Cyr2Lat",
"settings_cyr2latProfileDeleted": "Профилът беше изтрит успешно",
"settings_cyr2latProfileDeleteDscr": "Сигурен ли сте, че искате да изтриете профила \"{name}\"?",
"channels_channelUpdated": "Каналът \"{name}\" е актуализиран",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -387,6 +387,22 @@
}
},
"channels_smazCompression": "SMAZ-Komprimierung",
"channels_cyr2latCompression": "Cyr2Lat-Komprimierung",
"channels_cyr2latCompressionDscr": "Ersetzt einige kyrillische Zeichen durch lateinische Zeichen, wenn sie gesendet werden.",
"channels_cyr2latSettingsHeading": "Cyr2Lat-Einstellungen",
"channels_cyr2latSettingsSubheading": "Ersetzungsliste",
"channels_cyr2latSettingsDscr": "JSON-Konfiguration für die Zeichenersetzung bearbeiten",
"channels_cyr2latSettingsDialogHint": "JSON-Ersetzungstabelle",
"channels_cyr2latSettingsDialogWrongJSON": "Ungültiges JSON: {error}",
"settings_cyr2latProfileAdd": "Cyr2Lat-Profil hinzufügen",
"settings_cyr2latProfileName": "Profilname",
"settings_cyr2latProfileNameEmpty": "Der Profilname darf nicht leer sein",
"settings_cyr2latProfileAdded": "Profil erfolgreich hinzugefügt",
"settings_cyr2latProfileUpdated": "Profil erfolgreich aktualisiert",
"settings_cyr2latProfileEdit": "Cyr2Lat-Profil bearbeiten",
"settings_cyr2latProfileDelete": "Cyr2Lat-Profil löschen",
"settings_cyr2latProfileDeleted": "Profil erfolgreich gelöscht",
"settings_cyr2latProfileDeleteDscr": "Möchten Sie das Profil \"{name}\" wirklich löschen?",
"channels_channelUpdated": "Kanal \"{name}\" aktualisiert",
"@channels_channelUpdated": {
"placeholders": {
+28
View File
@@ -580,6 +580,18 @@
}
},
"channels_smazCompression": "SMAZ compression",
"channels_cyr2latCompression": "Cyr2Lat compression",
"channels_cyr2latCompressionDscr": "Replaces some Cyrillic characters with Latin characters when sending.",
"channels_cyr2latSettingsHeading": "Cyr2Lat Setup",
"channels_cyr2latSettingsSubheading": "List of replacements",
"channels_cyr2latSettingsDscr": "Edit the JSON configuration of character replacement",
"channels_cyr2latSettingsDialogHint": "JSON replacement map",
"channels_cyr2latSettingsDialogWrongJSON": "Invalid JSON: {error}",
"@channels_cyr2latSettingsDialogWrongJSON": {
"placeholders": {
"error": {}
}
},
"channels_channelUpdated": "Channel \"{name}\" updated",
"@channels_channelUpdated": {
"placeholders": {
@@ -588,6 +600,22 @@
}
}
},
"settings_cyr2latProfileAdd": "Add Cyr2Lat Profile",
"settings_cyr2latProfileName": "Profile Name",
"settings_cyr2latProfileNameEmpty": "Profile name cannot be empty",
"settings_cyr2latProfileAdded": "Profile added successfully",
"settings_cyr2latProfileUpdated": "Profile updated successfully",
"settings_cyr2latProfileEdit": "Edit Cyr2Lat Profile",
"settings_cyr2latProfileDelete": "Delete Cyr2Lat Profile",
"settings_cyr2latProfileDeleted": "Profile deleted successfully",
"settings_cyr2latProfileDeleteDscr": "Are you sure you want to delete the profile \"{name}\"?",
"@settings_cyr2latProfileDeleteDscr": {
"placeholders": {
"name": {
"type": "String"
}
}
},
"channels_publicChannelAdded": "Public channel added",
"channels_sortBy": "Sort by",
"channels_sortManual": "Manual",
+16
View File
@@ -387,6 +387,22 @@
}
},
"channels_smazCompression": "Compresión SMAZ",
"channels_cyr2latCompression": "Compresión Cyr2Lat",
"channels_cyr2latCompressionDscr": "Reemplaza algunos caracteres cirílicos con caracteres latinos al enviar.",
"channels_cyr2latSettingsHeading": "Configuración de Cyr2Lat",
"channels_cyr2latSettingsSubheading": "Lista de sustituciones",
"channels_cyr2latSettingsDscr": "Editar la configuración JSON de sustitución de caracteres",
"channels_cyr2latSettingsDialogHint": "Mapa JSON de sustituciones",
"channels_cyr2latSettingsDialogWrongJSON": "JSON incorrecto: {error}",
"settings_cyr2latProfileAdd": "Añadir perfil Cyr2Lat",
"settings_cyr2latProfileName": "Nombre del perfil",
"settings_cyr2latProfileNameEmpty": "El nombre del perfil no puede estar vacío",
"settings_cyr2latProfileAdded": "Perfil añadido correctamente",
"settings_cyr2latProfileUpdated": "Perfil actualizado correctamente",
"settings_cyr2latProfileEdit": "Editar perfil Cyr2Lat",
"settings_cyr2latProfileDelete": "Eliminar perfil Cyr2Lat",
"settings_cyr2latProfileDeleted": "Perfil eliminado correctamente",
"settings_cyr2latProfileDeleteDscr": "¿Está seguro de que desea eliminar el perfil \"{name}\"?",
"channels_channelUpdated": "Canal \"{name}\" actualizado",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -387,6 +387,22 @@
}
},
"channels_smazCompression": "Compression SMAZ",
"channels_cyr2latCompression": "Compression Cyr2Lat",
"channels_cyr2latCompressionDscr": "Remplace certains caractères cyrilliques par des caractères latins lors de l'envoi.",
"channels_cyr2latSettingsHeading" : "Paramètres Cyr2Lat",
"channels_cyr2latSettingsSubheading" : "Liste des remplacements",
"channels_cyr2latSettingsDscr" : "Modifier la configuration JSON des remplacements de caractères",
"channels_cyr2latSettingsDialogHint": "Tableau de remplacement JSON",
"channels_cyr2latSettingsDialogWrongJSON": "JSON incorrect : {error}",
"settings_cyr2latProfileAdd" : "Ajouter un profil Cyr2Lat",
"settings_cyr2latProfileName" : "Nom du profil",
"settings_cyr2latProfileNameEmpty" : "Le nom du profil ne peut pas être vide",
"settings_cyr2latProfileAdded": "Profil ajouté avec succès",
"settings_cyr2latProfileUpdated": "Profil mis à jour avec succès",
"settings_cyr2latProfileEdit": "Modifier le profil Cyr2Lat",
"settings_cyr2latProfileDelete" : "Supprimer le profil Cyr2Lat",
"settings_cyr2latProfileDeleted" : "Profil supprimé avec succès",
"settings_cyr2latProfileDeleteDscr" : "Êtes-vous sûr de vouloir supprimer le profil \"{name}\"?",
"channels_channelUpdated": "Le canal \"{name}\" a été mis à jour",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -547,6 +547,22 @@
}
},
"channels_smazCompression": "SMAZ kompresszió",
"channels_cyr2latCompression": "Cyr2Lat kompresszió",
"channels_cyr2latCompressionDscr": "Néhány Cirill betűt Latin betűkkel helyettesít küldéskor.",
"channels_cyr2latSettingsHeading": "Cyr2Lat beállítások",
"channels_cyr2latSettingsSubheading": "Helyettesítési lista",
"channels_cyr2latSettingsDscr": "A karakterhelyettesítési JSON-konfiguráció szerkesztése",
"channels_cyr2latSettingsDialogHint": "JSON-csere táblázat",
"channels_cyr2latSettingsDialogWrongJSON": "Hibás JSON: {error}",
"settings_cyr2latProfileAdd": "Cyr2Lat-profil hozzáadása",
"settings_cyr2latProfileName": "Profil neve",
"settings_cyr2latProfileNameEmpty": "A profil neve nem lehet üres",
"settings_cyr2latProfileAdded": "A profil hozzáadása sikeres",
"settings_cyr2latProfileUpdated": "A profil frissítése sikeres",
"settings_cyr2latProfileEdit": "Cyr2Lat profil szerkesztése",
"settings_cyr2latProfileDelete": "Cyr2Lat profil törlése",
"settings_cyr2latProfileDeleted": "A profil törlése sikeresen megtörtént",
"settings_cyr2latProfileDeleteDscr": "Biztosan törölni szeretné a \"{name}\" profilt?",
"channels_channelUpdated": "A {name} csatorna frissítve",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -387,6 +387,22 @@
}
},
"channels_smazCompression": "Compressione SMAZ",
"channels_cyr2latCompression": "Compressione Cyr2Lat",
"channels_cyr2latCompressionDscr": "Sostituisce alcuni caratteri cirillici con caratteri latini durante l'invio.",
"channels_cyr2latSettingsHeading": "Impostazioni Cyr2Lat",
"channels_cyr2latSettingsSubheading": "Elenco delle sostituzioni",
"channels_cyr2latSettingsDscr": "Modifica la configurazione JSON delle sostituzioni dei caratteri",
"channels_cyr2latSettingsDialogHint": "Mappa JSON delle sostituzioni",
"channels_cyr2latSettingsDialogWrongJSON": "JSON non corretto: {error}",
"settings_cyr2latProfileAdd": "Aggiungi profilo Cyr2Lat",
"settings_cyr2latProfileName": "Nome profilo",
"settings_cyr2latProfileNameEmpty": "Il nome del profilo non può essere vuoto",
"settings_cyr2latProfileAdded": "Profilo aggiunto con successo",
"settings_cyr2latProfileUpdated": "Profilo aggiornato con successo",
"settings_cyr2latProfileEdit": "Modifica profilo Cyr2Lat",
"settings_cyr2latProfileDelete": "Elimina profilo Cyr2Lat",
"settings_cyr2latProfileDeleted": "Profilo eliminato con successo",
"settings_cyr2latProfileDeleteDscr": "Sei sicuro di voler eliminare il profilo \"{name}\"?",
"channels_channelUpdated": "Canale \"{name}\" aggiornato",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -547,6 +547,22 @@
}
},
"channels_smazCompression": "SMAZ 圧縮",
"channels_cyr2latCompression": "Cyr2Lat 圧縮",
"channels_cyr2latCompressionDscr": "送信時に一部のキリル文字をラテン文字に置き換えます。",
"channels_cyr2latSettingsHeading": "cyr2latの設定",
"channels_cyr2latSettingsSubheading": "置換リスト",
"channels_cyr2latSettingsDscr": "文字置換のJSON設定を編集する",
"channels_cyr2latSettingsDialogHint": "JSON置換マップ",
"channels_cyr2latSettingsDialogWrongJSON": "不正なJSON: {error}",
"settings_cyr2latProfileAdd": "Cyr2Latプロファイルの追加",
"settings_cyr2latProfileName": "プロファイル名",
"settings_cyr2latProfileNameEmpty": "プロファイル名は空にできません",
"settings_cyr2latProfileAdded": "プロファイルが正常に追加されました",
"settings_cyr2latProfileUpdated": "プロファイルの更新に成功しました",
"settings_cyr2latProfileEdit": "Cyr2Latプロファイルを編集",
"settings_cyr2latProfileDelete": "Cyr2Latプロファイルを削除",
"settings_cyr2latProfileDeleted": "プロファイルの削除に成功しました",
"settings_cyr2latProfileDeleteDscr": "プロファイル \"{name}\" を削除してもよろしいですか?",
"channels_channelUpdated": "チャンネル「{name}」が更新されました",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -547,6 +547,22 @@
}
},
"channels_smazCompression": "SMAZ 압축",
"channels_cyr2latCompression": "Cyr2Lat 압축",
"channels_cyr2latCompressionDscr": "보낼 때 일부 키릴 문자를 라틴 문자로 바꿉니다.",
"channels_cyr2latSettingsHeading": "Cyr2Lat 설정",
"channels_cyr2latSettingsSubheading": "변환 목록",
"channels_cyr2latSettingsDscr": "문자 변환 JSON 구성 편집",
"channels_cyr2latSettingsDialogHint": "JSON 변환 맵",
"channels_cyr2latSettingsDialogWrongJSON": "잘못된 JSON: {error}",
"settings_cyr2latProfileAdd": "Cyr2Lat 프로필 추가",
"settings_cyr2latProfileName": "프로필 이름",
"settings_cyr2latProfileNameEmpty": "프로필 이름은 비워둘 수 없습니다",
"settings_cyr2latProfileAdded": "프로필이 성공적으로 추가되었습니다",
"settings_cyr2latProfileUpdated": "프로필이 성공적으로 업데이트되었습니다",
"settings_cyr2latProfileEdit": "Cyr2Lat 프로필 편집",
"settings_cyr2latProfileDelete": "Cyr2Lat 프로필 삭제",
"settings_cyr2latProfileDeleted": "프로필이 성공적으로 삭제되었습니다",
"settings_cyr2latProfileDeleteDscr": "\"{name}\" 프로필을 삭제하시겠습니까?",
"channels_channelUpdated": "채널 \"{name}\"이 업데이트되었습니다.",
"@channels_channelUpdated": {
"placeholders": {
+96
View File
@@ -2182,12 +2182,108 @@ abstract class AppLocalizations {
/// **'SMAZ compression'**
String get channels_smazCompression;
/// No description provided for @channels_cyr2latCompression.
///
/// In en, this message translates to:
/// **'Cyr2Lat compression'**
String get channels_cyr2latCompression;
/// No description provided for @channels_cyr2latCompressionDscr.
///
/// In en, this message translates to:
/// **'Replaces some Cyrillic characters with Latin characters when sending.'**
String get channels_cyr2latCompressionDscr;
/// No description provided for @channels_cyr2latSettingsHeading.
///
/// In en, this message translates to:
/// **'Cyr2Lat Setup'**
String get channels_cyr2latSettingsHeading;
/// No description provided for @channels_cyr2latSettingsSubheading.
///
/// In en, this message translates to:
/// **'List of replacements'**
String get channels_cyr2latSettingsSubheading;
/// No description provided for @channels_cyr2latSettingsDscr.
///
/// In en, this message translates to:
/// **'Edit the JSON configuration of character replacement'**
String get channels_cyr2latSettingsDscr;
/// No description provided for @channels_cyr2latSettingsDialogHint.
///
/// In en, this message translates to:
/// **'JSON replacement map'**
String get channels_cyr2latSettingsDialogHint;
/// No description provided for @channels_cyr2latSettingsDialogWrongJSON.
///
/// In en, this message translates to:
/// **'Invalid JSON: {error}'**
String channels_cyr2latSettingsDialogWrongJSON(Object error);
/// No description provided for @channels_channelUpdated.
///
/// In en, this message translates to:
/// **'Channel \"{name}\" updated'**
String channels_channelUpdated(String name);
/// No description provided for @settings_cyr2latProfileAdd.
///
/// In en, this message translates to:
/// **'Add Cyr2Lat Profile'**
String get settings_cyr2latProfileAdd;
/// No description provided for @settings_cyr2latProfileName.
///
/// In en, this message translates to:
/// **'Profile Name'**
String get settings_cyr2latProfileName;
/// No description provided for @settings_cyr2latProfileNameEmpty.
///
/// In en, this message translates to:
/// **'Profile name cannot be empty'**
String get settings_cyr2latProfileNameEmpty;
/// No description provided for @settings_cyr2latProfileAdded.
///
/// In en, this message translates to:
/// **'Profile added successfully'**
String get settings_cyr2latProfileAdded;
/// No description provided for @settings_cyr2latProfileUpdated.
///
/// In en, this message translates to:
/// **'Profile updated successfully'**
String get settings_cyr2latProfileUpdated;
/// No description provided for @settings_cyr2latProfileEdit.
///
/// In en, this message translates to:
/// **'Edit Cyr2Lat Profile'**
String get settings_cyr2latProfileEdit;
/// No description provided for @settings_cyr2latProfileDelete.
///
/// In en, this message translates to:
/// **'Delete Cyr2Lat Profile'**
String get settings_cyr2latProfileDelete;
/// No description provided for @settings_cyr2latProfileDeleted.
///
/// In en, this message translates to:
/// **'Profile deleted successfully'**
String get settings_cyr2latProfileDeleted;
/// No description provided for @settings_cyr2latProfileDeleteDscr.
///
/// In en, this message translates to:
/// **'Are you sure you want to delete the profile \"{name}\"?'**
String settings_cyr2latProfileDeleteDscr(String name);
/// No description provided for @channels_publicChannelAdded.
///
/// In en, this message translates to:
+56
View File
@@ -1178,11 +1178,67 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get channels_smazCompression => 'Компресия SMAZ';
@override
String get channels_cyr2latCompression => 'Компресия Cyr2Lat';
@override
String get channels_cyr2latCompressionDscr =>
'Заменя някои кирилични символи с латиница при изпращане.';
@override
String get channels_cyr2latSettingsHeading => 'Настройки на Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Списък със замествания';
@override
String get channels_cyr2latSettingsDscr =>
'Редактиране на JSON конфигурацията за заместване на символи';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON карта за замествания';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'Неправилен JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Каналът \"$name\" е актуализиран';
}
@override
String get settings_cyr2latProfileAdd => 'Добавяне на профил Cyr2Lat';
@override
String get settings_cyr2latProfileName => 'Име на профила';
@override
String get settings_cyr2latProfileNameEmpty =>
'Името на профила не може да бъде празно';
@override
String get settings_cyr2latProfileAdded => 'Профилът е добавен успешно';
@override
String get settings_cyr2latProfileUpdated =>
'Профилът е актуализиран успешно';
@override
String get settings_cyr2latProfileEdit => 'Редактиране на Cyr2Lat профил';
@override
String get settings_cyr2latProfileDelete => 'Изтриване на профил Cyr2Lat';
@override
String get settings_cyr2latProfileDeleted => 'Профилът беше изтрит успешно';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Сигурен ли сте, че искате да изтриете профила \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'Публичен канал добавен';
+56
View File
@@ -1174,11 +1174,67 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get channels_smazCompression => 'SMAZ-Komprimierung';
@override
String get channels_cyr2latCompression => 'Cyr2Lat-Komprimierung';
@override
String get channels_cyr2latCompressionDscr =>
'Ersetzt einige kyrillische Zeichen durch lateinische Zeichen, wenn sie gesendet werden.';
@override
String get channels_cyr2latSettingsHeading => 'Cyr2Lat-Einstellungen';
@override
String get channels_cyr2latSettingsSubheading => 'Ersetzungsliste';
@override
String get channels_cyr2latSettingsDscr =>
'JSON-Konfiguration für die Zeichenersetzung bearbeiten';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON-Ersetzungstabelle';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'Ungültiges JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Kanal \"$name\" aktualisiert';
}
@override
String get settings_cyr2latProfileAdd => 'Cyr2Lat-Profil hinzufügen';
@override
String get settings_cyr2latProfileName => 'Profilname';
@override
String get settings_cyr2latProfileNameEmpty =>
'Der Profilname darf nicht leer sein';
@override
String get settings_cyr2latProfileAdded => 'Profil erfolgreich hinzugefügt';
@override
String get settings_cyr2latProfileUpdated =>
'Profil erfolgreich aktualisiert';
@override
String get settings_cyr2latProfileEdit => 'Cyr2Lat-Profil bearbeiten';
@override
String get settings_cyr2latProfileDelete => 'Cyr2Lat-Profil löschen';
@override
String get settings_cyr2latProfileDeleted => 'Profil erfolgreich gelöscht';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Möchten Sie das Profil \"$name\" wirklich löschen?';
}
@override
String get channels_publicChannelAdded => 'Öffentlicher Kanal hinzugefügt';
+54
View File
@@ -1154,11 +1154,65 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get channels_smazCompression => 'SMAZ compression';
@override
String get channels_cyr2latCompression => 'Cyr2Lat compression';
@override
String get channels_cyr2latCompressionDscr =>
'Replaces some Cyrillic characters with Latin characters when sending.';
@override
String get channels_cyr2latSettingsHeading => 'Cyr2Lat Setup';
@override
String get channels_cyr2latSettingsSubheading => 'List of replacements';
@override
String get channels_cyr2latSettingsDscr =>
'Edit the JSON configuration of character replacement';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON replacement map';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'Invalid JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Channel \"$name\" updated';
}
@override
String get settings_cyr2latProfileAdd => 'Add Cyr2Lat Profile';
@override
String get settings_cyr2latProfileName => 'Profile Name';
@override
String get settings_cyr2latProfileNameEmpty => 'Profile name cannot be empty';
@override
String get settings_cyr2latProfileAdded => 'Profile added successfully';
@override
String get settings_cyr2latProfileUpdated => 'Profile updated successfully';
@override
String get settings_cyr2latProfileEdit => 'Edit Cyr2Lat Profile';
@override
String get settings_cyr2latProfileDelete => 'Delete Cyr2Lat Profile';
@override
String get settings_cyr2latProfileDeleted => 'Profile deleted successfully';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Are you sure you want to delete the profile \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'Public channel added';
+56
View File
@@ -1176,11 +1176,67 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get channels_smazCompression => 'Compresión SMAZ';
@override
String get channels_cyr2latCompression => 'Compresión Cyr2Lat';
@override
String get channels_cyr2latCompressionDscr =>
'Reemplaza algunos caracteres cirílicos con caracteres latinos al enviar.';
@override
String get channels_cyr2latSettingsHeading => 'Configuración de Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Lista de sustituciones';
@override
String get channels_cyr2latSettingsDscr =>
'Editar la configuración JSON de sustitución de caracteres';
@override
String get channels_cyr2latSettingsDialogHint => 'Mapa JSON de sustituciones';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'JSON incorrecto: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Canal \"$name\" actualizado';
}
@override
String get settings_cyr2latProfileAdd => 'Añadir perfil Cyr2Lat';
@override
String get settings_cyr2latProfileName => 'Nombre del perfil';
@override
String get settings_cyr2latProfileNameEmpty =>
'El nombre del perfil no puede estar vacío';
@override
String get settings_cyr2latProfileAdded => 'Perfil añadido correctamente';
@override
String get settings_cyr2latProfileUpdated =>
'Perfil actualizado correctamente';
@override
String get settings_cyr2latProfileEdit => 'Editar perfil Cyr2Lat';
@override
String get settings_cyr2latProfileDelete => 'Eliminar perfil Cyr2Lat';
@override
String get settings_cyr2latProfileDeleted => 'Perfil eliminado correctamente';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return '¿Está seguro de que desea eliminar el perfil \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'Canal público añadido';
+56
View File
@@ -1181,11 +1181,67 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get channels_smazCompression => 'Compression SMAZ';
@override
String get channels_cyr2latCompression => 'Compression Cyr2Lat';
@override
String get channels_cyr2latCompressionDscr =>
'Remplace certains caractères cyrilliques par des caractères latins lors de l\'envoi.';
@override
String get channels_cyr2latSettingsHeading => 'Paramètres Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Liste des remplacements';
@override
String get channels_cyr2latSettingsDscr =>
'Modifier la configuration JSON des remplacements de caractères';
@override
String get channels_cyr2latSettingsDialogHint =>
'Tableau de remplacement JSON';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'JSON incorrect : $error';
}
@override
String channels_channelUpdated(String name) {
return 'Le canal \"$name\" a été mis à jour';
}
@override
String get settings_cyr2latProfileAdd => 'Ajouter un profil Cyr2Lat';
@override
String get settings_cyr2latProfileName => 'Nom du profil';
@override
String get settings_cyr2latProfileNameEmpty =>
'Le nom du profil ne peut pas être vide';
@override
String get settings_cyr2latProfileAdded => 'Profil ajouté avec succès';
@override
String get settings_cyr2latProfileUpdated => 'Profil mis à jour avec succès';
@override
String get settings_cyr2latProfileEdit => 'Modifier le profil Cyr2Lat';
@override
String get settings_cyr2latProfileDelete => 'Supprimer le profil Cyr2Lat';
@override
String get settings_cyr2latProfileDeleted => 'Profil supprimé avec succès';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Êtes-vous sûr de vouloir supprimer le profil \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'Le canal public a été ajouté';
+55
View File
@@ -1181,11 +1181,66 @@ class AppLocalizationsHu extends AppLocalizations {
@override
String get channels_smazCompression => 'SMAZ kompresszió';
@override
String get channels_cyr2latCompression => 'Cyr2Lat kompresszió';
@override
String get channels_cyr2latCompressionDscr =>
'Néhány Cirill betűt Latin betűkkel helyettesít küldéskor.';
@override
String get channels_cyr2latSettingsHeading => 'Cyr2Lat beállítások';
@override
String get channels_cyr2latSettingsSubheading => 'Helyettesítési lista';
@override
String get channels_cyr2latSettingsDscr =>
'A karakterhelyettesítési JSON-konfiguráció szerkesztése';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON-csere táblázat';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'Hibás JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'A $name csatorna frissítve';
}
@override
String get settings_cyr2latProfileAdd => 'Cyr2Lat-profil hozzáadása';
@override
String get settings_cyr2latProfileName => 'Profil neve';
@override
String get settings_cyr2latProfileNameEmpty => 'A profil neve nem lehet üres';
@override
String get settings_cyr2latProfileAdded => 'A profil hozzáadása sikeres';
@override
String get settings_cyr2latProfileUpdated => 'A profil frissítése sikeres';
@override
String get settings_cyr2latProfileEdit => 'Cyr2Lat profil szerkesztése';
@override
String get settings_cyr2latProfileDelete => 'Cyr2Lat profil törlése';
@override
String get settings_cyr2latProfileDeleted =>
'A profil törlése sikeresen megtörtént';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Biztosan törölni szeretné a \"$name\" profilt?';
}
@override
String get channels_publicChannelAdded => 'A nyilvános csatorna hozzáadva';
+57
View File
@@ -1177,11 +1177,68 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get channels_smazCompression => 'Compressione SMAZ';
@override
String get channels_cyr2latCompression => 'Compressione Cyr2Lat';
@override
String get channels_cyr2latCompressionDscr =>
'Sostituisce alcuni caratteri cirillici con caratteri latini durante l\'invio.';
@override
String get channels_cyr2latSettingsHeading => 'Impostazioni Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Elenco delle sostituzioni';
@override
String get channels_cyr2latSettingsDscr =>
'Modifica la configurazione JSON delle sostituzioni dei caratteri';
@override
String get channels_cyr2latSettingsDialogHint =>
'Mappa JSON delle sostituzioni';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'JSON non corretto: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Canale \"$name\" aggiornato';
}
@override
String get settings_cyr2latProfileAdd => 'Aggiungi profilo Cyr2Lat';
@override
String get settings_cyr2latProfileName => 'Nome profilo';
@override
String get settings_cyr2latProfileNameEmpty =>
'Il nome del profilo non può essere vuoto';
@override
String get settings_cyr2latProfileAdded => 'Profilo aggiunto con successo';
@override
String get settings_cyr2latProfileUpdated =>
'Profilo aggiornato con successo';
@override
String get settings_cyr2latProfileEdit => 'Modifica profilo Cyr2Lat';
@override
String get settings_cyr2latProfileDelete => 'Elimina profilo Cyr2Lat';
@override
String get settings_cyr2latProfileDeleted => 'Profilo eliminato con successo';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Sei sicuro di voler eliminare il profilo \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'Canale pubblico aggiunto';
+52
View File
@@ -1122,11 +1122,63 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get channels_smazCompression => 'SMAZ 圧縮';
@override
String get channels_cyr2latCompression => 'Cyr2Lat 圧縮';
@override
String get channels_cyr2latCompressionDscr => '送信時に一部のキリル文字をラテン文字に置き換えます。';
@override
String get channels_cyr2latSettingsHeading => 'cyr2latの設定';
@override
String get channels_cyr2latSettingsSubheading => '置換リスト';
@override
String get channels_cyr2latSettingsDscr => '文字置換のJSON設定を編集する';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON置換マップ';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return '不正なJSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'チャンネル「$name」が更新されました';
}
@override
String get settings_cyr2latProfileAdd => 'Cyr2Latプロファイルの追加';
@override
String get settings_cyr2latProfileName => 'プロファイル名';
@override
String get settings_cyr2latProfileNameEmpty => 'プロファイル名は空にできません';
@override
String get settings_cyr2latProfileAdded => 'プロファイルが正常に追加されました';
@override
String get settings_cyr2latProfileUpdated => 'プロファイルの更新に成功しました';
@override
String get settings_cyr2latProfileEdit => 'Cyr2Latプロファイルを編集';
@override
String get settings_cyr2latProfileDelete => 'Cyr2Latプロファイルを削除';
@override
String get settings_cyr2latProfileDeleted => 'プロファイルの削除に成功しました';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'プロファイル \"$name\" を削除してもよろしいですか?';
}
@override
String get channels_publicChannelAdded => 'パブリックチャンネルが追加されました';
+52
View File
@@ -1117,11 +1117,63 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get channels_smazCompression => 'SMAZ 압축';
@override
String get channels_cyr2latCompression => 'Cyr2Lat 압축';
@override
String get channels_cyr2latCompressionDscr => '보낼 때 일부 키릴 문자를 라틴 문자로 바꿉니다.';
@override
String get channels_cyr2latSettingsHeading => 'Cyr2Lat 설정';
@override
String get channels_cyr2latSettingsSubheading => '변환 목록';
@override
String get channels_cyr2latSettingsDscr => '문자 변환 JSON 구성 편집';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON 변환 맵';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return '잘못된 JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return '채널 \"$name\"이 업데이트되었습니다.';
}
@override
String get settings_cyr2latProfileAdd => 'Cyr2Lat 프로필 추가';
@override
String get settings_cyr2latProfileName => '프로필 이름';
@override
String get settings_cyr2latProfileNameEmpty => '프로필 이름은 비워둘 수 없습니다';
@override
String get settings_cyr2latProfileAdded => '프로필이 성공적으로 추가되었습니다';
@override
String get settings_cyr2latProfileUpdated => '프로필이 성공적으로 업데이트되었습니다';
@override
String get settings_cyr2latProfileEdit => 'Cyr2Lat 프로필 편집';
@override
String get settings_cyr2latProfileDelete => 'Cyr2Lat 프로필 삭제';
@override
String get settings_cyr2latProfileDeleted => '프로필이 성공적으로 삭제되었습니다';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return '\"$name\" 프로필을 삭제하시겠습니까?';
}
@override
String get channels_publicChannelAdded => '공개 채널 추가';
+55
View File
@@ -1165,11 +1165,66 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get channels_smazCompression => 'SMAZ compressie';
@override
String get channels_cyr2latCompression => 'Cyr2Lat compressie';
@override
String get channels_cyr2latCompressionDscr =>
'Vervangt sommige Cyrillische tekens door Latijnse tekens bij het verzenden.';
@override
String get channels_cyr2latSettingsHeading => 'Instellingen Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Lijst met vervangingen';
@override
String get channels_cyr2latSettingsDscr =>
'Bewerk de JSON-configuratie voor tekenvervanging';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON-vervangingskaart';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'Onjuiste JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Kanaal \"$name\" is bijgewerkt';
}
@override
String get settings_cyr2latProfileAdd => 'Cyr2Lat-profiel toevoegen';
@override
String get settings_cyr2latProfileName => 'Profielnaam';
@override
String get settings_cyr2latProfileNameEmpty =>
'Profielnaam mag niet leeg zijn';
@override
String get settings_cyr2latProfileAdded => 'Profiel succesvol toegevoegd';
@override
String get settings_cyr2latProfileUpdated => 'Profiel succesvol bijgewerkt';
@override
String get settings_cyr2latProfileEdit => 'Cyr2Lat-profiel bewerken';
@override
String get settings_cyr2latProfileDelete => 'Cyr2Lat-profiel verwijderen';
@override
String get settings_cyr2latProfileDeleted => 'Profiel succesvol verwijderd';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Weet u zeker dat u het profiel \"$name\" wilt verwijderen?';
}
@override
String get channels_publicChannelAdded => 'Open kanaal toegevoegd';
+57
View File
@@ -1185,11 +1185,68 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get channels_smazCompression => 'Kompresja SMAZ';
@override
String get channels_cyr2latCompression => 'Kompresja Cyr2Lat';
@override
String get channels_cyr2latCompressionDscr =>
'Zastępuje niektóre znaki cyrylicy alfabetem łacińskim podczas wysyłania.';
@override
String get channels_cyr2latSettingsHeading => 'Ustawienia Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Lista zamian';
@override
String get channels_cyr2latSettingsDscr =>
'Edytuj konfigurację JSON zamiany znaków';
@override
String get channels_cyr2latSettingsDialogHint => 'Mapa zamian JSON';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'Nieprawidłowy JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Kanał \"$name\" został zaktualizowany';
}
@override
String get settings_cyr2latProfileAdd => 'Dodaj profil Cyr2Lat';
@override
String get settings_cyr2latProfileName => 'Nazwa profilu';
@override
String get settings_cyr2latProfileNameEmpty =>
'Nazwa profilu nie może być pusta';
@override
String get settings_cyr2latProfileAdded => 'Profil dodano pomyślnie';
@override
String get settings_cyr2latProfileUpdated =>
'Profil został pomyślnie zaktualizowany';
@override
String get settings_cyr2latProfileEdit => 'Edytuj profil Cyr2Lat';
@override
String get settings_cyr2latProfileDelete => 'Usuń profil Cyr2Lat';
@override
String get settings_cyr2latProfileDeleted =>
'Profil został pomyślnie usunięty';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Czy na pewno chcesz usunąć profil \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'Kanał publiczny dodany';
+55
View File
@@ -1176,11 +1176,66 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get channels_smazCompression => 'Compressão SMAZ';
@override
String get channels_cyr2latCompression => 'Compressão Cyr2Lat';
@override
String get channels_cyr2latCompressionDscr =>
'Substitui alguns caracteres cirílicos por caracteres latinos ao enviar.';
@override
String get channels_cyr2latSettingsHeading => 'Configuração do Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Lista de substituições';
@override
String get channels_cyr2latSettingsDscr =>
'Editar a configuração JSON de substituição de caracteres';
@override
String get channels_cyr2latSettingsDialogHint => 'Mapa de substituições JSON';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'JSON incorreto: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Canal \"$name\" atualizado';
}
@override
String get settings_cyr2latProfileAdd => 'Adicionar perfil Cyr2Lat';
@override
String get settings_cyr2latProfileName => 'Nome do perfil';
@override
String get settings_cyr2latProfileNameEmpty =>
'O nome do perfil não pode estar vazio';
@override
String get settings_cyr2latProfileAdded => 'Perfil adicionado com sucesso';
@override
String get settings_cyr2latProfileUpdated => 'Perfil atualizado com sucesso';
@override
String get settings_cyr2latProfileEdit => 'Editar perfil Cyr2Lat';
@override
String get settings_cyr2latProfileDelete => 'Eliminar perfil Cyr2Lat';
@override
String get settings_cyr2latProfileDeleted => 'Perfil eliminado com sucesso';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Tem a certeza de que deseja eliminar o perfil \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'Canal público adicionado';
+57 -2
View File
@@ -1176,11 +1176,66 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get channels_smazCompression => 'Сжатие SMAZ';
@override
String get channels_cyr2latCompression => 'Сжатие Cyr2Lat';
@override
String get channels_cyr2latCompressionDscr =>
'Заменяет некоторые кириллические символы на латиницу при отправке.';
@override
String get channels_cyr2latSettingsHeading => 'Настройка Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Список замен';
@override
String get channels_cyr2latSettingsDscr =>
'Редактировать JSON-конфигурацию замены символов';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON-карта замен';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'Некорректный JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Канал \"$name\" обновлён';
}
@override
String get settings_cyr2latProfileAdd => 'Добавить профиль Cyr2Lat';
@override
String get settings_cyr2latProfileName => 'Название профиля';
@override
String get settings_cyr2latProfileNameEmpty =>
'Название профиля не может быть пустым';
@override
String get settings_cyr2latProfileAdded => 'Профиль добавлен';
@override
String get settings_cyr2latProfileUpdated => 'Профиль успешно обновлен';
@override
String get settings_cyr2latProfileEdit => 'Редактировать профиль Cyr2Lat';
@override
String get settings_cyr2latProfileDelete => 'Удалить профиль Cyr2Lat';
@override
String get settings_cyr2latProfileDeleted => 'Профиль успешно удален';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Вы действительно хотите удалить профиль \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'Публичный канал добавлен';
@@ -1568,10 +1623,10 @@ class AppLocalizationsRu extends AppLocalizations {
}
@override
String get chat_markAsUnread => 'Mark as Unread';
String get chat_markAsUnread => 'Пометить как непрочитанные';
@override
String get chat_newMessages => 'New messages';
String get chat_newMessages => 'Новые сообщения';
@override
String get chat_openLink => 'Открыть ссылку?';
+56
View File
@@ -1165,11 +1165,67 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get channels_smazCompression => 'Odstránenie kompresie SMAZ';
@override
String get channels_cyr2latCompression => 'Odstránenie kompresie Cyr2Lat';
@override
String get channels_cyr2latCompressionDscr =>
'Pri odosielaní nahradí niektoré znaky cyriliky latinskými znakmi.';
@override
String get channels_cyr2latSettingsHeading => 'Nastavenia Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Zoznam nahradení';
@override
String get channels_cyr2latSettingsDscr =>
'Upravte konfiguráciu JSON pre nahradenie znakov';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON mapa nahradení';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'Nesprávny JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Kanál \"$name\" bol aktualizovaný';
}
@override
String get settings_cyr2latProfileAdd => 'Pridať profil Cyr2Lat';
@override
String get settings_cyr2latProfileName => 'Názov profilu';
@override
String get settings_cyr2latProfileNameEmpty =>
'Názov profilu nesmie byť prázdny';
@override
String get settings_cyr2latProfileAdded => 'Profil bol úspešne pridaný';
@override
String get settings_cyr2latProfileUpdated =>
'Profil bol úspešne aktualizovaný';
@override
String get settings_cyr2latProfileEdit => 'Upraviť profil Cyr2Lat';
@override
String get settings_cyr2latProfileDelete => 'Odstrániť profil Cyr2Lat';
@override
String get settings_cyr2latProfileDeleted => 'Profil bol úspešne odstránený';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Naozaj chcete odstrániť profil \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'Veľký kanál pridaný';
+56
View File
@@ -1163,11 +1163,67 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get channels_smazCompression => 'Kompresija SMAZ';
@override
String get channels_cyr2latCompression => 'Kompresija Cyr2Lat';
@override
String get channels_cyr2latCompressionDscr =>
'Pri pošiljanju nekatere cirilice nadomesti z latiničnimi.';
@override
String get channels_cyr2latSettingsHeading => 'Nastavitve Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Seznam zamenjav';
@override
String get channels_cyr2latSettingsDscr =>
'Uredi JSON-konfiguracijo zamenjav znakov';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON-tabela zamenjav';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'Nepravilen JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Kanal $name je bil posodobljen';
}
@override
String get settings_cyr2latProfileAdd => 'Dodaj profil Cyr2Lat';
@override
String get settings_cyr2latProfileName => 'Ime profila';
@override
String get settings_cyr2latProfileNameEmpty =>
'Ime profila ne sme biti prazno';
@override
String get settings_cyr2latProfileAdded => 'Profil je bil uspešno dodan';
@override
String get settings_cyr2latProfileUpdated =>
'Profil je bil uspešno posodobljen';
@override
String get settings_cyr2latProfileEdit => 'Uredi profil Cyr2Lat';
@override
String get settings_cyr2latProfileDelete => 'Izbriši profil Cyr2Lat';
@override
String get settings_cyr2latProfileDeleted => 'Profil je bil uspešno izbrisan';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Ali res želite izbrisati profil \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'javna skupnost dodana';
+55
View File
@@ -1156,11 +1156,66 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get channels_smazCompression => 'SMAZ-komprimering';
@override
String get channels_cyr2latCompression => 'Cyr2Lat-komprimering';
@override
String get channels_cyr2latCompressionDscr =>
'Ersätter vissa kyrilliska tecken med latinska tecken när du skickar.';
@override
String get channels_cyr2latSettingsHeading => 'Inställningar för Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Ersättningslista';
@override
String get channels_cyr2latSettingsDscr =>
'Redigera JSON-konfigurationen för teckenersättning';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON-ersättningskarta';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'Felaktig JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Kanalen \"$name\" har uppdaterats';
}
@override
String get settings_cyr2latProfileAdd => 'Lägg till Cyr2Lat-profil';
@override
String get settings_cyr2latProfileName => 'Profilnamn';
@override
String get settings_cyr2latProfileNameEmpty =>
'Profilnamnet får inte vara tomt';
@override
String get settings_cyr2latProfileAdded => 'Profilen har lagts till';
@override
String get settings_cyr2latProfileUpdated => 'Profilen har uppdaterats';
@override
String get settings_cyr2latProfileEdit => 'Redigera Cyr2Lat-profil';
@override
String get settings_cyr2latProfileDelete => 'Ta bort Cyr2Lat-profil';
@override
String get settings_cyr2latProfileDeleted => 'Profilen har tagits bort';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Är du säker på att du vill ta bort profilen \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'Allmänt kanal tillagd';
+55
View File
@@ -1171,11 +1171,66 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get channels_smazCompression => 'Стиснення SMAZ';
@override
String get channels_cyr2latCompression => 'Стиснення Cyr2Lat';
@override
String get channels_cyr2latCompressionDscr =>
'Замінює деякі кириличні символи на латиницю при відправці.';
@override
String get channels_cyr2latSettingsHeading => 'Налаштування Cyr2Lat';
@override
String get channels_cyr2latSettingsSubheading => 'Список замін';
@override
String get channels_cyr2latSettingsDscr =>
'Редагувати JSON-конфігурацію заміни символів';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON-карта замін';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'Некоректний JSON: $error';
}
@override
String channels_channelUpdated(String name) {
return 'Канал «$name» оновлено';
}
@override
String get settings_cyr2latProfileAdd => 'Додати профіль Cyr2Lat';
@override
String get settings_cyr2latProfileName => 'Назва профілю';
@override
String get settings_cyr2latProfileNameEmpty =>
'Назва профілю не може бути порожньою';
@override
String get settings_cyr2latProfileAdded => 'Профіль успішно додано';
@override
String get settings_cyr2latProfileUpdated => 'Профіль успішно оновлено';
@override
String get settings_cyr2latProfileEdit => 'Редагувати профіль Cyr2Lat';
@override
String get settings_cyr2latProfileDelete => 'Видалити профіль Cyr2Lat';
@override
String get settings_cyr2latProfileDeleted => 'Профіль успішно видалено';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return 'Ви впевнені, що хочете видалити профіль \"$name\"?';
}
@override
String get channels_publicChannelAdded => 'Публічний канал додано';
+52
View File
@@ -1104,11 +1104,63 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get channels_smazCompression => 'SMAZ 压缩';
@override
String get channels_cyr2latCompression => 'Cyr2Lat 压缩';
@override
String get channels_cyr2latCompressionDscr => '发送时将一些西里尔字符替换为拉丁字符。';
@override
String get channels_cyr2latSettingsHeading => 'Cyr2Lat 設定';
@override
String get channels_cyr2latSettingsSubheading => '替換清單';
@override
String get channels_cyr2latSettingsDscr => '編輯 JSON 字元替換設定檔';
@override
String get channels_cyr2latSettingsDialogHint => 'JSON 替換映射表';
@override
String channels_cyr2latSettingsDialogWrongJSON(Object error) {
return 'JSON 格式錯誤:$error';
}
@override
String channels_channelUpdated(String name) {
return '频道 \"$name\" 已更新';
}
@override
String get settings_cyr2latProfileAdd => '新增 Cyr2Lat 設定檔';
@override
String get settings_cyr2latProfileName => '設定檔名稱';
@override
String get settings_cyr2latProfileNameEmpty => '設定檔名稱不能為空';
@override
String get settings_cyr2latProfileAdded => '設定檔已成功新增';
@override
String get settings_cyr2latProfileUpdated => '設定檔已成功更新';
@override
String get settings_cyr2latProfileEdit => '編輯 Cyr2Lat 設定檔';
@override
String get settings_cyr2latProfileDelete => '刪除 Cyr2Lat 設定檔';
@override
String get settings_cyr2latProfileDeleted => '設定檔已成功刪除';
@override
String settings_cyr2latProfileDeleteDscr(String name) {
return '您確定要刪除設定檔 \"$name\" 嗎?';
}
@override
String get channels_publicChannelAdded => '已添加公共频道';
+16
View File
@@ -387,6 +387,22 @@
}
},
"channels_smazCompression": "SMAZ compressie",
"channels_cyr2latCompression": "Cyr2Lat compressie",
"channels_cyr2latCompressionDscr": "Vervangt sommige Cyrillische tekens door Latijnse tekens bij het verzenden.",
"channels_cyr2latSettingsHeading": "Instellingen Cyr2Lat",
"channels_cyr2latSettingsSubheading": "Lijst met vervangingen",
"channels_cyr2latSettingsDscr": "Bewerk de JSON-configuratie voor tekenvervanging",
"channels_cyr2latSettingsDialogHint": "JSON-vervangingskaart",
"channels_cyr2latSettingsDialogWrongJSON": "Onjuiste JSON: {error}",
"settings_cyr2latProfileAdd": "Cyr2Lat-profiel toevoegen",
"settings_cyr2latProfileName": "Profielnaam",
"settings_cyr2latProfileNameEmpty": "Profielnaam mag niet leeg zijn",
"settings_cyr2latProfileAdded": "Profiel succesvol toegevoegd",
"settings_cyr2latProfileUpdated": "Profiel succesvol bijgewerkt",
"settings_cyr2latProfileEdit": "Cyr2Lat-profiel bewerken",
"settings_cyr2latProfileDelete": "Cyr2Lat-profiel verwijderen",
"settings_cyr2latProfileDeleted": "Profiel succesvol verwijderd",
"settings_cyr2latProfileDeleteDscr": "Weet u zeker dat u het profiel \"{name}\" wilt verwijderen?",
"channels_channelUpdated": "Kanaal \"{name}\" is bijgewerkt",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -397,6 +397,22 @@
}
},
"channels_smazCompression": "Kompresja SMAZ",
"channels_cyr2latCompression": "Kompresja Cyr2Lat",
"channels_cyr2latCompressionDscr": "Zastępuje niektóre znaki cyrylicy alfabetem łacińskim podczas wysyłania.",
"channels_cyr2latSettingsHeading": "Ustawienia Cyr2Lat",
"channels_cyr2latSettingsSubheading": "Lista zamian",
"channels_cyr2latSettingsDscr": "Edytuj konfigurację JSON zamiany znaków",
"channels_cyr2latSettingsDialogHint": "Mapa zamian JSON",
"channels_cyr2latSettingsDialogWrongJSON": "Nieprawidłowy JSON: {error}",
"settings_cyr2latProfileAdd": "Dodaj profil Cyr2Lat",
"settings_cyr2latProfileName": "Nazwa profilu",
"settings_cyr2latProfileNameEmpty": "Nazwa profilu nie może być pusta",
"settings_cyr2latProfileAdded": "Profil dodano pomyślnie",
"settings_cyr2latProfileUpdated": "Profil został pomyślnie zaktualizowany",
"settings_cyr2latProfileEdit": "Edytuj profil Cyr2Lat",
"settings_cyr2latProfileDelete": "Usuń profil Cyr2Lat",
"settings_cyr2latProfileDeleted": "Profil został pomyślnie usunięty",
"settings_cyr2latProfileDeleteDscr": "Czy na pewno chcesz usunąć profil \"{name}\"?",
"channels_channelUpdated": "Kanał \"{name}\" został zaktualizowany",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -387,6 +387,22 @@
}
},
"channels_smazCompression": "Compressão SMAZ",
"channels_cyr2latCompression": "Compressão Cyr2Lat",
"channels_cyr2latCompressionDscr": "Substitui alguns caracteres cirílicos por caracteres latinos ao enviar.",
"channels_cyr2latSettingsHeading": "Configuração do Cyr2Lat",
"channels_cyr2latSettingsSubheading": "Lista de substituições",
"channels_cyr2latSettingsDscr": "Editar a configuração JSON de substituição de caracteres",
"channels_cyr2latSettingsDialogHint": "Mapa de substituições JSON",
"channels_cyr2latSettingsDialogWrongJSON": "JSON incorreto: {error}",
"settings_cyr2latProfileAdd": "Adicionar perfil Cyr2Lat",
"settings_cyr2latProfileName": "Nome do perfil",
"settings_cyr2latProfileNameEmpty": "O nome do perfil não pode estar vazio",
"settings_cyr2latProfileAdded": "Perfil adicionado com sucesso",
"settings_cyr2latProfileUpdated": "Perfil atualizado com sucesso",
"settings_cyr2latProfileEdit": "Editar perfil Cyr2Lat",
"settings_cyr2latProfileDelete": "Eliminar perfil Cyr2Lat",
"settings_cyr2latProfileDeleted": "Perfil eliminado com sucesso",
"settings_cyr2latProfileDeleteDscr": "Tem a certeza de que deseja eliminar o perfil \"{name}\"?",
"channels_channelUpdated": "Canal \"{name}\" atualizado",
"@channels_channelUpdated": {
"placeholders": {
+18
View File
@@ -251,6 +251,22 @@
"channels_channelAdded": "Канал \"{name}\" добавлен",
"channels_editChannelTitle": "Изменить канал {index}",
"channels_smazCompression": "Сжатие SMAZ",
"channels_cyr2latCompression": "Сжатие Cyr2Lat",
"channels_cyr2latCompressionDscr": "Заменяет некоторые кириллические символы на латиницу при отправке.",
"channels_cyr2latSettingsHeading": "Настройка Cyr2Lat",
"channels_cyr2latSettingsSubheading": "Список замен",
"channels_cyr2latSettingsDscr": "Редактировать JSON-конфигурацию замены символов",
"channels_cyr2latSettingsDialogHint": "JSON-карта замен",
"channels_cyr2latSettingsDialogWrongJSON": "Некорректный JSON: {error}",
"settings_cyr2latProfileAdd": "Добавить профиль Cyr2Lat",
"settings_cyr2latProfileName": "Название профиля",
"settings_cyr2latProfileNameEmpty": "Название профиля не может быть пустым",
"settings_cyr2latProfileAdded": "Профиль добавлен",
"settings_cyr2latProfileUpdated": "Профиль успешно обновлен",
"settings_cyr2latProfileEdit": "Редактировать профиль Cyr2Lat",
"settings_cyr2latProfileDelete": "Удалить профиль Cyr2Lat",
"settings_cyr2latProfileDeleted": "Профиль успешно удален",
"settings_cyr2latProfileDeleteDscr": "Вы действительно хотите удалить профиль \"{name}\"?",
"channels_channelUpdated": "Канал \"{name}\" обновлён",
"channels_publicChannelAdded": "Публичный канал добавлен",
"channels_sortBy": "Сортировка",
@@ -357,6 +373,8 @@
"chat_direct": "Прямой",
"chat_poiShared": "Точка интереса отправлена",
"chat_unread": "Непрочитанных: {count}",
"chat_markAsUnread": "Пометить как непрочитанные",
"chat_newMessages": "Новые сообщения",
"map_title": "Карта нод",
"map_noNodesWithLocation": "Нет нод с данными о местоположении",
"map_nodesNeedGps": "Ноды должны передавать свои GPS-координаты, чтобы отображаться на карте",
+16
View File
@@ -387,6 +387,22 @@
}
},
"channels_smazCompression": "Odstránenie kompresie SMAZ",
"channels_cyr2latCompression": "Odstránenie kompresie Cyr2Lat",
"channels_cyr2latCompressionDscr": "Pri odosielaní nahradí niektoré znaky cyriliky latinskými znakmi.",
"channels_cyr2latSettingsHeading": "Nastavenia Cyr2Lat",
"channels_cyr2latSettingsSubheading": "Zoznam nahradení",
"channels_cyr2latSettingsDscr": "Upravte konfiguráciu JSON pre nahradenie znakov",
"channels_cyr2latSettingsDialogHint": "JSON mapa nahradení",
"channels_cyr2latSettingsDialogWrongJSON": "Nesprávny JSON: {error}",
"settings_cyr2latProfileAdd": "Pridať profil Cyr2Lat",
"settings_cyr2latProfileName": "Názov profilu",
"settings_cyr2latProfileNameEmpty": "Názov profilu nesmie byť prázdny",
"settings_cyr2latProfileAdded": "Profil bol úspešne pridaný",
"settings_cyr2latProfileUpdated": "Profil bol úspešne aktualizovaný",
"settings_cyr2latProfileEdit": "Upraviť profil Cyr2Lat",
"settings_cyr2latProfileDelete": "Odstrániť profil Cyr2Lat",
"settings_cyr2latProfileDeleted": "Profil bol úspešne odstránený",
"settings_cyr2latProfileDeleteDscr": "Naozaj chcete odstrániť profil \"{name}\"?",
"channels_channelUpdated": "Kanál \"{name}\" bol aktualizovaný",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -387,6 +387,22 @@
}
},
"channels_smazCompression": "Kompresija SMAZ",
"channels_cyr2latCompression": "Kompresija Cyr2Lat",
"channels_cyr2latCompressionDscr": "Pri pošiljanju nekatere cirilice nadomesti z latiničnimi.",
"channels_cyr2latSettingsHeading": "Nastavitve Cyr2Lat",
"channels_cyr2latSettingsSubheading": "Seznam zamenjav",
"channels_cyr2latSettingsDscr": "Uredi JSON-konfiguracijo zamenjav znakov",
"channels_cyr2latSettingsDialogHint": "JSON-tabela zamenjav",
"channels_cyr2latSettingsDialogWrongJSON": "Nepravilen JSON: {error}",
"settings_cyr2latProfileAdd": "Dodaj profil Cyr2Lat",
"settings_cyr2latProfileName": "Ime profila",
"settings_cyr2latProfileNameEmpty": "Ime profila ne sme biti prazno",
"settings_cyr2latProfileAdded": "Profil je bil uspešno dodan",
"settings_cyr2latProfileUpdated": "Profil je bil uspešno posodobljen",
"settings_cyr2latProfileEdit": "Uredi profil Cyr2Lat",
"settings_cyr2latProfileDelete": "Izbriši profil Cyr2Lat",
"settings_cyr2latProfileDeleted": "Profil je bil uspešno izbrisan",
"settings_cyr2latProfileDeleteDscr": "Ali res želite izbrisati profil \"{name}\"?",
"channels_channelUpdated": "Kanal {name} je bil posodobljen",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -387,6 +387,22 @@
}
},
"channels_smazCompression": "SMAZ-komprimering",
"channels_cyr2latCompression": "Cyr2Lat-komprimering",
"channels_cyr2latCompressionDscr": "Ersätter vissa kyrilliska tecken med latinska tecken när du skickar.",
"channels_cyr2latSettingsHeading": "Inställningar för Cyr2Lat",
"channels_cyr2latSettingsSubheading": "Ersättningslista",
"channels_cyr2latSettingsDscr": "Redigera JSON-konfigurationen för teckenersättning",
"channels_cyr2latSettingsDialogHint": "JSON-ersättningskarta",
"channels_cyr2latSettingsDialogWrongJSON": "Felaktig JSON: {error}",
"settings_cyr2latProfileAdd": "Lägg till Cyr2Lat-profil",
"settings_cyr2latProfileName": "Profilnamn",
"settings_cyr2latProfileNameEmpty": "Profilnamnet får inte vara tomt",
"settings_cyr2latProfileAdded": "Profilen har lagts till",
"settings_cyr2latProfileUpdated": "Profilen har uppdaterats",
"settings_cyr2latProfileEdit": "Redigera Cyr2Lat-profil",
"settings_cyr2latProfileDelete": "Ta bort Cyr2Lat-profil",
"settings_cyr2latProfileDeleted": "Profilen har tagits bort",
"settings_cyr2latProfileDeleteDscr": "Är du säker på att du vill ta bort profilen \"{name}\"?",
"channels_channelUpdated": "Kanalen \"{name}\" har uppdaterats",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -390,6 +390,22 @@
}
},
"channels_smazCompression": "Стиснення SMAZ",
"channels_cyr2latCompression": "Стиснення Cyr2Lat",
"channels_cyr2latCompressionDscr": "Замінює деякі кириличні символи на латиницю при відправці.",
"channels_cyr2latSettingsHeading": "Налаштування Cyr2Lat",
"channels_cyr2latSettingsSubheading": "Список замін",
"channels_cyr2latSettingsDscr": "Редагувати JSON-конфігурацію заміни символів",
"channels_cyr2latSettingsDialogHint": "JSON-карта замін",
"channels_cyr2latSettingsDialogWrongJSON": "Некоректний JSON: {error}",
"settings_cyr2latProfileAdd": "Додати профіль Cyr2Lat",
"settings_cyr2latProfileName": "Назва профілю",
"settings_cyr2latProfileNameEmpty": "Назва профілю не може бути порожньою",
"settings_cyr2latProfileAdded": "Профіль успішно додано",
"settings_cyr2latProfileUpdated": "Профіль успішно оновлено",
"settings_cyr2latProfileEdit": "Редагувати профіль Cyr2Lat",
"settings_cyr2latProfileDelete": "Видалити профіль Cyr2Lat",
"settings_cyr2latProfileDeleted": "Профіль успішно видалено",
"settings_cyr2latProfileDeleteDscr": "Ви впевнені, що хочете видалити профіль \"{name}\"?",
"channels_channelUpdated": "Канал «{name}» оновлено",
"@channels_channelUpdated": {
"placeholders": {
+16
View File
@@ -402,6 +402,22 @@
}
},
"channels_smazCompression": "SMAZ 压缩",
"channels_cyr2latCompression": "Cyr2Lat 压缩",
"channels_cyr2latCompressionDscr": "发送时将一些西里尔字符替换为拉丁字符。",
"channels_cyr2latSettingsHeading": "Cyr2Lat 設定",
"channels_cyr2latSettingsSubheading": "替換清單",
"channels_cyr2latSettingsDscr": "編輯 JSON 字元替換設定檔",
"channels_cyr2latSettingsDialogHint": "JSON 替換映射表",
"channels_cyr2latSettingsDialogWrongJSON": "JSON 格式錯誤:{error}",
"settings_cyr2latProfileAdd": "新增 Cyr2Lat 設定檔",
"settings_cyr2latProfileName": "設定檔名稱",
"settings_cyr2latProfileNameEmpty": "設定檔名稱不能為空",
"settings_cyr2latProfileAdded": "設定檔已成功新增",
"settings_cyr2latProfileUpdated": "設定檔已成功更新",
"settings_cyr2latProfileEdit": "編輯 Cyr2Lat 設定檔",
"settings_cyr2latProfileDelete": "刪除 Cyr2Lat 設定檔",
"settings_cyr2latProfileDeleted": "設定檔已成功刪除",
"settings_cyr2latProfileDeleteDscr": "您確定要刪除設定檔 \"{name}\" 嗎?",
"channels_channelUpdated": "频道 \"{name}\" 已更新",
"@channels_channelUpdated": {
"placeholders": {
+125 -1
View File
@@ -13,6 +13,67 @@ extension UnitSystemValue on UnitSystem {
}
}
const Map<String, String> defaultCyr2LatCharMap = {
'А': 'A',
'В': 'B',
'Е': 'E',
'Ё': 'E',
'З': '3',
'К': 'K',
'М': 'M',
'Н': 'H',
'О': 'O',
'Р': 'P',
'С': 'C',
'Т': 'T',
'Х': 'X',
'Ь': 'b',
'а': 'a',
'е': 'e',
'ё': 'e',
'о': 'o',
'р': 'p',
'с': 'c',
'у': 'y',
'х': 'x',
};
class Cyr2LatProfile {
final String id;
final String name;
final Map<String, String> charMap;
Cyr2LatProfile({required this.id, required this.name, required this.charMap});
Map<String, dynamic> toJson() {
return {'id': id, 'name': name, 'char_map': charMap};
}
factory Cyr2LatProfile.fromJson(Map<String, dynamic> json) {
return Cyr2LatProfile(
id: json['id'] as String,
name: json['name'] as String,
charMap:
(json['char_map'] as Map?)?.map(
(key, value) => MapEntry(key.toString(), value.toString()),
) ??
{},
);
}
Cyr2LatProfile copyWith({
String? id,
String? name,
Map<String, String>? charMap,
}) {
return Cyr2LatProfile(
id: id ?? this.id,
name: name ?? this.name,
charMap: charMap ?? this.charMap,
);
}
}
class AppSettings {
static const Object _unset = Object();
@@ -57,6 +118,16 @@ class AppSettings {
final String? translationModelSourceUrl;
final String? translationSelectedModelId;
final List<TranslationModelRecord> translationDownloadedModels;
final List<Cyr2LatProfile> cyr2latProfiles;
final String selectedCyr2latProfileId;
Map<String, String> get cyr2latCharMap {
final profile = cyr2latProfiles.firstWhere(
(p) => p.id == selectedCyr2latProfileId,
orElse: () => cyr2latProfiles.first,
);
return profile.charMap;
}
AppSettings({
this.clearPathOnMaxRetry = false,
@@ -100,10 +171,22 @@ class AppSettings {
this.translationModelSourceUrl,
this.translationSelectedModelId,
List<TranslationModelRecord>? translationDownloadedModels,
List<Cyr2LatProfile>? cyr2latProfiles,
String? selectedCyr2latProfileId,
}) : batteryChemistryByDeviceId = batteryChemistryByDeviceId ?? {},
batteryChemistryByRepeaterId = batteryChemistryByRepeaterId ?? {},
mutedChannels = mutedChannels ?? {},
translationDownloadedModels = translationDownloadedModels ?? const [];
translationDownloadedModels = translationDownloadedModels ?? const [],
cyr2latProfiles =
cyr2latProfiles ??
[
Cyr2LatProfile(
id: 'default',
name: 'Default',
charMap: defaultCyr2LatCharMap,
),
],
selectedCyr2latProfileId = selectedCyr2latProfileId ?? 'default';
Map<String, dynamic> toJson() {
return {
@@ -150,6 +233,10 @@ class AppSettings {
'translation_downloaded_models': translationDownloadedModels
.map((model) => model.toJson())
.toList(),
'cyr2lat_profiles': cyr2latProfiles
.map((profile) => profile.toJson())
.toList(),
'selected_cyr2lat_profile_id': selectedCyr2latProfileId,
};
}
@@ -237,6 +324,38 @@ class AppSettings {
)
.toList() ??
const [],
cyr2latProfiles:
(json['cyr2lat_profiles'] as List<dynamic>?)
?.map(
(entry) => Cyr2LatProfile.fromJson(
Map<String, dynamic>.from(entry as Map),
),
)
.toList() ??
// Backward compatibility: if old cyr2lat_char_map exists, create a profile from it
(json['cyr2lat_char_map'] != null
? [
Cyr2LatProfile(
id: 'migrated',
name: 'Migrated Profile',
charMap:
(json['cyr2lat_char_map'] as Map?)?.map(
(key, value) =>
MapEntry(key.toString(), value.toString()),
) ??
defaultCyr2LatCharMap,
),
]
: [
Cyr2LatProfile(
id: 'default',
name: 'Default',
charMap: defaultCyr2LatCharMap,
),
]),
selectedCyr2latProfileId:
json['selected_cyr2lat_profile_id'] as String? ??
(json['cyr2lat_char_map'] != null ? 'migrated' : 'default'),
);
}
@@ -282,6 +401,8 @@ class AppSettings {
Object? translationModelSourceUrl = _unset,
Object? translationSelectedModelId = _unset,
List<TranslationModelRecord>? translationDownloadedModels,
List<Cyr2LatProfile>? cyr2latProfiles,
String? selectedCyr2latProfileId,
}) {
return AppSettings(
clearPathOnMaxRetry: clearPathOnMaxRetry ?? this.clearPathOnMaxRetry,
@@ -345,6 +466,9 @@ class AppSettings {
: translationSelectedModelId as String?,
translationDownloadedModels:
translationDownloadedModels ?? this.translationDownloadedModels,
cyr2latProfiles: cyr2latProfiles ?? this.cyr2latProfiles,
selectedCyr2latProfileId:
selectedCyr2latProfileId ?? this.selectedCyr2latProfileId,
);
}
}
+295
View File
@@ -1,3 +1,4 @@
import 'dart:convert';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@@ -60,6 +61,8 @@ class AppSettingsScreen extends StatelessWidget {
const SizedBox(height: 16),
_buildMapSettingsCard(context, settingsService),
const SizedBox(height: 16),
_buildCyr2LatCard(context, settingsService),
const SizedBox(height: 16),
_buildDebugCard(context, settingsService),
],
);
@@ -1260,6 +1263,298 @@ class AppSettingsScreen extends StatelessWidget {
return '${sizeMb.toStringAsFixed(1)} MB • $source';
}
Widget _buildCyr2LatCard(
BuildContext context,
AppSettingsService settingsService,
) {
final selectedProfile = settingsService.getSelectedCyr2LatProfile();
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
context.l10n.channels_cyr2latSettingsHeading,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: DropdownButtonFormField<String>(
initialValue: settingsService.settings.selectedCyr2latProfileId,
decoration: InputDecoration(
labelText: context.l10n.channels_cyr2latSettingsSubheading,
border: const OutlineInputBorder(),
),
items: settingsService.settings.cyr2latProfiles.map((profile) {
return DropdownMenuItem(
value: profile.id,
child: Text(profile.name),
);
}).toList(),
onChanged: (value) {
if (value != null) {
settingsService.setSelectedCyr2LatProfile(value);
}
},
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: () =>
_showAddCyr2LatProfileDialog(context, settingsService),
icon: const Icon(Icons.add),
label: Text(context.l10n.common_add),
),
),
const SizedBox(width: 8),
Expanded(
child: OutlinedButton.icon(
onPressed: () => _showEditCyr2LatProfileDialog(
context,
settingsService,
selectedProfile,
),
icon: const Icon(Icons.edit),
label: Text(context.l10n.common_edit),
),
),
const SizedBox(width: 8),
Expanded(
child: OutlinedButton.icon(
onPressed:
settingsService.settings.cyr2latProfiles.length > 1
? () => _showDeleteCyr2LatProfileDialog(
context,
settingsService,
selectedProfile,
)
: null,
icon: const Icon(Icons.delete),
label: Text(context.l10n.common_delete),
),
),
],
),
),
],
),
);
}
void _showAddCyr2LatProfileDialog(
BuildContext context,
AppSettingsService settingsService,
) {
final nameController = TextEditingController();
final jsonController = TextEditingController(
text: const JsonEncoder.withIndent(' ').convert(defaultCyr2LatCharMap),
);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(context.l10n.settings_cyr2latProfileAdd),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: InputDecoration(
labelText: context.l10n.settings_cyr2latProfileName,
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: jsonController,
maxLines: 15,
decoration: InputDecoration(
labelText: context.l10n.channels_cyr2latSettingsDialogHint,
border: const OutlineInputBorder(),
hintText: context.l10n.channels_cyr2latSettingsDscr,
),
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(context.l10n.common_cancel),
),
TextButton(
onPressed: () async {
if (nameController.text.isEmpty) {
showDismissibleSnackBar(
context,
content: Text(context.l10n.settings_cyr2latProfileNameEmpty),
);
return;
}
try {
final json =
jsonDecode(jsonController.text) as Map<String, dynamic>;
final map = json.map(
(key, value) => MapEntry(key, value.toString()),
);
final profile = Cyr2LatProfile(
id: DateTime.now().millisecondsSinceEpoch.toString(),
name: nameController.text,
charMap: map,
);
await settingsService.addCyr2LatProfile(profile);
if (!context.mounted) return;
Navigator.pop(context);
showDismissibleSnackBar(
context,
content: Text(context.l10n.settings_cyr2latProfileAdded),
);
} catch (e) {
showDismissibleSnackBar(
context,
content: Text(
context.l10n.channels_cyr2latSettingsDialogWrongJSON(
e.toString(),
),
),
);
}
},
child: Text(context.l10n.common_save),
),
],
),
);
}
void _showEditCyr2LatProfileDialog(
BuildContext context,
AppSettingsService settingsService,
Cyr2LatProfile profile,
) {
final nameController = TextEditingController(text: profile.name);
final jsonController = TextEditingController(
text: const JsonEncoder.withIndent(' ').convert(profile.charMap),
);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(context.l10n.settings_cyr2latProfileEdit),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: InputDecoration(
labelText: context.l10n.settings_cyr2latProfileName,
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: jsonController,
maxLines: 15,
decoration: InputDecoration(
labelText: context.l10n.channels_cyr2latSettingsDialogHint,
border: const OutlineInputBorder(),
hintText: context.l10n.channels_cyr2latSettingsDscr,
),
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(context.l10n.common_cancel),
),
TextButton(
onPressed: () async {
if (nameController.text.isEmpty) {
showDismissibleSnackBar(
context,
content: Text(context.l10n.settings_cyr2latProfileNameEmpty),
);
return;
}
try {
final json =
jsonDecode(jsonController.text) as Map<String, dynamic>;
final map = json.map(
(key, value) => MapEntry(key, value.toString()),
);
final updatedProfile = profile.copyWith(
name: nameController.text,
charMap: map,
);
await settingsService.updateCyr2LatProfile(updatedProfile);
if (!context.mounted) return;
Navigator.pop(context);
showDismissibleSnackBar(
context,
content: Text(context.l10n.settings_cyr2latProfileUpdated),
);
} catch (e) {
showDismissibleSnackBar(
context,
content: Text(
context.l10n.channels_cyr2latSettingsDialogWrongJSON(
e.toString(),
),
),
);
}
},
child: Text(context.l10n.common_save),
),
],
),
);
}
void _showDeleteCyr2LatProfileDialog(
BuildContext context,
AppSettingsService settingsService,
Cyr2LatProfile profile,
) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(context.l10n.settings_cyr2latProfileDelete),
content: Text(
context.l10n.settings_cyr2latProfileDeleteDscr(profile.name),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(context.l10n.common_cancel),
),
TextButton(
onPressed: () async {
await settingsService.removeCyr2LatProfile(profile.id);
if (!context.mounted) return;
Navigator.pop(context);
showDismissibleSnackBar(
context,
content: Text(context.l10n.settings_cyr2latProfileDeleted),
);
},
child: Text(context.l10n.common_delete),
),
],
),
);
}
Widget _buildDebugCard(
BuildContext context,
AppSettingsService settingsService,
+16 -1
View File
@@ -11,6 +11,7 @@ import '../connector/meshcore_connector.dart';
import '../utils/platform_info.dart';
import '../helpers/chat_scroll_controller.dart';
import '../connector/meshcore_protocol.dart';
import '../helpers/cyr2lat.dart';
import '../helpers/gif_helper.dart';
import '../helpers/reaction_helper.dart';
import '../helpers/snack_bar_builder.dart';
@@ -1151,7 +1152,12 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
hintText: context.l10n.chat_typeMessage,
onSubmitted: (_) => _sendMessage(),
encoder:
connector.isChannelSmazEnabled(widget.channel.index)
(connector.isChannelSmazEnabled(
widget.channel.index,
) ||
connector.isChannelCyr2LatEnabled(
widget.channel.index,
))
? (text) => connector.prepareChannelOutboundText(
widget.channel.index,
text,
@@ -1264,6 +1270,15 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
return;
}
// When messageText is transformed with cyr2lat, it (generally) hasn't visual differences,
// but we getting messages doubles in chat screen (source text and transformed).
// To prevent, we'll perform transform of source before pass to main sender logic.
// We can pass whole text, senderName will be kept intact
if (connector.isChannelCyr2LatEnabled(widget.channel.index)) {
messageText = Cyr2Lat.encode(messageText);
}
// end transform
_textController.clear();
_cancelReply();
_textFieldFocusNode.requestFocus();
+61 -1
View File
@@ -1397,9 +1397,17 @@ class _ChannelsScreenState extends State<ChannelsScreen>
MeshCoreConnector connector,
Channel channel,
) {
final appSettingsService = Provider.of<AppSettingsService>(
context,
listen: false,
);
final nameController = TextEditingController(text: channel.name);
final pskController = TextEditingController(text: channel.pskHex);
bool smazEnabled = connector.isChannelSmazEnabled(channel.index);
bool cyr2latEnabled = connector.isChannelCyr2LatEnabled(channel.index);
String? selectedCyr2LatProfileId = connector.getChannelCyr2LatProfileId(
channel.index,
);
showDialog(
context: context,
@@ -1445,8 +1453,52 @@ class _ChannelsScreenState extends State<ChannelsScreen>
contentPadding: EdgeInsets.zero,
title: Text(dialogContext.l10n.channels_smazCompression),
value: smazEnabled,
onChanged: (value) => setState(() => smazEnabled = value),
onChanged: (value) => setState(() {
smazEnabled = value;
if (smazEnabled) {
cyr2latEnabled = false;
}
}),
),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: Text(dialogContext.l10n.channels_cyr2latCompression),
subtitle: Text(
dialogContext.l10n.channels_cyr2latCompressionDscr,
),
value: cyr2latEnabled,
onChanged: (value) => setState(() {
cyr2latEnabled = value;
if (cyr2latEnabled) {
smazEnabled = false;
}
}),
),
if (cyr2latEnabled) ...[
Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 8),
child: DropdownButtonFormField<String>(
initialValue: selectedCyr2LatProfileId,
decoration: InputDecoration(
labelText: dialogContext
.l10n
.channels_cyr2latSettingsSubheading,
border: const OutlineInputBorder(),
),
items: appSettingsService.settings.cyr2latProfiles.map((
profile,
) {
return DropdownMenuItem(
value: profile.id,
child: Text(profile.name),
);
}).toList(),
onChanged: (value) => setState(() {
selectedCyr2LatProfileId = value;
}),
),
),
],
],
),
),
@@ -1478,6 +1530,14 @@ class _ChannelsScreenState extends State<ChannelsScreen>
channel.index,
smazEnabled,
);
await connector.setChannelCyr2LatEnabled(
channel.index,
cyr2latEnabled,
);
await connector.setChannelCyr2LatProfileId(
channel.index,
selectedCyr2LatProfileId,
);
if (!context.mounted) return;
showDismissibleSnackBar(
context,
+93 -4
View File
@@ -12,6 +12,7 @@ import '../utils/platform_info.dart';
import '../connector/meshcore_connector.dart';
import '../connector/meshcore_protocol.dart';
import '../helpers/cyr2lat.dart';
import '../helpers/reaction_helper.dart';
import '../widgets/message_status_icon.dart';
import '../helpers/chat_scroll_controller.dart';
@@ -624,9 +625,12 @@ class _ChatScreenState extends State<ChatScreen> {
hintText: context.l10n.chat_typeMessage,
onSubmitted: (_) => _sendMessage(connector),
encoder:
connector.isContactSmazEnabled(
widget.contact.publicKeyHex,
)
(connector.isContactSmazEnabled(
widget.contact.publicKeyHex,
) ||
connector.isContactCyr2LatEnabled(
widget.contact.publicKeyHex,
))
? (text) => connector.prepareContactOutboundText(
widget.contact,
text,
@@ -745,6 +749,18 @@ class _ChatScreenState extends State<ChatScreen> {
return;
}
// This is only for cyr2lat compression - to see the message being sent in the same format as the other person will receive
try {
if (connector.isContactCyr2LatEnabled(
_resolveContact(connector).publicKeyHex,
)) {
outgoingText = Cyr2Lat.encode(outgoingText);
}
} catch (_) {
// TODO maybe log
}
// end transform
_textController.clear();
_textFieldFocusNode.requestFocus();
connector.sendMessage(
@@ -1251,9 +1267,20 @@ class _ChatScreenState extends State<ChatScreen> {
void _showContactSettings(BuildContext context) {
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
final appSettingsService = Provider.of<AppSettingsService>(
context,
listen: false,
);
connector.ensureContactSmazSettingLoaded(widget.contact.publicKeyHex);
connector.ensureContactCyr2LatSettingLoaded(widget.contact.publicKeyHex);
final contact = widget.contact;
bool smazEnabled = connector.isContactSmazEnabled(contact.publicKeyHex);
bool cyr2latEnabled = connector.isContactCyr2LatEnabled(
contact.publicKeyHex,
);
String? selectedCyr2LatProfileId = connector.getContactCyr2LatProfileId(
contact.publicKeyHex,
);
bool teleBaseEnabled = contact.teleBaseEnabled;
bool teleLocEnabled = contact.teleLocEnabled;
bool teleEnvEnabled = contact.teleEnvEnabled;
@@ -1284,10 +1311,72 @@ class _ChatScreenState extends State<ChatScreen> {
contact.publicKeyHex,
value,
);
setDialogState(() => smazEnabled = value);
connector.setContactCyr2LatEnabled(
contact.publicKeyHex,
false,
);
setDialogState(() {
smazEnabled = value;
if (smazEnabled) {
cyr2latEnabled = false;
}
});
},
),
const Divider(height: 8),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: Text(context.l10n.channels_cyr2latCompression),
subtitle: Text(context.l10n.channels_cyr2latCompressionDscr),
value: cyr2latEnabled,
onChanged: (value) {
connector.setContactCyr2LatEnabled(
contact.publicKeyHex,
value,
);
connector.setContactSmazEnabled(
contact.publicKeyHex,
false,
);
setDialogState(() {
cyr2latEnabled = value;
if (cyr2latEnabled) {
smazEnabled = false;
}
});
},
),
if (cyr2latEnabled) ...[
Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 8),
child: DropdownButtonFormField<String>(
initialValue: selectedCyr2LatProfileId,
decoration: InputDecoration(
labelText:
context.l10n.channels_cyr2latSettingsSubheading,
border: const OutlineInputBorder(),
),
items: appSettingsService.settings.cyr2latProfiles.map((
profile,
) {
return DropdownMenuItem(
value: profile.id,
child: Text(profile.name),
);
}).toList(),
onChanged: (value) {
connector.setContactCyr2LatProfileId(
contact.publicKeyHex,
value,
);
setDialogState(() {
selectedCyr2LatProfileId = value;
});
},
),
),
],
const Divider(height: 8),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: Text(context.l10n.contact_teleBase),
+59
View File
@@ -4,6 +4,7 @@ import '../models/app_settings.dart';
import '../models/translation_support.dart';
import '../storage/prefs_manager.dart';
import '../utils/app_logger.dart';
import '../helpers/cyr2lat.dart';
class AppSettingsService extends ChangeNotifier {
static const String _settingsKey = 'app_settings';
@@ -32,16 +33,22 @@ class AppSettingsService extends ChangeNotifier {
try {
final json = jsonDecode(jsonStr) as Map<String, dynamic>;
_settings = AppSettings.fromJson(json);
Cyr2Lat.setCharMap(_settings.cyr2latCharMap);
notifyListeners();
} catch (e) {
// If parsing fails, use defaults
_settings = AppSettings();
Cyr2Lat.setCharMap(_settings.cyr2latCharMap);
}
} else {
_settings = AppSettings();
Cyr2Lat.setCharMap(_settings.cyr2latCharMap);
}
}
Future<void> updateSettings(AppSettings newSettings) async {
_settings = newSettings;
Cyr2Lat.setCharMap(_settings.cyr2latCharMap);
notifyListeners();
final prefs = PrefsManager.instance;
@@ -253,4 +260,56 @@ class AppSettingsService extends ChangeNotifier {
_settings.copyWith(translationDownloadedModels: value),
);
}
Cyr2LatProfile getSelectedCyr2LatProfile() {
return _settings.cyr2latProfiles.firstWhere(
(p) => p.id == _settings.selectedCyr2latProfileId,
orElse: () => _settings.cyr2latProfiles.first,
);
}
Cyr2LatProfile? getCyr2LatProfileById(String profileId) {
return _settings.cyr2latProfiles.cast<Cyr2LatProfile?>().firstWhere(
(p) => p?.id == profileId,
orElse: () => null,
);
}
Future<void> setSelectedCyr2LatProfile(String profileId) async {
await updateSettings(
_settings.copyWith(selectedCyr2latProfileId: profileId),
);
}
Future<void> addCyr2LatProfile(Cyr2LatProfile profile) async {
final updated = List<Cyr2LatProfile>.from(_settings.cyr2latProfiles)
..add(profile);
await updateSettings(_settings.copyWith(cyr2latProfiles: updated));
}
Future<void> updateCyr2LatProfile(Cyr2LatProfile updatedProfile) async {
final updated = _settings.cyr2latProfiles
.map((p) => p.id == updatedProfile.id ? updatedProfile : p)
.toList();
await updateSettings(_settings.copyWith(cyr2latProfiles: updated));
}
Future<void> removeCyr2LatProfile(String profileId) async {
if (_settings.cyr2latProfiles.length <= 1) {
return; // Don't remove the last profile
}
final updated = _settings.cyr2latProfiles
.where((p) => p.id != profileId)
.toList();
var newSelectedId = _settings.selectedCyr2latProfileId;
if (newSelectedId == profileId) {
newSelectedId = updated.first.id;
}
await updateSettings(
_settings.copyWith(
cyr2latProfiles: updated,
selectedCyr2latProfileId: newSelectedId,
),
);
}
}
+55 -1
View File
@@ -3,12 +3,14 @@ import 'prefs_manager.dart';
class ChannelSettingsStore {
static const String _keyPrefix = 'channel_smaz_';
static const String _cyr2latKeyPrefix = 'channel_cyr2lat_';
String publicKeyHex = '';
set setPublicKeyHex(String value) =>
publicKeyHex = value.length > 10 ? value.substring(0, 10) : '';
String get keyFor => '$_keyPrefix$publicKeyHex';
String get keyForCyr2Lat => '$_cyr2latKeyPrefix$publicKeyHex';
Future<bool> loadSmazEnabled(int channelIndex) async {
if (publicKeyHex.isEmpty) {
@@ -20,7 +22,7 @@ class ChannelSettingsStore {
final prefs = PrefsManager.instance;
final key = '$keyFor$channelIndex';
final oldKey = '$_keyPrefix$channelIndex';
bool? enabled = prefs.getBool(oldKey);
bool? enabled = prefs.getBool(key);
if (enabled == null) {
// Attempt migration from legacy unscoped key on first load
enabled = prefs.getBool(oldKey);
@@ -46,4 +48,56 @@ class ChannelSettingsStore {
final key = '$keyFor$channelIndex';
await prefs.setBool(key, enabled);
}
Future<bool> loadCyr2LatEnabled(int channelIndex) async {
if (publicKeyHex.isEmpty) {
appLogger.warn(
'Public key hex is not set. Cannot load channel Cyr2Lat settings.',
);
return false;
}
final prefs = PrefsManager.instance;
final key = '$keyForCyr2Lat$channelIndex';
return prefs.getBool(key) ?? false;
}
Future<void> saveCyr2LatEnabled(int channelIndex, bool enabled) async {
if (publicKeyHex.isEmpty) {
appLogger.warn(
'Public key hex is not set. Cannot save channel Cyr2Lat settings.',
);
return;
}
final prefs = PrefsManager.instance;
final key = '$keyForCyr2Lat$channelIndex';
await prefs.setBool(key, enabled);
}
Future<String?> loadCyr2LatProfileId(int channelIndex) async {
if (publicKeyHex.isEmpty) {
appLogger.warn(
'Public key hex is not set. Cannot load channel settings.',
);
return null;
}
final prefs = PrefsManager.instance;
final key = '${keyForCyr2Lat}profile_$channelIndex';
return prefs.getString(key);
}
Future<void> saveCyr2LatProfileId(int channelIndex, String? profileId) async {
if (publicKeyHex.isEmpty) {
appLogger.warn(
'Public key hex is not set. Cannot save channel settings.',
);
return;
}
final prefs = PrefsManager.instance;
final key = '${keyForCyr2Lat}profile_$channelIndex';
if (profileId == null) {
await prefs.remove(key);
} else {
await prefs.setString(key, profileId);
}
}
}
+57
View File
@@ -3,12 +3,14 @@ import 'prefs_manager.dart';
class ContactSettingsStore {
static const String _keyPrefix = 'contact_smaz_';
static const String _cyr2latKeyPrefix = 'contact_cyr2lat_';
String publicKeyHex = '';
set setPublicKeyHex(String value) =>
publicKeyHex = value.length > 10 ? value.substring(0, 10) : '';
String get keyFor => '$_keyPrefix$publicKeyHex';
String get keyForCyr2Lat => '$_cyr2latKeyPrefix$publicKeyHex';
Future<bool> loadSmazEnabled(String contactKeyHex) async {
if (publicKeyHex.isEmpty) {
@@ -46,4 +48,59 @@ class ContactSettingsStore {
final key = '$keyFor$contactKeyHex';
await prefs.setBool(key, enabled);
}
Future<bool> loadCyr2LatEnabled(String contactKeyHex) async {
if (publicKeyHex.isEmpty) {
appLogger.warn(
'Public key hex is not set. Cannot load contact Cyr2Lat settings.',
);
return false;
}
final prefs = PrefsManager.instance;
final key = '$keyForCyr2Lat$contactKeyHex';
return prefs.getBool(key) ?? false;
}
Future<void> saveCyr2LatEnabled(String contactKeyHex, bool enabled) async {
if (publicKeyHex.isEmpty) {
appLogger.warn(
'Public key hex is not set. Cannot save contact Cyr2Lat settings.',
);
return;
}
final prefs = PrefsManager.instance;
final key = '$keyForCyr2Lat$contactKeyHex';
await prefs.setBool(key, enabled);
}
Future<String?> loadCyr2LatProfileId(String contactKeyHex) async {
if (publicKeyHex.isEmpty) {
appLogger.warn(
'Public key hex is not set. Cannot load contact settings.',
);
return null;
}
final prefs = PrefsManager.instance;
final key = '${keyForCyr2Lat}profile_$contactKeyHex';
return prefs.getString(key);
}
Future<void> saveCyr2LatProfileId(
String contactKeyHex,
String? profileId,
) async {
if (publicKeyHex.isEmpty) {
appLogger.warn(
'Public key hex is not set. Cannot save contact settings.',
);
return;
}
final prefs = PrefsManager.instance;
final key = '${keyForCyr2Lat}profile_$contactKeyHex';
if (profileId == null) {
await prefs.remove(key);
} else {
await prefs.setString(key, profileId);
}
}
}