diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index fceee150..6e3a1b9f 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -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'; @@ -283,9 +284,11 @@ class MeshCoreConnector extends ChangeNotifier { final UnreadStore _unreadStore = UnreadStore(); List _cachedChannels = []; final Map _channelSmazEnabled = {}; + final Map _channelCyr2LatEnabled = {}; bool _lastSentWasCliCommand = false; // Track if last sent message was a CLI command final Map _contactSmazEnabled = {}; + final Map _contactCyr2LatEnabled = {}; final Set _knownContactKeys = {}; final Map _contactUnreadCount = {}; final Map _repeaterBatterySnapshots = {}; @@ -607,6 +610,18 @@ class MeshCoreConnector extends ChangeNotifier { _ensureContactSmazSettingLoaded(contactKeyHex); } + bool isChannelCyr2LatEnabled(int channelIndex) { + return _channelCyr2LatEnabled[channelIndex] ?? false; + } + + bool isContactCyr2LatEnabled(String contactKeyHex) { + return _contactCyr2LatEnabled[contactKeyHex] ?? false; + } + + void ensureContactCyr2LatSettingLoaded(String contactKeyHex) { + _ensureContactCyr2LatSettingLoaded(contactKeyHex); + } + Future loadUnreadState() async { _contactUnreadCount ..clear() @@ -682,6 +697,10 @@ class MeshCoreConnector extends ChangeNotifier { Future 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(); } @@ -692,6 +711,25 @@ class MeshCoreConnector extends ChangeNotifier { notifyListeners(); } + Future 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 setContactCyr2LatEnabled( + String contactKeyHex, + bool enabled, + ) async { + _contactCyr2LatEnabled[contactKeyHex] = enabled; + await _contactSettingsStore.saveCyr2LatEnabled(contactKeyHex, enabled); + notifyListeners(); + } + Future _loadChannelOrder() async { _channelOrder = await _channelOrderStore.loadChannelOrder(); _applyChannelOrder(); @@ -828,6 +866,7 @@ class MeshCoreConnector extends ChangeNotifier { ..addAll(cached); for (final contact in cached) { _ensureContactSmazSettingLoaded(contact.publicKeyHex); + _ensureContactCyr2LatSettingLoaded(contact.publicKeyHex); } } @@ -840,9 +879,12 @@ class MeshCoreConnector extends ChangeNotifier { Future 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); } } @@ -4432,6 +4474,15 @@ 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(); + }); + } + /// 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) { @@ -4440,8 +4491,12 @@ 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)) { + return Cyr2Lat.encode(text); + } } return text; } @@ -4450,8 +4505,12 @@ 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)) { + return Cyr2Lat.encode(text); + } } return text; } diff --git a/lib/helpers/cyr2lat.dart b/lib/helpers/cyr2lat.dart new file mode 100644 index 00000000..a7ec5dde --- /dev/null +++ b/lib/helpers/cyr2lat.dart @@ -0,0 +1,63 @@ +class Cyr2Lat { + static Map _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 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 ''; + } +} diff --git a/lib/l10n/app_bg.arb b/lib/l10n/app_bg.arb index 7ac54177..8f59d981 100644 --- a/lib/l10n/app_bg.arb +++ b/lib/l10n/app_bg.arb @@ -388,6 +388,16 @@ } }, "channels_smazCompression": "Компресия SMAZ", + "channels_cyr2latCompression": "Компресия Cyr2Lat", + "channels_cyr2latCompressionDscr": "Заменя някои кирилични символи с латиница при изпращане.", + "channels_cyr2latSettingsHeading": "Настройки на Cyr2Lat", + "channels_cyr2latSettingsSubheading": "Списък със замествания", + "channels_cyr2latSettingsDscr": "Редактиране на JSON конфигурацията за заместване на символи", + "channels_cyr2latSettingsDialogHint": "JSON карта за замествания", + "channels_cyr2latSettingsDialogSuccess": "Списъкът със замествания е актуализиран", + "channels_cyr2latSettingsDialogWrongJSON": "Неправилен JSON: {error}", + "channels_cyr2latSettingsDialogReset": "Възстановяване на първоначалните настройки", + "channels_cyr2latSettingsDialogResetted": "Настройките за заместване на Cyr2Lat са възстановени към първоначалните", "channels_channelUpdated": "Каналът \"{name}\" е актуализиран", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 46955052..e0cc58b5 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -388,6 +388,16 @@ } }, "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_cyr2latSettingsDialogSuccess": "Ersetzungsliste aktualisiert", + "channels_cyr2latSettingsDialogWrongJSON": "Ungültiges JSON: {error}", + "channels_cyr2latSettingsDialogReset": "Auf Standard zurücksetzen", + "channels_cyr2latSettingsDialogResetted": "Die Cyr2Lat-Ersetzungseinstellungen wurden auf die Standardeinstellungen zurückgesetzt", "channels_channelUpdated": "Kanal \"{name}\" aktualisiert", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 8ad6bf37..03612b8d 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -580,6 +580,21 @@ } }, "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_cyr2latSettingsDialogSuccess": "The list of replacements has been updated", + "channels_cyr2latSettingsDialogWrongJSON": "Invalid JSON: {error}", + "@channels_cyr2latSettingsDialogWrongJSON": { + "placeholders": { + "error": {} + } + }, + "channels_cyr2latSettingsDialogReset": "Reset to default", + "channels_cyr2latSettingsDialogResetted": "Cyr2Lat replacement settings reset to default", "channels_channelUpdated": "Channel \"{name}\" updated", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index ac9527bd..bcb1b708 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -388,6 +388,16 @@ } }, "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_cyr2latSettingsDialogSuccess": "Lista de sustituciones actualizada", + "channels_cyr2latSettingsDialogWrongJSON": "JSON incorrecto: {error}", + "channels_cyr2latSettingsDialogReset": "Restablecer valores predeterminados", + "channels_cyr2latSettingsDialogResetted": "La configuración de sustituciones de Cyr2Lat se ha restablecido a los valores predeterminados", "channels_channelUpdated": "Canal \"{name}\" actualizado", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index a942aa2b..d0813a23 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -388,6 +388,16 @@ } }, "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_cyr2latSettingsDialogSuccess": "Liste de remplacement mise à jour", + "channels_cyr2latSettingsDialogWrongJSON": "JSON incorrect : {error}", + "channels_cyr2latSettingsDialogReset": "Réinitialiser les paramètres par défaut", + "channels_cyr2latSettingsDialogResetted": "Les paramètres de remplacement Cyr2Lat ont été réinitialisés aux valeurs par défaut", "channels_channelUpdated": "Le canal \"{name}\" a été mis à jour", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_hu.arb b/lib/l10n/app_hu.arb index 6f43463c..45a40a5f 100644 --- a/lib/l10n/app_hu.arb +++ b/lib/l10n/app_hu.arb @@ -548,6 +548,16 @@ } }, "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_cyr2latSettingsDialogSuccess": "A csere lista frissítve", + "channels_cyr2latSettingsDialogWrongJSON": "Hibás JSON: {error}", + "channels_cyr2latSettingsDialogReset": "Alapértelmezett értékekre állítás", + "channels_cyr2latSettingsDialogResetted": "A Cyr2Lat helyettesítési beállítások alapértelmezett értékekre lettek állítva", "channels_channelUpdated": "A {name} csatorna frissítve", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 387c8cff..5f78fdcf 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -388,6 +388,16 @@ } }, "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_cyr2latSettingsDialogSuccess": "Elenco delle sostituzioni aggiornato", + "channels_cyr2latSettingsDialogWrongJSON": "JSON non corretto: {error}", + "channels_cyr2latSettingsDialogReset": "Ripristina impostazioni predefinite", + "channels_cyr2latSettingsDialogResetted": "Le impostazioni di sostituzione Cyr2Lat sono state ripristinate alle impostazioni predefinite", "channels_channelUpdated": "Canale \"{name}\" aggiornato", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index b63f146d..ae3d6898 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -548,6 +548,16 @@ } }, "channels_smazCompression": "SMAZ 圧縮", + "channels_cyr2latCompression": "Cyr2Lat 圧縮", + "channels_cyr2latCompressionDscr": "送信時に一部のキリル文字をラテン文字に置き換えます。", + "channels_cyr2latSettingsHeading": "cyr2latの設定", + "channels_cyr2latSettingsSubheading": "置換リスト", + "channels_cyr2latSettingsDscr": "文字置換のJSON設定を編集する", + "channels_cyr2latSettingsDialogHint": "JSON置換マップ", + "channels_cyr2latSettingsDialogSuccess": "置換リストが更新されました", + "channels_cyr2latSettingsDialogWrongJSON": "不正なJSON: {error}", + "channels_cyr2latSettingsDialogReset": "初期設定に戻す", + "channels_cyr2latSettingsDialogResetted": "cyr2latの置換設定が初期設定に戻されました", "channels_channelUpdated": "チャンネル「{name}」が更新されました", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index 40721fe1..982bd421 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -548,6 +548,16 @@ } }, "channels_smazCompression": "SMAZ 압축", + "channels_cyr2latCompression": "Cyr2Lat 압축", + "channels_cyr2latCompressionDscr": "보낼 때 일부 키릴 문자를 라틴 문자로 바꿉니다.", + "channels_cyr2latSettingsHeading": "Cyr2Lat 설정", + "channels_cyr2latSettingsSubheading": "변환 목록", + "channels_cyr2latSettingsDscr": "문자 변환 JSON 구성 편집", + "channels_cyr2latSettingsDialogHint": "JSON 변환 맵", + "channels_cyr2latSettingsDialogSuccess": "변환 목록이 업데이트되었습니다", + "channels_cyr2latSettingsDialogWrongJSON": "잘못된 JSON: {error}", + "channels_cyr2latSettingsDialogReset": "초기값으로 초기화", + "channels_cyr2latSettingsDialogResetted": "Cyr2Lat 변환 설정이 초기값으로 초기화되었습니다", "channels_channelUpdated": "채널 \"{name}\"이 업데이트되었습니다.", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 2c1342d0..419d8c98 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -2176,6 +2176,66 @@ 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_cyr2latSettingsDialogSuccess. + /// + /// In en, this message translates to: + /// **'The list of replacements has been updated'** + String get channels_cyr2latSettingsDialogSuccess; + + /// 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_cyr2latSettingsDialogReset. + /// + /// In en, this message translates to: + /// **'Reset to default'** + String get channels_cyr2latSettingsDialogReset; + + /// No description provided for @channels_cyr2latSettingsDialogResetted. + /// + /// In en, this message translates to: + /// **'Cyr2Lat replacement settings reset to default'** + String get channels_cyr2latSettingsDialogResetted; + /// No description provided for @channels_channelUpdated. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index b3e12799..a34ae450 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -1174,6 +1174,43 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'Списъкът със замествания е актуализиран'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'Неправилен JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => + 'Възстановяване на първоначалните настройки'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Настройките за заместване на Cyr2Lat са възстановени към първоначалните'; + @override String channels_channelUpdated(String name) { return 'Каналът \"$name\" е актуализиран'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index d7c16914..7f6cfb25 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -1170,6 +1170,42 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'Ersetzungsliste aktualisiert'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'Ungültiges JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => 'Auf Standard zurücksetzen'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Die Cyr2Lat-Ersetzungseinstellungen wurden auf die Standardeinstellungen zurückgesetzt'; + @override String channels_channelUpdated(String name) { return 'Kanal \"$name\" aktualisiert'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index a2a88b0d..c63079e2 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -1150,6 +1150,42 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'The list of replacements has been updated'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'Invalid JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => 'Reset to default'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Cyr2Lat replacement settings reset to default'; + @override String channels_channelUpdated(String name) { return 'Channel \"$name\" updated'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index a1270124..4f2980c8 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -1172,6 +1172,43 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'Lista de sustituciones actualizada'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'JSON incorrecto: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => + 'Restablecer valores predeterminados'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'La configuración de sustituciones de Cyr2Lat se ha restablecido a los valores predeterminados'; + @override String channels_channelUpdated(String name) { return 'Canal \"$name\" actualizado'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index a0063914..57105660 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -1177,6 +1177,44 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'Liste de remplacement mise à jour'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'JSON incorrect : $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => + 'Réinitialiser les paramètres par défaut'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Les paramètres de remplacement Cyr2Lat ont été réinitialisés aux valeurs par défaut'; + @override String channels_channelUpdated(String name) { return 'Le canal \"$name\" a été mis à jour'; diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index 1ad8558c..690fed15 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -1177,6 +1177,42 @@ 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 get channels_cyr2latSettingsDialogSuccess => 'A csere lista frissítve'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'Hibás JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => + 'Alapértelmezett értékekre állítás'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'A Cyr2Lat helyettesítési beállítások alapértelmezett értékekre lettek állítva'; + @override String channels_channelUpdated(String name) { return 'A $name csatorna frissítve'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 3a55559a..beea60d4 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -1173,6 +1173,44 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'Elenco delle sostituzioni aggiornato'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'JSON non corretto: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => + 'Ripristina impostazioni predefinite'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Le impostazioni di sostituzione Cyr2Lat sono state ripristinate alle impostazioni predefinite'; + @override String channels_channelUpdated(String name) { return 'Canale \"$name\" aggiornato'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index afb8c29b..6dadfa38 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -1117,6 +1117,39 @@ 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 get channels_cyr2latSettingsDialogSuccess => '置換リストが更新されました'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return '不正なJSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => '初期設定に戻す'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'cyr2latの置換設定が初期設定に戻されました'; + @override String channels_channelUpdated(String name) { return 'チャンネル「$name」が更新されました'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index ff4bd261..358c7b1b 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -1112,6 +1112,39 @@ 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 get channels_cyr2latSettingsDialogSuccess => '변환 목록이 업데이트되었습니다'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return '잘못된 JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => '초기값으로 초기화'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Cyr2Lat 변환 설정이 초기값으로 초기화되었습니다'; + @override String channels_channelUpdated(String name) { return '채널 \"$name\"이 업데이트되었습니다.'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index dd770e15..78908531 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -1161,6 +1161,43 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'Lijst met vervangingen bijgewerkt'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'Onjuiste JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => + 'Terugzetten naar standaard'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'De instellingen voor Cyr2Lat-vervangingen zijn teruggezet naar de standaardinstellingen'; + @override String channels_channelUpdated(String name) { return 'Kanaal \"$name\" is bijgewerkt'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 357dd7e3..fcb236fc 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -1181,6 +1181,43 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'Lista zamian zaktualizowana'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'Nieprawidłowy JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => + 'Przywróć ustawienia domyślne'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Ustawienia zamiany Cyr2Lat zostały przywrócone do wartości domyślnych'; + @override String channels_channelUpdated(String name) { return 'Kanał \"$name\" został zaktualizowany'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index 2dfcd8bd..97c5a50f 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -1172,6 +1172,43 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'Lista de substituições atualizada'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'JSON incorreto: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => + 'Redefinir para os valores iniciais'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'As configurações de substituição Cyr2Lat foram redefinidas para os valores iniciais'; + @override String channels_channelUpdated(String name) { return 'Canal \"$name\" atualizado'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 4fac42ce..a4d6dd07 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -1172,6 +1172,41 @@ 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 get channels_cyr2latSettingsDialogSuccess => 'Список замен обновлён'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'Некорректный JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => 'Сбросить к начальным'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Настройки замен Cyr2Lat сброшены к начальным'; + @override String channels_channelUpdated(String name) { return 'Канал \"$name\" обновлён'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index c42e0249..5f523de0 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -1161,6 +1161,43 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'Zoznam nahradení bol aktualizovaný'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'Nesprávny JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => + 'Obnoviť predvolené nastavenia'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Nastavenia nahradzovania Cyr2Lat boli obnovené na predvolené'; + @override String channels_channelUpdated(String name) { return 'Kanál \"$name\" bol aktualizovaný'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index 2d89aa41..91df3cf4 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -1159,6 +1159,42 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'Seznam zamenjav je posodobljen'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'Nepravilen JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => 'Ponastavi na privzete'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Nastavitve zamenjav Cyr2Lat so ponastavljene na privzete'; + @override String channels_channelUpdated(String name) { return 'Kanal $name je bil posodobljen'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index 38e08939..2273c426 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -1151,6 +1151,42 @@ 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 get channels_cyr2latSettingsDialogSuccess => + 'Ersättningslistan har uppdaterats'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'Felaktig JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => 'Återställ till standard'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Inställningarna för Cyr2Lat-ersättningar har återställts till standard'; + @override String channels_channelUpdated(String name) { return 'Kanalen \"$name\" har uppdaterats'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 8ed4b9ff..639cbd12 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -1166,6 +1166,41 @@ 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 get channels_cyr2latSettingsDialogSuccess => 'Список замін оновлено'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'Некоректний JSON: $error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => 'Скинути до початкових'; + + @override + String get channels_cyr2latSettingsDialogResetted => + 'Налаштування замін Cyr2Lat скинуто до початкових'; + @override String channels_channelUpdated(String name) { return 'Канал «$name» оновлено'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 4f38c64a..0ccb9c59 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -1099,6 +1099,38 @@ 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 get channels_cyr2latSettingsDialogSuccess => '替換清單已更新'; + + @override + String channels_cyr2latSettingsDialogWrongJSON(Object error) { + return 'JSON 格式錯誤:$error'; + } + + @override + String get channels_cyr2latSettingsDialogReset => '還原為預設值'; + + @override + String get channels_cyr2latSettingsDialogResetted => 'Cyr2Lat 替換設定已還原為預設值'; + @override String channels_channelUpdated(String name) { return '频道 \"$name\" 已更新'; diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 96bdb845..8ffadd08 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -388,6 +388,16 @@ } }, "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_cyr2latSettingsDialogSuccess": "Lijst met vervangingen bijgewerkt", + "channels_cyr2latSettingsDialogWrongJSON": "Onjuiste JSON: {error}", + "channels_cyr2latSettingsDialogReset": "Terugzetten naar standaard", + "channels_cyr2latSettingsDialogResetted": "De instellingen voor Cyr2Lat-vervangingen zijn teruggezet naar de standaardinstellingen", "channels_channelUpdated": "Kanaal \"{name}\" is bijgewerkt", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index b62a78af..d44f8c80 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -398,6 +398,16 @@ } }, "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_cyr2latSettingsDialogSuccess": "Lista zamian zaktualizowana", + "channels_cyr2latSettingsDialogWrongJSON": "Nieprawidłowy JSON: {error}", + "channels_cyr2latSettingsDialogReset": "Przywróć ustawienia domyślne", + "channels_cyr2latSettingsDialogResetted": "Ustawienia zamiany Cyr2Lat zostały przywrócone do wartości domyślnych", "channels_channelUpdated": "Kanał \"{name}\" został zaktualizowany", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index bf3e8936..67840825 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -388,6 +388,16 @@ } }, "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_cyr2latSettingsDialogSuccess": "Lista de substituições atualizada", + "channels_cyr2latSettingsDialogWrongJSON": "JSON incorreto: {error}", + "channels_cyr2latSettingsDialogReset": "Redefinir para os valores iniciais", + "channels_cyr2latSettingsDialogResetted": "As configurações de substituição Cyr2Lat foram redefinidas para os valores iniciais", "channels_channelUpdated": "Canal \"{name}\" atualizado", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index a83d1394..a2052b02 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -252,6 +252,16 @@ "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_cyr2latSettingsDialogSuccess": "Список замен обновлён", + "channels_cyr2latSettingsDialogWrongJSON": "Некорректный JSON: {error}", + "channels_cyr2latSettingsDialogReset": "Сбросить к начальным", + "channels_cyr2latSettingsDialogResetted": "Настройки замен Cyr2Lat сброшены к начальным", "channels_channelUpdated": "Канал \"{name}\" обновлён", "channels_publicChannelAdded": "Публичный канал добавлен", "channels_sortBy": "Сортировка", diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index e4466c30..27db3e77 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -388,6 +388,16 @@ } }, "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_cyr2latSettingsDialogSuccess": "Zoznam nahradení bol aktualizovaný", + "channels_cyr2latSettingsDialogWrongJSON": "Nesprávny JSON: {error}", + "channels_cyr2latSettingsDialogReset": "Obnoviť predvolené nastavenia", + "channels_cyr2latSettingsDialogResetted": "Nastavenia nahradzovania Cyr2Lat boli obnovené na predvolené", "channels_channelUpdated": "Kanál \"{name}\" bol aktualizovaný", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_sl.arb b/lib/l10n/app_sl.arb index f6a317ef..b941fe78 100644 --- a/lib/l10n/app_sl.arb +++ b/lib/l10n/app_sl.arb @@ -388,6 +388,16 @@ } }, "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_cyr2latSettingsDialogSuccess": "Seznam zamenjav je posodobljen", + "channels_cyr2latSettingsDialogWrongJSON": "Nepravilen JSON: {error}", + "channels_cyr2latSettingsDialogReset": "Ponastavi na privzete", + "channels_cyr2latSettingsDialogResetted": "Nastavitve zamenjav Cyr2Lat so ponastavljene na privzete", "channels_channelUpdated": "Kanal {name} je bil posodobljen", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb index eab348c7..ca9abac8 100644 --- a/lib/l10n/app_sv.arb +++ b/lib/l10n/app_sv.arb @@ -388,6 +388,16 @@ } }, "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_cyr2latSettingsDialogSuccess": "Ersättningslistan har uppdaterats", + "channels_cyr2latSettingsDialogWrongJSON": "Felaktig JSON: {error}", + "channels_cyr2latSettingsDialogReset": "Återställ till standard", + "channels_cyr2latSettingsDialogResetted": "Inställningarna för Cyr2Lat-ersättningar har återställts till standard", "channels_channelUpdated": "Kanalen \"{name}\" har uppdaterats", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index a005b369..826d3b84 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -389,6 +389,16 @@ } }, "channels_smazCompression": "Стиснення SMAZ", + "channels_cyr2latCompression": "Стиснення Cyr2Lat", + "channels_cyr2latCompressionDscr": "Замінює деякі кириличні символи на латиницю при відправці.", + "channels_cyr2latSettingsHeading": "Налаштування Cyr2Lat", + "channels_cyr2latSettingsSubheading": "Список замін", + "channels_cyr2latSettingsDscr": "Редагувати JSON-конфігурацію заміни символів", + "channels_cyr2latSettingsDialogHint": "JSON-карта замін", + "channels_cyr2latSettingsDialogSuccess": "Список замін оновлено", + "channels_cyr2latSettingsDialogWrongJSON": "Некоректний JSON: {error}", + "channels_cyr2latSettingsDialogReset": "Скинути до початкових", + "channels_cyr2latSettingsDialogResetted": "Налаштування замін Cyr2Lat скинуто до початкових", "channels_channelUpdated": "Канал «{name}» оновлено", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 9dc2325c..6932a4bc 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -403,6 +403,16 @@ } }, "channels_smazCompression": "SMAZ 压缩", + "channels_cyr2latCompression": "Cyr2Lat 压缩", + "channels_cyr2latCompressionDscr": "发送时将一些西里尔字符替换为拉丁字符。", + "channels_cyr2latSettingsHeading": "Cyr2Lat 設定", + "channels_cyr2latSettingsSubheading": "替換清單", + "channels_cyr2latSettingsDscr": "編輯 JSON 字元替換設定檔", + "channels_cyr2latSettingsDialogHint": "JSON 替換映射表", + "channels_cyr2latSettingsDialogSuccess": "替換清單已更新", + "channels_cyr2latSettingsDialogWrongJSON": "JSON 格式錯誤:{error}", + "channels_cyr2latSettingsDialogReset": "還原為預設值", + "channels_cyr2latSettingsDialogResetted": "Cyr2Lat 替換設定已還原為預設值", "channels_channelUpdated": "频道 \"{name}\" 已更新", "@channels_channelUpdated": { "placeholders": { diff --git a/lib/models/app_settings.dart b/lib/models/app_settings.dart index d3d3421d..d7c176e4 100644 --- a/lib/models/app_settings.dart +++ b/lib/models/app_settings.dart @@ -13,6 +13,31 @@ extension UnitSystemValue on UnitSystem { } } +const Map defaultCyr2LatCharMap = { + 'А': 'A', + 'В': 'B', + 'Е': 'E', + 'Ё': 'E', + 'З': '3', + 'К': 'K', + 'М': 'M', + 'Н': 'H', + 'О': 'O', + 'Р': 'P', + 'С': 'C', + 'Т': 'T', + 'Х': 'X', + 'Ь': 'b', + 'а': 'a', + 'е': 'e', + 'ё': 'e', + 'о': 'o', + 'р': 'p', + 'с': 'c', + 'у': 'y', + 'х': 'x', +}; + class AppSettings { static const Object _unset = Object(); @@ -57,6 +82,7 @@ class AppSettings { final String? translationModelSourceUrl; final String? translationSelectedModelId; final List translationDownloadedModels; + final Map cyr2latCharMap; AppSettings({ this.clearPathOnMaxRetry = false, @@ -100,10 +126,12 @@ class AppSettings { this.translationModelSourceUrl, this.translationSelectedModelId, List? translationDownloadedModels, + Map? cyr2latCharMap, }) : batteryChemistryByDeviceId = batteryChemistryByDeviceId ?? {}, batteryChemistryByRepeaterId = batteryChemistryByRepeaterId ?? {}, mutedChannels = mutedChannels ?? {}, - translationDownloadedModels = translationDownloadedModels ?? const []; + translationDownloadedModels = translationDownloadedModels ?? const [], + cyr2latCharMap = cyr2latCharMap ?? defaultCyr2LatCharMap; Map toJson() { return { @@ -150,6 +178,7 @@ class AppSettings { 'translation_downloaded_models': translationDownloadedModels .map((model) => model.toJson()) .toList(), + 'cyr2lat_char_map': cyr2latCharMap, }; } @@ -237,6 +266,11 @@ class AppSettings { ) .toList() ?? const [], + cyr2latCharMap: + (json['cyr2lat_char_map'] as Map?)?.map( + (key, value) => MapEntry(key.toString(), value.toString()), + ) ?? + defaultCyr2LatCharMap, ); } @@ -282,6 +316,7 @@ class AppSettings { Object? translationModelSourceUrl = _unset, Object? translationSelectedModelId = _unset, List? translationDownloadedModels, + Map? cyr2latCharMap, }) { return AppSettings( clearPathOnMaxRetry: clearPathOnMaxRetry ?? this.clearPathOnMaxRetry, @@ -345,6 +380,7 @@ class AppSettings { : translationSelectedModelId as String?, translationDownloadedModels: translationDownloadedModels ?? this.translationDownloadedModels, + cyr2latCharMap: cyr2latCharMap ?? this.cyr2latCharMap, ); } } diff --git a/lib/screens/app_settings_screen.dart b/lib/screens/app_settings_screen.dart index 80d8adbc..425248de 100644 --- a/lib/screens/app_settings_screen.dart +++ b/lib/screens/app_settings_screen.dart @@ -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), ], ); @@ -1255,6 +1258,116 @@ class AppSettingsScreen extends StatelessWidget { return '${sizeMb.toStringAsFixed(1)} MB • $source'; } + Widget _buildCyr2LatCard( + BuildContext context, + AppSettingsService settingsService, + ) { + 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), + ), + ), + ListTile( + leading: const Icon(Icons.translate), + + title: Text(context.l10n.channels_cyr2latSettingsSubheading), + subtitle: Text(context.l10n.channels_cyr2latSettingsDscr), + trailing: const Icon(Icons.chevron_right), + onTap: () => _showCyr2LatDialog(context, settingsService), + ), + ], + ), + ); + } + + void _showCyr2LatDialog( + BuildContext context, + AppSettingsService settingsService, + ) { + final controller = TextEditingController( + text: const JsonEncoder.withIndent( + ' ', + ).convert(settingsService.settings.cyr2latCharMap), + ); + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(context.l10n.channels_cyr2latSettingsDscr), + content: SingleChildScrollView( + child: TextField( + controller: controller, + maxLines: 20, + decoration: InputDecoration( + border: const OutlineInputBorder(), + hintText: context.l10n.channels_cyr2latSettingsDialogHint, + ), + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(context.l10n.common_cancel), + ), + TextButton( + onPressed: () { + try { + final json = + jsonDecode(controller.text) as Map; + final map = json.map( + (key, value) => MapEntry(key, value.toString()), + ); + final newSettings = settingsService.settings.copyWith( + cyr2latCharMap: map, + ); + settingsService.updateSettings(newSettings); + Navigator.pop(context); + showDismissibleSnackBar( + context, + content: Text( + context.l10n.channels_cyr2latSettingsDialogSuccess, + ), + ); + } catch (e) { + showDismissibleSnackBar( + context, + content: Text( + context.l10n.channels_cyr2latSettingsDialogWrongJSON( + e.toString(), + ), + ), + ); + } + }, + child: Text(context.l10n.common_save), + ), + TextButton( + onPressed: () { + final newSettings = settingsService.settings.copyWith( + cyr2latCharMap: defaultCyr2LatCharMap, + ); + settingsService.updateSettings(newSettings); + Navigator.pop(context); + showDismissibleSnackBar( + context, + content: Text( + context.l10n.channels_cyr2latSettingsDialogResetted, + ), + ); + }, + child: Text(context.l10n.channels_cyr2latSettingsDialogReset), + ), + ], + ), + ); + } + Widget _buildDebugCard( BuildContext context, AppSettingsService settingsService, diff --git a/lib/screens/channel_chat_screen.dart b/lib/screens/channel_chat_screen.dart index b203cbbe..4f43e9c3 100644 --- a/lib/screens/channel_chat_screen.dart +++ b/lib/screens/channel_chat_screen.dart @@ -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'; @@ -1100,7 +1101,12 @@ class _ChannelChatScreenState extends State { 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, @@ -1213,6 +1219,15 @@ class _ChannelChatScreenState extends State { 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(); diff --git a/lib/screens/channels_screen.dart b/lib/screens/channels_screen.dart index 44c7a69c..868aac77 100644 --- a/lib/screens/channels_screen.dart +++ b/lib/screens/channels_screen.dart @@ -1404,6 +1404,7 @@ class _ChannelsScreenState extends State final nameController = TextEditingController(text: channel.name); final pskController = TextEditingController(text: channel.pskHex); bool smazEnabled = connector.isChannelSmazEnabled(channel.index); + bool cyr2latEnabled = connector.isChannelCyr2LatEnabled(channel.index); showDialog( context: context, @@ -1449,7 +1450,26 @@ class _ChannelsScreenState extends State 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; + } + }), ), ], ), @@ -1482,6 +1502,10 @@ class _ChannelsScreenState extends State channel.index, smazEnabled, ); + await connector.setChannelCyr2LatEnabled( + channel.index, + cyr2latEnabled, + ); if (!context.mounted) return; showDismissibleSnackBar( context, diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart index ffa8344b..b9cdf0ec 100644 --- a/lib/screens/chat_screen.dart +++ b/lib/screens/chat_screen.dart @@ -13,6 +13,7 @@ import 'package:latlong2/latlong.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'; @@ -574,9 +575,12 @@ class _ChatScreenState extends State { 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, @@ -695,6 +699,18 @@ class _ChatScreenState extends State { 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( @@ -1196,8 +1212,12 @@ class _ChatScreenState extends State { void _showContactSettings(BuildContext context) { final connector = Provider.of(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, + ); bool teleBaseEnabled = contact.teleBaseEnabled; bool teleLocEnabled = contact.teleLocEnabled; bool teleEnvEnabled = contact.teleEnvEnabled; @@ -1228,7 +1248,39 @@ class _ChatScreenState extends State { 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; + } + }); }, ), const Divider(height: 8), diff --git a/lib/services/app_settings_service.dart b/lib/services/app_settings_service.dart index 283ce726..820fe0ec 100644 --- a/lib/services/app_settings_service.dart +++ b/lib/services/app_settings_service.dart @@ -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; _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 updateSettings(AppSettings newSettings) async { _settings = newSettings; + Cyr2Lat.setCharMap(_settings.cyr2latCharMap); notifyListeners(); final prefs = PrefsManager.instance; diff --git a/lib/storage/channel_settings_store.dart b/lib/storage/channel_settings_store.dart index 276826d7..eb637acd 100644 --- a/lib/storage/channel_settings_store.dart +++ b/lib/storage/channel_settings_store.dart @@ -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 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,41 @@ class ChannelSettingsStore { final key = '$keyFor$channelIndex'; await prefs.setBool(key, enabled); } + + Future 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'; + final oldKey = '$_cyr2latKeyPrefix$channelIndex'; + bool? enabled = prefs.getBool(key); + if (enabled == null) { + // Attempt migration from legacy unscoped key on first load + enabled = prefs.getBool(oldKey); + prefs.remove(oldKey); + if (enabled != null) { + appLogger.info( + 'Migrating channel Cyr2Lat settings from legacy key $oldKey to scoped key $key', + ); + await prefs.setBool(key, enabled); + } + } + return enabled ?? false; + } + + Future 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); + } } diff --git a/lib/storage/contact_settings_store.dart b/lib/storage/contact_settings_store.dart index 94c6430e..5db15add 100644 --- a/lib/storage/contact_settings_store.dart +++ b/lib/storage/contact_settings_store.dart @@ -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 loadSmazEnabled(String contactKeyHex) async { if (publicKeyHex.isEmpty) { @@ -46,4 +48,41 @@ class ContactSettingsStore { final key = '$keyFor$contactKeyHex'; await prefs.setBool(key, enabled); } + + Future 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'; + final oldKey = '$_cyr2latKeyPrefix$contactKeyHex'; + bool? enabled = prefs.getBool(key); + if (enabled == null) { + // Attempt migration from legacy unscoped key on first load + enabled = prefs.getBool(oldKey); + prefs.remove(oldKey); + if (enabled != null) { + appLogger.info( + 'Migrating contact Cyr2Lat settings from legacy key $oldKey to scoped key $key', + ); + await prefs.setBool(key, enabled); + } + } + return prefs.getBool(key) ?? false; + } + + Future 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); + } }