From 723bf7293c0765510096dbf0a29a324493d62f0d Mon Sep 17 00:00:00 2001 From: ericz Date: Tue, 17 Mar 2026 21:56:42 +0100 Subject: [PATCH 01/25] location aware channel_message_path --- .gitignore | 3 +- lib/screens/channel_message_path_screen.dart | 93 +++++++++++--------- 2 files changed, 54 insertions(+), 42 deletions(-) diff --git a/.gitignore b/.gitignore index a312737..779856c 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ secrets.dart .DS_Store .AppleDouble .LSOverride +macos/Flutter/GeneratedPluginRegistrant.swift # iOS **/ios/Pods/ @@ -85,4 +86,4 @@ keystore.properties .vscode/settings.json # Cloudflare Wrangler -.wrangler \ No newline at end of file +.wrangler diff --git a/lib/screens/channel_message_path_screen.dart b/lib/screens/channel_message_path_screen.dart index 747c2bf..e2c5b49 100644 --- a/lib/screens/channel_message_path_screen.dart +++ b/lib/screens/channel_message_path_screen.dart @@ -40,8 +40,7 @@ class ChannelMessagePathScreen extends StatelessWidget { final primaryPath = !channelMessage && !message.isOutgoing ? Uint8List.fromList(primaryPathTmp.reversed.toList()) : primaryPathTmp; - final contacts = connector.allContacts; - final hops = _buildPathHops(primaryPath, contacts, l10n); + final hops = _buildPathHops(primaryPath, connector, l10n); final hasHopDetails = primaryPath.isNotEmpty; final observedLabel = _formatObservedHops( primaryPath.length, @@ -365,8 +364,7 @@ class _ChannelMessagePathMapScreenState : selectedPathTmp; final selectedIndex = _indexForPath(selectedPath, observedPaths); - final contacts = connector.allContacts; - final hops = _buildPathHops(selectedPath, contacts, context.l10n); + final hops = _buildPathHops(selectedPath, connector, context.l10n); final points = []; @@ -787,17 +785,62 @@ class _ObservedPath { List<_PathHop> _buildPathHops( Uint8List pathBytes, - List contacts, + MeshCoreConnector connector, AppLocalizations l10n, ) { + if (pathBytes.isEmpty) return const []; + final candidatesByPrefix = >{}; + for (final contact in connector.allContacts) { + if (contact.publicKey.isEmpty) continue; + if (contact.type != advTypeRepeater && contact.type != advTypeRoom) { + continue; + } + final prefix = contact.publicKey.first; + candidatesByPrefix.putIfAbsent(prefix, () => []).add(contact); + } + for (final candidates in candidatesByPrefix.values) { + candidates.sort((a, b) => b.lastSeen.compareTo(a.lastSeen)); + } + final startPoint = + (connector.selfLatitude != null && connector.selfLongitude != null) + ? LatLng(connector.selfLatitude!, connector.selfLongitude!) + : null; + var previousPosition = startPoint; + final distance = Distance(); + final hops = <_PathHop>[]; for (var i = 0; i < pathBytes.length; i++) { - final prefix = pathBytes[i]; - final contact = _matchContactForPrefix(contacts, prefix); + final searchPoint = i == 0 ? startPoint : previousPosition; + final candidates = candidatesByPrefix[pathBytes[i]]; + Contact? contact; + if (candidates != null && candidates.isNotEmpty) { + var bestIndex = 0; + if (searchPoint != null) { + var bestDistance = double.infinity; + for (var j = 0; j < candidates.length; j++) { + final candidate = candidates[j]; + if (!candidate.hasLocation) continue; + final currentDistance = distance( + searchPoint, + LatLng(candidate.latitude!, candidate.longitude!), + ); + if (currentDistance < bestDistance) { + bestDistance = currentDistance; + bestIndex = j; + } + } + } + contact = candidates.removeAt(bestIndex); + if (candidates.isEmpty) { + candidatesByPrefix.remove(pathBytes[i]); + } + } + + previousPosition = _resolvePosition(contact); hops.add( _PathHop( index: i + 1, - prefix: prefix, + prefix: pathBytes[i], contact: contact, position: _resolvePosition(contact), l10n: l10n, @@ -807,44 +850,12 @@ List<_PathHop> _buildPathHops( return hops; } -Contact? _matchContactForPrefix(List contacts, int prefix) { - final matches = contacts - .where( - (contact) => - (contact.type == advTypeRepeater || contact.type == advTypeRoom) && - contact.publicKey.isNotEmpty && - contact.publicKey[0] == prefix, - ) - .toList(); - if (matches.isEmpty) return null; - - Contact? pickWhere(bool Function(Contact) predicate) { - for (final contact in matches) { - if (predicate(contact)) return contact; - } - return null; - } - - return pickWhere((c) => c.type == advTypeRepeater && _hasValidLocation(c)) ?? - pickWhere((c) => c.type == advTypeRepeater) ?? - pickWhere(_hasValidLocation) ?? - matches.first; -} - LatLng? _resolvePosition(Contact? contact) { if (contact == null) return null; - if (!_hasValidLocation(contact)) return null; + if (!contact.hasLocation) return null; return LatLng(contact.latitude!, contact.longitude!); } -bool _hasValidLocation(Contact contact) { - final lat = contact.latitude; - final lon = contact.longitude; - if (lat == null || lon == null) return false; - if (lat == 0 && lon == 0) return false; - return true; -} - String _formatPrefix(int prefix) { return prefix.toRadixString(16).padLeft(2, '0').toUpperCase(); } From d2df2b0beda0f3826bd6d712be77e474e87b8066 Mon Sep 17 00:00:00 2001 From: ericszimmermann Date: Tue, 17 Mar 2026 22:23:23 +0100 Subject: [PATCH 02/25] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- lib/screens/channel_message_path_screen.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/screens/channel_message_path_screen.dart b/lib/screens/channel_message_path_screen.dart index e2c5b49..d2f8d81 100644 --- a/lib/screens/channel_message_path_screen.dart +++ b/lib/screens/channel_message_path_screen.dart @@ -836,13 +836,16 @@ List<_PathHop> _buildPathHops( } } - previousPosition = _resolvePosition(contact); + final resolvedPosition = _resolvePosition(contact); + if (resolvedPosition != null) { + previousPosition = resolvedPosition; + } hops.add( _PathHop( index: i + 1, prefix: pathBytes[i], contact: contact, - position: _resolvePosition(contact), + position: resolvedPosition, l10n: l10n, ), ); From 11cb14a9256b53b43a937b55fe5810dbc2d10163 Mon Sep 17 00:00:00 2001 From: ericz Date: Tue, 17 Mar 2026 23:22:23 +0100 Subject: [PATCH 03/25] focus on hop if you click on one in the legend. --- lib/screens/channel_message_path_screen.dart | 29 +++++++++++++++++++ macos/Flutter/GeneratedPluginRegistrant.swift | 2 ++ 2 files changed, 31 insertions(+) diff --git a/lib/screens/channel_message_path_screen.dart b/lib/screens/channel_message_path_screen.dart index d2f8d81..0092f22 100644 --- a/lib/screens/channel_message_path_screen.dart +++ b/lib/screens/channel_message_path_screen.dart @@ -302,10 +302,12 @@ class _ChannelMessagePathMapScreenState extends State { static const double _labelZoomThreshold = 8.5; + final MapController _mapController = MapController(); Uint8List? _selectedPath; double _pathDistance = 0.0; bool _showNodeLabels = true; bool _didReceivePositionUpdate = false; + int? _focusedHopIndex; @override void initState() { @@ -336,6 +338,22 @@ class _ChannelMessagePathMapScreenState return totalDistance; } + void _focusHop(_PathHop hop) { + if (!hop.hasLocation) return; + final targetZoom = _didReceivePositionUpdate + ? max(_mapController.camera.zoom, 14.0) + : 14.0; + _mapController.move(hop.position!, targetZoom); + } + + void _onHopTapped(_PathHop hop) { + _focusHop(hop); + if (!mounted) return; + setState(() { + _focusedHopIndex = hop.index; + }); + } + @override Widget build(BuildContext context) { return Consumer( @@ -419,6 +437,7 @@ class _ChannelMessagePathMapScreenState children: [ FlutterMap( key: mapKey, + mapController: _mapController, options: MapOptions( initialCenter: initialCenter, initialZoom: initialZoom, @@ -470,6 +489,7 @@ class _ChannelMessagePathMapScreenState ) { setState(() { _selectedPath = observedPaths[index].pathBytes; + _focusedHopIndex = null; }); }), if (points.isEmpty) @@ -725,8 +745,17 @@ class _ChannelMessagePathMapScreenState separatorBuilder: (_, _) => const Divider(height: 1), itemBuilder: (context, index) { final hop = hops[index]; + final isFocused = _focusedHopIndex == hop.index; return ListTile( dense: true, + enabled: hop.hasLocation, + selected: isFocused, + selectedTileColor: Theme.of( + context, + ).colorScheme.primary.withValues(alpha: 0.12), + onTap: hop.hasLocation + ? () => _onHopTapped(hop) + : null, leading: CircleAvatar( radius: 14, child: Text( diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index d2ea57e..4084d9b 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,6 +9,7 @@ import flutter_blue_plus_darwin import flutter_local_notifications import mobile_scanner import package_info_plus +import path_provider_foundation import share_plus import shared_preferences_foundation import sqflite_darwin @@ -20,6 +21,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) From 7b3c09973604c592bca8d3002edd537788333d2a Mon Sep 17 00:00:00 2001 From: ericz Date: Wed, 18 Mar 2026 06:52:08 +0100 Subject: [PATCH 04/25] reduce zoomlevel --- lib/screens/channel_message_path_screen.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/screens/channel_message_path_screen.dart b/lib/screens/channel_message_path_screen.dart index 0092f22..76273c4 100644 --- a/lib/screens/channel_message_path_screen.dart +++ b/lib/screens/channel_message_path_screen.dart @@ -341,8 +341,8 @@ class _ChannelMessagePathMapScreenState void _focusHop(_PathHop hop) { if (!hop.hasLocation) return; final targetZoom = _didReceivePositionUpdate - ? max(_mapController.camera.zoom, 14.0) - : 14.0; + ? max(_mapController.camera.zoom, 10.0) + : 12.0; _mapController.move(hop.position!, targetZoom); } From 87d11c2e6b3d0e1e21099ee9c39ba852c863e92d Mon Sep 17 00:00:00 2001 From: ericszimmermann Date: Wed, 18 Mar 2026 07:00:16 +0100 Subject: [PATCH 05/25] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- lib/screens/channel_message_path_screen.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/screens/channel_message_path_screen.dart b/lib/screens/channel_message_path_screen.dart index 76273c4..f64cb53 100644 --- a/lib/screens/channel_message_path_screen.dart +++ b/lib/screens/channel_message_path_screen.dart @@ -848,7 +848,11 @@ List<_PathHop> _buildPathHops( var bestDistance = double.infinity; for (var j = 0; j < candidates.length; j++) { final candidate = candidates[j]; - if (!candidate.hasLocation) continue; + if (!candidate.hasLocation || + candidate.latitude == null || + candidate.longitude == null) { + continue; + } final currentDistance = distance( searchPoint, LatLng(candidate.latitude!, candidate.longitude!), From b88e5e647ad9a0cda4a994210aa84ae6717148b0 Mon Sep 17 00:00:00 2001 From: ericszimmermann Date: Wed, 18 Mar 2026 08:06:22 +0100 Subject: [PATCH 06/25] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- lib/screens/channel_message_path_screen.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/screens/channel_message_path_screen.dart b/lib/screens/channel_message_path_screen.dart index f64cb53..ea07eae 100644 --- a/lib/screens/channel_message_path_screen.dart +++ b/lib/screens/channel_message_path_screen.dart @@ -889,7 +889,10 @@ List<_PathHop> _buildPathHops( LatLng? _resolvePosition(Contact? contact) { if (contact == null) return null; if (!contact.hasLocation) return null; - return LatLng(contact.latitude!, contact.longitude!); + final latitude = contact.latitude; + final longitude = contact.longitude; + if (latitude == null || longitude == null) return null; + return LatLng(latitude, longitude); } String _formatPrefix(int prefix) { From dbefb0b5f419e90328cca9a775e2136f944b5630 Mon Sep 17 00:00:00 2001 From: Winston Lowe Date: Sat, 21 Mar 2026 13:01:02 -0700 Subject: [PATCH 07/25] feat: Enhance MeshCoreConnector with storage metrics and improve error handling - Added storageUsedKb and storageTotalKb properties to MeshCoreConnector. - Updated battery and storage frame parsing with improved error handling. - Refactored log RX data handling to use BufferReader for better readability and error management. - Enhanced message parsing in ChannelMessage and Message classes to utilize BufferReader. - Introduced new text type for signed messages in meshcore_protocol.dart. - Updated BLE debug log screen to use BufferReader for payload parsing. - Refactored message retry service to handle ACK hashes as integers instead of Uint8List. - Improved message storage serialization and deserialization to accommodate new expectedAckHash type. - Added wasPulled property to Contact model for better state management. --- lib/connector/meshcore_connector.dart | 191 ++++++++++++++---------- lib/connector/meshcore_protocol.dart | 135 ++++++++--------- lib/models/channel.dart | 21 +-- lib/models/channel_message.dart | 150 +++++++++---------- lib/models/contact.dart | 2 + lib/models/message.dart | 54 +++---- lib/screens/ble_debug_log_screen.dart | 110 +++++++------- lib/services/ble_debug_log_service.dart | 13 ++ lib/services/message_retry_service.dart | 30 ++-- lib/storage/message_store.dart | 8 +- lib/widgets/debug_frame_viewer.dart | 8 + 11 files changed, 382 insertions(+), 340 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 87a6755..e8555ff 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -257,6 +257,9 @@ class MeshCoreConnector extends ChangeNotifier { int? _activeChannelIndex; List _channelOrder = []; + int _storageUsedKb = -1; + int _storageTotalKb = -1; + // Getters MeshCoreConnectionState get state => _state; BluetoothDevice? get device => _device; @@ -338,6 +341,8 @@ class MeshCoreConnector extends ChangeNotifier { int? get firmwareVerCode => _firmwareVerCode; Map? get currentCustomVars => _currentCustomVars; int? get batteryMillivolts => _batteryMillivolts; + int? get storageUsedKb => _storageUsedKb; + int? get storageTotalKb => _storageTotalKb; int get maxContacts => _maxContacts; int get maxChannels => _maxChannels; Set get knownContactKeys => Set.unmodifiable(_knownContactKeys); @@ -3037,14 +3042,23 @@ class MeshCoreConnector extends ChangeNotifier { // [1-2] = battery_mv (uint16 LE) // [3-6] = storage_used_kb (uint32 LE) // [7-10] = storage_total_kb (uint32 LE) - if (frame.length >= 3) { - _batteryMillivolts = readUint16LE(frame, 1); + try { + final reader = BufferReader(frame); + reader.skipBytes(1); + _batteryMillivolts = reader.readInt16LE(); + _storageUsedKb = reader.readUInt32LE(); + _storageTotalKb = reader.readUInt32LE(); final volts = (_batteryMillivolts! / 1000.0).toStringAsFixed(2); _appDebugLogService?.info( 'Pulled battery: $volts V ($_batteryMillivolts mV)', tag: 'Battery', ); notifyListeners(); + } catch (e) { + _appDebugLogService?.error( + 'Error parsing battery and storage frame: $e', + tag: 'Connector', + ); } } @@ -3702,68 +3716,89 @@ class MeshCoreConnector extends ChangeNotifier { void _handleLogRxData(Uint8List frame) { if (frame.length < 4) return; - final raw = Uint8List.fromList(frame.sublist(3)); - final packet = _parseRawPacket(raw); - if (packet == null || packet.payloadType != _payloadTypeGroupText) return; + try { + final reader = BufferReader(frame); + reader.skipBytes(3); // Skip header - final payload = packet.payload; - if (payload.length <= _cipherMacSize) return; - final channelHash = payload[0]; - final encrypted = Uint8List.fromList(payload.sublist(1)); + final raw = reader.readRemainingBytes(); + final packet = _parseRawPacket(raw); + if (packet == null || packet.payloadType != _payloadTypeGroupText) return; - // Use cached channels as fallback if live channels not yet loaded - final channelsToSearch = _channels.isNotEmpty ? _channels : _cachedChannels; - for (final channel in channelsToSearch) { - if (channel.isEmpty) continue; - final hash = _computeChannelHash(channel.psk); - if (hash != channelHash) continue; + final payload = packet.payload; + if (payload.length <= _cipherMacSize) return; + final channelHash = payload[0]; + final encrypted = Uint8List.fromList(payload.sublist(1)); - final decrypted = _decryptPayload(channel.psk, encrypted); - if (decrypted == null || decrypted.length < 6) return; + // Use cached channels as fallback if live channels not yet loaded + final channelsToSearch = _channels.isNotEmpty + ? _channels + : _cachedChannels; + for (final channel in channelsToSearch) { + if (channel.isEmpty) continue; + final hash = _computeChannelHash(channel.psk); + if (hash != channelHash) continue; + try { + final decryptedBytes = _decryptPayload(channel.psk, encrypted); + if (decryptedBytes == null || decryptedBytes.length < 6) return; + final decrypted = BufferReader(decryptedBytes); + // Skip header + SNR + reserved (2) + decrypted.skipBytes(4); + final txtType = decrypted.readByte(); + if ((txtType >> 2) != 0) { + return; + } - final txtType = decrypted[4]; - if ((txtType >> 2) != 0) { - return; + final timestampRaw = decrypted.readUInt32LE(); + final text = decrypted.readString(); + final parsed = _splitSenderText(text); + final decodedText = + Smaz.tryDecodePrefixed(parsed.text) ?? parsed.text; + if (_shouldDropSelfChannelMessage( + parsed.senderName, + packet.pathBytes, + )) { + return; + } + + final pktHash = _computePacketHash( + packet.payloadType, + packet.payload, + ); + + final message = ChannelMessage( + senderKey: null, + senderName: parsed.senderName, + text: decodedText, + timestamp: DateTime.fromMillisecondsSinceEpoch(timestampRaw * 1000), + isOutgoing: false, + status: ChannelMessageStatus.sent, + pathLength: packet.isFlood ? packet.hopCount : 0, + pathBytes: packet.pathBytes, + channelIndex: channel.index, + packetHash: pktHash, + ); + + _updateContactLastMessageAtByName( + parsed.senderName, + message.timestamp, + pathBytes: message.pathBytes, + ); + final isNew = _addChannelMessage(channel.index, message); + _maybeIncrementChannelUnread(message, isNew: isNew); + notifyListeners(); + if (isNew) { + final label = channel.name.isEmpty + ? 'Channel ${channel.index}' + : channel.name; + _maybeNotifyChannelMessage(message, channelName: label); + } + return; + } catch (e) { + appLogger.warn('Decryption failed for channel ${channel.index}: $e'); + } } - - final timestampRaw = readUint32LE(decrypted, 0); - final text = readCString(decrypted, 5, decrypted.length - 5); - final parsed = _splitSenderText(text); - final decodedText = Smaz.tryDecodePrefixed(parsed.text) ?? parsed.text; - if (_shouldDropSelfChannelMessage(parsed.senderName, packet.pathBytes)) { - return; - } - - final pktHash = _computePacketHash(packet.payloadType, packet.payload); - - final message = ChannelMessage( - senderKey: null, - senderName: parsed.senderName, - text: decodedText, - timestamp: DateTime.fromMillisecondsSinceEpoch(timestampRaw * 1000), - isOutgoing: false, - status: ChannelMessageStatus.sent, - pathLength: packet.isFlood ? packet.hopCount : 0, - pathBytes: packet.pathBytes, - channelIndex: channel.index, - packetHash: pktHash, - ); - - _updateContactLastMessageAtByName( - parsed.senderName, - message.timestamp, - pathBytes: message.pathBytes, - ); - final isNew = _addChannelMessage(channel.index, message); - _maybeIncrementChannelUnread(message, isNew: isNew); - notifyListeners(); - if (isNew) { - final label = channel.name.isEmpty - ? 'Channel ${channel.index}' - : channel.name; - _maybeNotifyChannelMessage(message, channelName: label); - } - return; + } catch (e) { + appLogger.warn('Error handling log RX data frame: $e'); } } @@ -3774,15 +3809,15 @@ class MeshCoreConnector extends ChangeNotifier { // [2-5] = expected_ack_hash (uint32) // [6-9] = estimated_timeout_ms (uint32) - if (frame.length >= 10) { - final ackHash = Uint8List.fromList(frame.sublist(2, 6)); - final timeoutMs = readUint32LE(frame, 6); + try { + final reader = BufferReader(frame); + reader.skipBytes(2); // code + is_flood + final ackHash = reader.readUInt32LE(); + final timeoutMs = reader.readUInt32LE(); // Check if this is a CLI command ACK - if so, ignore it if (_lastSentWasCliCommand) { - final ackHashHex = ackHash - .map((b) => b.toRadixString(16).padLeft(2, '0')) - .join(); + final ackHashHex = ackHash.toRadixString(16).padLeft(8, '0'); debugPrint('Ignoring CLI command ACK (sent): $ackHashHex'); _lastSentWasCliCommand = false; return; @@ -3801,7 +3836,8 @@ class MeshCoreConnector extends ChangeNotifier { if (_markNextPendingChannelMessageSent()) { return; } - } else { + } catch (e) { + appLogger.warn('Error handling message sent frame: $e'); // Fallback to old behavior for (var messages in _conversations.values) { for (int i = messages.length - 1; i >= 0; i--) { @@ -3880,9 +3916,11 @@ class MeshCoreConnector extends ChangeNotifier { // [1-4] = ack_hash (uint32) // [5-8] = trip_time_ms (uint32) - if (frame.length >= 9) { - final ackHash = Uint8List.fromList(frame.sublist(1, 5)); - final tripTimeMs = readUint32LE(frame, 5); + try { + final reader = BufferReader(frame); + reader.skipBytes(1); // Skip code + final ackHash = reader.readUInt32LE(); + final tripTimeMs = reader.readUInt32LE(); // CLI command ACKs are already filtered in _handleMessageSent, so this should only see real messages @@ -3894,7 +3932,8 @@ class MeshCoreConnector extends ChangeNotifier { if (_retryService != null) { _retryService!.handleAckReceived(ackHash, tripTimeMs); } - } else { + } catch (e) { + appLogger.warn('Error handling send confirmed frame: $e'); // Fallback to old behavior for (var messages in _conversations.values) { for (int i = messages.length - 1; i >= 0; i--) { @@ -3909,10 +3948,8 @@ class MeshCoreConnector extends ChangeNotifier { } } - bool _handleRepeaterCommandSent(Uint8List ackHash, int timeoutMs) { - final ackHashHex = ackHash - .map((b) => b.toRadixString(16).padLeft(2, '0')) - .join(); + bool _handleRepeaterCommandSent(int ackHash, int timeoutMs) { + final ackHashHex = ackHash.toRadixString(16).padLeft(8, '0'); final entry = _pendingRepeaterAcks[ackHashHex]; if (entry == null) return false; @@ -3930,10 +3967,8 @@ class MeshCoreConnector extends ChangeNotifier { return true; } - bool _handleRepeaterCommandAck(Uint8List ackHash, int tripTimeMs) { - final ackHashHex = ackHash - .map((b) => b.toRadixString(16).padLeft(2, '0')) - .join(); + bool _handleRepeaterCommandAck(int ackHash, int tripTimeMs) { + final ackHashHex = ackHash.toRadixString(16).padLeft(8, '0'); final entry = _pendingRepeaterAcks.remove(ackHashHex); if (entry == null) return false; entry.timeout?.cancel(); diff --git a/lib/connector/meshcore_protocol.dart b/lib/connector/meshcore_protocol.dart index 01b41d4..a2e20cd 100644 --- a/lib/connector/meshcore_protocol.dart +++ b/lib/connector/meshcore_protocol.dart @@ -220,6 +220,7 @@ const int cmdGetAutoAddConfig = 59; // Text message types const int txtTypePlain = 0; const int txtTypeCliData = 1; +const int txtTypeSigned = 2; // Repeater request types (for server requests) const int reqTypeGetStatus = 0x01; @@ -314,6 +315,7 @@ const int autoAddSensorFlag = // Sizes const int pubKeySize = 32; +const int signatureSize = 64; const int maxPathSize = 64; const int pathHashSize = 1; const int maxNameSize = 32; @@ -377,88 +379,79 @@ const int msgTextOffset = 38; class ParsedContactText { final Uint8List senderPrefix; final String text; - const ParsedContactText({required this.senderPrefix, required this.text}); } ParsedContactText? parseContactMessageText(Uint8List frame) { if (frame.isEmpty) return null; - final code = frame[0]; - if (code != respCodeContactMsgRecv && code != respCodeContactMsgRecvV3) { - return null; - } - // Companion radio layout: - // [code][snr?][res?][res?][prefix x6][path_len][txt_type][timestamp x4][extra?][text...] - final isV3 = code == respCodeContactMsgRecvV3; - final prefixOffset = isV3 ? 4 : 1; - const prefixLen = 6; - final txtTypeOffset = prefixOffset + prefixLen + 1; - final timestampOffset = txtTypeOffset + 1; - final baseTextOffset = timestampOffset + 4; - if (frame.length <= baseTextOffset) return null; - - final flags = frame[txtTypeOffset]; - final shiftedType = flags >> 2; - final rawType = flags; - final isPlain = shiftedType == txtTypePlain || rawType == txtTypePlain; - final isCli = shiftedType == txtTypeCliData || rawType == txtTypeCliData; - if (!isPlain && !isCli) { - return null; - } - - var text = readCString( - frame, - baseTextOffset, - frame.length - baseTextOffset, - ).trim(); - if (text.isEmpty && frame.length > baseTextOffset + 4) { - text = readCString( - frame, - baseTextOffset + 4, - frame.length - (baseTextOffset + 4), - ).trim(); - } - if (text.isEmpty) return null; - - final senderPrefix = frame.sublist(prefixOffset, prefixOffset + prefixLen); - return ParsedContactText(senderPrefix: senderPrefix, text: text); -} - -// Helper to read uint32 little-endian -int readUint32LE(Uint8List data, int offset) { - return data[offset] | - (data[offset + 1] << 8) | - (data[offset + 2] << 16) | - (data[offset + 3] << 24); -} - -// Helper to read uint16 little-endian -int readUint16LE(Uint8List data, int offset) { - return data[offset] | (data[offset + 1] << 8); -} - -// Helper to read int32 little-endian -int readInt32LE(Uint8List data, int offset) { - int val = readUint32LE(data, offset); - if (val >= 0x80000000) val -= 0x100000000; - return val; -} - -// Helper to read null-terminated UTF-8 string -String readCString(Uint8List data, int offset, int maxLen) { - int end = offset; - while (end < offset + maxLen && end < data.length && data[end] != 0) { - end++; - } + final message = BufferReader(frame); try { - return utf8.decode(data.sublist(offset, end), allowMalformed: true); + final code = message.readByte(); + if (code != respCodeContactMsgRecv && code != respCodeContactMsgRecvV3) { + return null; + } + + // Companion radio layout: + // [code][snr?][res?][res?][prefix x6][path_len][txt_type][timestamp x4][extra?][text...] + if (code == respCodeContactMsgRecvV3) { + // Skip SNR and reserved bytes in v3 layout + message.skipBytes(3); + } + final senderPrefix = message.readBytes(6); // public key + message.skipBytes(1); // path length + final textType = message.readByte(); + message.skipBytes(4); // timestamp (4 bytes) + + final shiftedType = textType >> 2; + final isSigned = shiftedType == txtTypeSigned || textType == txtTypeSigned; + if (isSigned) { + // Signed messages have a 4-byte signature after the timestamp, before the text + message.skipBytes(4); + } + final text = message.readString(); + if (text.isEmpty) return null; + + return ParsedContactText(senderPrefix: senderPrefix, text: text); } catch (e) { - // Fallback to Latin-1 if UTF-8 decoding fails - return String.fromCharCodes(data.sublist(offset, end)); + return null; } } +// // Helper to read uint32 little-endian +// int readUint32LE(Uint8List data, int offset) { +// return data[offset] | +// (data[offset + 1] << 8) | +// (data[offset + 2] << 16) | +// (data[offset + 3] << 24); +// } + +// // Helper to read uint16 little-endian +// int readUint16LE(Uint8List data, int offset) { +// return data[offset] | (data[offset + 1] << 8); +// } + +// // Helper to read int32 little-endian +// int readInt32LE(Uint8List data, int offset) { +// int val = readUint32LE(data, offset); +// if (val >= 0x80000000) val -= 0x100000000; +// return val; +// } + +// // Helper to read null-terminated UTF-8 string +// String readCString(Uint8List data, int offset, int maxLen) { +// int end = offset; +// while (end < offset + maxLen && end < data.length && data[end] != 0) { +// end++; +// } +// try { +// return utf8.decode(data.sublist(offset, end), allowMalformed: true); +// } catch (e) { +// // Fallback to Latin-1 if UTF-8 decoding fails +// return String.fromCharCodes(data.sublist(offset, end)); +// } +// } + // Helper to convert public key to hex string String pubKeyToHex(Uint8List pubKey) { return pubKey.map((b) => b.toRadixString(16).padLeft(2, '0')).join(); diff --git a/lib/models/channel.dart b/lib/models/channel.dart index 1a2ecdc..4fdd627 100644 --- a/lib/models/channel.dart +++ b/lib/models/channel.dart @@ -24,20 +24,23 @@ class Channel { bool get isPublicChannel => pskHex == publicChannelPsk; - static Channel? fromFrame(Uint8List data) { + static Channel? fromFrame(Uint8List frame) { // CHANNEL_INFO format: // [0] = RESP_CODE_CHANNEL_INFO (18) // [1] = channel_idx // [2-33] = name (32 bytes, null-terminated) // [34-49] = psk (16 bytes) - if (data.length < 50) return null; - if (data[0] != respCodeChannelInfo) return null; - - final index = data[1]; - final name = readCString(data, 2, 32); - final psk = Uint8List.fromList(data.sublist(34, 50)); - - return Channel(index: index, name: name, psk: psk); + if (frame.length < 50) return null; + final reader = BufferReader(frame); + try { + if (reader.readByte() != respCodeChannelInfo) return null; + final index = reader.readByte(); + final name = reader.readCStringGreedy(32); + final psk = reader.readBytes(16); + return Channel(index: index, name: name, psk: psk); + } catch (e) { + return null; + } } static Channel empty(int index) { diff --git a/lib/models/channel_message.dart b/lib/models/channel_message.dart index b0af3eb..ab81630 100644 --- a/lib/models/channel_message.dart +++ b/lib/models/channel_message.dart @@ -109,89 +109,85 @@ class ChannelMessage { ); } - static ChannelMessage? fromFrame(Uint8List data) { + static ChannelMessage? fromFrame(Uint8List frame) { // CHANNEL_MSG_RECV format varies by version: // V3: [0]=code [1]=SNR [2]=rsv1 [3]=rsv2 [4]=channel_idx [5]=path_len [path... optional] [txt_type] [timestamp x4] [text...] // Non-V3: [0]=code [1]=channel_idx [2]=path_len [3]=txt_type [4-7]=timestamp [8+]=text - if (data.length < 8) return null; + if (frame.length < 8) return null; + final reader = BufferReader(frame); + try { + final code = reader.readByte(); + if (code != respCodeChannelMsgRecv && code != respCodeChannelMsgRecvV3) { + return null; + } - final code = data[0]; - if (code != respCodeChannelMsgRecv && code != respCodeChannelMsgRecvV3) { + int pathLen; + int txtType; + Uint8List pathBytes = Uint8List(0); + int channelIdx; + if (code == respCodeChannelMsgRecvV3) { + reader.skipBytes(1); // Skip SNR + final flags = reader.readByte(); + reader.skipBytes(1); // Skip reserved byte + channelIdx = reader.readByte(); + pathLen = reader.readByte(); + txtType = reader.readByte(); + final hasPath = (flags & 0x01) != 0; + if (hasPath) { + reader.rewind(); // Rewind to read path length again for pathBytes + pathBytes = reader.readBytes(pathLen); + // Force text type to plain if path is present + txtType = txtTypePlain; + } else { + pathLen = 0; + } + } else { + channelIdx = reader.readByte(); + pathLen = reader.readByte(); + txtType = reader.readByte(); + } + final timestampRaw = reader.readUInt32LE(); + + if (txtType != txtTypePlain) { + return null; + } + + final text = reader.readString(); + + // Extract sender name and actual message from "name: msg" format + String senderName = 'Unknown'; + String actualText = text; + + final colonIndex = text.indexOf(':'); + if (colonIndex > 0 && colonIndex < text.length - 1 && colonIndex < 50) { + final potentialSender = text.substring(0, colonIndex); + if (!RegExp(r'[:\[\]]').hasMatch(potentialSender)) { + senderName = potentialSender; + final offset = + (colonIndex + 1 < text.length && text[colonIndex + 1] == ' ') + ? colonIndex + 2 + : colonIndex + 1; + actualText = text.substring(offset); + } + } + + final decodedText = Smaz.tryDecodePrefixed(actualText) ?? actualText; + + return ChannelMessage( + senderKey: null, + senderName: senderName, + text: decodedText, + timestamp: DateTime.fromMillisecondsSinceEpoch(timestampRaw * 1000), + isOutgoing: false, + status: ChannelMessageStatus.sent, + pathLength: pathLen, + pathBytes: pathBytes, + channelIndex: channelIdx, + ); + } catch (e) { + // If parsing fails, return null to avoid crashes return null; } - - int timestampOffset, textOffset, pathLenOffset, txtTypeOffset; - Uint8List pathBytes = Uint8List(0); - int channelIdx; - - if (code == respCodeChannelMsgRecvV3) { - channelIdx = data[4]; - pathLenOffset = 5; - final pathLen = data[pathLenOffset].toSigned(8); - var cursor = 6; - final hasPathBytesFlag = (data[2] & 0x01) != 0; - final canFitPath = pathLen > 0 && data.length >= cursor + pathLen + 5; - final hasValidTxtType = - cursor < data.length && - (data[cursor] == txtTypePlain || data[cursor] == txtTypeCliData); - if ((hasPathBytesFlag || (canFitPath && !hasValidTxtType)) && - canFitPath) { - pathBytes = Uint8List.fromList(data.sublist(cursor, cursor + pathLen)); - cursor += pathLen; - } - txtTypeOffset = cursor; - cursor += 1; // txt_type - timestampOffset = cursor; - textOffset = cursor + 4; - } else { - channelIdx = data[1]; - pathLenOffset = 2; - txtTypeOffset = 3; - timestampOffset = 4; - textOffset = 8; - } - - if (data.length < textOffset + 1) return null; - - final txtType = data[txtTypeOffset]; - if (txtType != txtTypePlain) { - return null; - } - - final pathLen = data[pathLenOffset].toSigned(8); - final timestampRaw = readUint32LE(data, timestampOffset); - final text = readCString(data, textOffset, data.length - textOffset); - - // Extract sender name and actual message from "name: msg" format - String senderName = 'Unknown'; - String actualText = text; - - final colonIndex = text.indexOf(':'); - if (colonIndex > 0 && colonIndex < text.length - 1 && colonIndex < 50) { - final potentialSender = text.substring(0, colonIndex); - if (!RegExp(r'[:\[\]]').hasMatch(potentialSender)) { - senderName = potentialSender; - final offset = - (colonIndex + 1 < text.length && text[colonIndex + 1] == ' ') - ? colonIndex + 2 - : colonIndex + 1; - actualText = text.substring(offset); - } - } - - final decodedText = Smaz.tryDecodePrefixed(actualText) ?? actualText; - - return ChannelMessage( - senderKey: null, - senderName: senderName, - text: decodedText, - timestamp: DateTime.fromMillisecondsSinceEpoch(timestampRaw * 1000), - isOutgoing: false, - status: ChannelMessageStatus.sent, - pathLength: pathLen, - pathBytes: pathBytes, - channelIndex: channelIdx, - ); } static ChannelMessage outgoing( diff --git a/lib/models/contact.dart b/lib/models/contact.dart index acd1da9..5c80893 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -18,6 +18,7 @@ class Contact { final DateTime lastSeen; final DateTime lastMessageAt; final bool isActive; + final bool wasPulled; final Uint8List? rawPacket; Contact({ @@ -34,6 +35,7 @@ class Contact { required this.lastSeen, DateTime? lastMessageAt, this.isActive = true, + this.wasPulled = false, this.rawPacket, }) : lastMessageAt = lastMessageAt ?? lastSeen; diff --git a/lib/models/message.dart b/lib/models/message.dart index 6f6ed88..d1660dd 100644 --- a/lib/models/message.dart +++ b/lib/models/message.dart @@ -16,7 +16,7 @@ class Message { final String? messageId; final int retryCount; final int? estimatedTimeoutMs; - final Uint8List? expectedAckHash; + final int? expectedAckHash; final DateTime? sentAt; final DateTime? deliveredAt; final int? tripTimeMs; @@ -56,7 +56,7 @@ class Message { MessageStatus? status, int? retryCount, int? estimatedTimeoutMs, - Uint8List? expectedAckHash, + int? expectedAckHash, DateTime? sentAt, DateTime? deliveredAt, int? tripTimeMs, @@ -90,33 +90,35 @@ class Message { ); } - static Message? fromFrame(Uint8List data, Uint8List selfPubKey) { - if (data.length < msgTextOffset + 1) return null; + static Message? fromFrame(Uint8List frame, Uint8List selfPubKey) { + if (frame.length < msgTextOffset + 1) return null; + final reader = BufferReader(frame); + try { + final code = reader.readByte(); + if (code != respCodeContactMsgRecv && code != respCodeContactMsgRecvV3) { + return null; + } - final code = data[0]; - if (code != respCodeContactMsgRecv && code != respCodeContactMsgRecvV3) { + final senderKey = reader.readBytes(pubKeySize); + final timestampRaw = reader.readInt32LE(); + final flags = reader.readByte(); + if ((flags >> 2) != txtTypePlain) { + return null; + } + final text = reader.readString(); + + return Message( + senderKey: senderKey, + text: text, + timestamp: DateTime.fromMillisecondsSinceEpoch(timestampRaw * 1000), + isOutgoing: false, + isCli: false, + status: MessageStatus.delivered, + pathBytes: Uint8List(0), + ); + } catch (e) { return null; } - - final senderKey = Uint8List.fromList( - data.sublist(msgPubKeyOffset, msgPubKeyOffset + pubKeySize), - ); - final timestampRaw = readUint32LE(data, msgTimestampOffset); - final flags = data[msgFlagsOffset]; - if ((flags >> 2) != txtTypePlain) { - return null; - } - final text = readCString(data, msgTextOffset, data.length - msgTextOffset); - - return Message( - senderKey: senderKey, - text: text, - timestamp: DateTime.fromMillisecondsSinceEpoch(timestampRaw * 1000), - isOutgoing: false, - isCli: false, - status: MessageStatus.delivered, - pathBytes: Uint8List(0), - ); } static Message outgoing( diff --git a/lib/screens/ble_debug_log_screen.dart b/lib/screens/ble_debug_log_screen.dart index a90f9f0..1009bc4 100644 --- a/lib/screens/ble_debug_log_screen.dart +++ b/lib/screens/ble_debug_log_screen.dart @@ -283,66 +283,66 @@ class _BleDebugLogScreenState extends State { if (payload.length < 101) { return 'ADVERT (short)'; } - var offset = 0; - final pubKey = _bytesToHex( - payload.sublist(offset, offset + 32), - spaced: false, - ); - offset += 32; - final timestamp = readUint32LE(payload, offset); - offset += 4; - offset += 64; // signature - final flags = payload[offset++]; - final role = _deviceRoleLabel(flags & 0x0F); - final hasLocation = (flags & 0x10) != 0; - final hasFeature1 = (flags & 0x20) != 0; - final hasFeature2 = (flags & 0x40) != 0; - final hasName = (flags & 0x80) != 0; - String? name; - double? lat; - double? lon; - if (hasLocation && payload.length >= offset + 8) { - lat = readInt32LE(payload, offset) / 1000000.0; - lon = readInt32LE(payload, offset + 4) / 1000000.0; - offset += 8; + final reader = BufferReader(payload); + try { + final pubKey = _bytesToHex(reader.readBytes(pubKeySize), spaced: false); + + final timestamp = reader.readUInt32LE(); + reader.skipBytes(signatureSize); + final flags = reader.readByte(); + final role = _deviceRoleLabel(flags & 0x0F); + final hasLocation = (flags & 0x10) != 0; + final hasFeature1 = (flags & 0x20) != 0; + final hasFeature2 = (flags & 0x40) != 0; + final hasName = (flags & 0x80) != 0; + String? name; + double? lat; + double? lon; + if (hasLocation) { + lat = reader.readInt32LE() / 1000000.0; + lon = reader.readInt32LE() / 1000000.0; + } + if (hasFeature1) reader.skipBytes(2); + if (hasFeature2) reader.skipBytes(2); + if (hasName) { + name = reader.readCStringGreedy(maxNameSize); + } + final namePart = (name != null && name.isNotEmpty) ? ' name="$name"' : ''; + final locPart = (lat != null && lon != null) + ? ' loc=${lat.toStringAsFixed(6)},${lon.toStringAsFixed(6)}' + : ''; + return 'ADVERT role=$role ts=$timestamp$namePart$locPart key=${pubKey.substring(0, 12)}…'; + } catch (e) { + return 'ADVERT (invalid)'; } - if (hasFeature1) offset += 2; - if (hasFeature2) offset += 2; - if (hasName && payload.length > offset) { - final rawName = String.fromCharCodes(payload.sublist(offset)); - final nul = rawName.indexOf('\u0000'); - name = nul >= 0 ? rawName.substring(0, nul) : rawName; - name = name.trim(); - } - final namePart = (name != null && name.isNotEmpty) ? ' name="$name"' : ''; - final locPart = (lat != null && lon != null) - ? ' loc=${lat.toStringAsFixed(6)},${lon.toStringAsFixed(6)}' - : ''; - return 'ADVERT role=$role ts=$timestamp$namePart$locPart key=${pubKey.substring(0, 12)}…'; } String _decodeControlSummary(Uint8List payload) { - if (payload.isEmpty) return 'CONTROL (empty)'; - final flags = payload[0]; - final subType = flags & 0xF0; - if (subType == 0x80) { - if (payload.length < 6) return 'CONTROL DISCOVER_REQ (short)'; - final typeFilter = payload[1]; - final tag = readUint32LE(payload, 2); - final since = payload.length >= 10 ? readUint32LE(payload, 6) : 0; - return 'CONTROL DISCOVER_REQ filter=0x${typeFilter.toRadixString(16).padLeft(2, '0')} tag=$tag since=$since'; + final reader = BufferReader(payload); + try { + final flags = reader.readByte(); + final subType = flags & 0xF0; + if (subType == 0x80) { + if (payload.length < 6) return 'CONTROL DISCOVER_REQ (short)'; + final typeFilter = reader.readByte(); + final tag = reader.readInt32LE(); + final since = payload.length >= 10 ? reader.readInt32LE() : 0; + return 'CONTROL DISCOVER_REQ filter=0x${typeFilter.toRadixString(16).padLeft(2, '0')} tag=$tag since=$since'; + } + if (subType == 0x90) { + if (payload.length < 14) return 'CONTROL DISCOVER_RESP (short)'; + final nodeType = flags & 0x0F; + final snrRaw = payload[1]; + final snrSigned = snrRaw > 127 ? snrRaw - 256 : snrRaw; + final snr = snrSigned / 4.0; + final tag = reader.readInt32LE(); + final keyLen = payload.length - 6; + return 'CONTROL DISCOVER_RESP node=${_deviceRoleLabel(nodeType)} snr=${snr.toStringAsFixed(2)} tag=$tag key=$keyLen'; + } + return 'CONTROL subtype=0x${subType.toRadixString(16).padLeft(2, '0')}'; + } catch (e) { + return 'CONTROL (invalid)'; } - if (subType == 0x90) { - if (payload.length < 14) return 'CONTROL DISCOVER_RESP (short)'; - final nodeType = flags & 0x0F; - final snrRaw = payload[1]; - final snrSigned = snrRaw > 127 ? snrRaw - 256 : snrRaw; - final snr = snrSigned / 4.0; - final tag = readUint32LE(payload, 2); - final keyLen = payload.length - 6; - return 'CONTROL DISCOVER_RESP node=${_deviceRoleLabel(nodeType)} snr=${snr.toStringAsFixed(2)} tag=$tag key=$keyLen'; - } - return 'CONTROL subtype=0x${subType.toRadixString(16).padLeft(2, '0')}'; } String _payloadTypeLabel(int payloadType) { diff --git a/lib/services/ble_debug_log_service.dart b/lib/services/ble_debug_log_service.dart index df2822b..745b243 100644 --- a/lib/services/ble_debug_log_service.dart +++ b/lib/services/ble_debug_log_service.dart @@ -245,6 +245,19 @@ class BleDebugLogService extends ChangeNotifier { } } + // Helper to read uint32 little-endian + int readUint32LE(Uint8List data, int offset) { + return data[offset] | + (data[offset + 1] << 8) | + (data[offset + 2] << 16) | + (data[offset + 3] << 24); + } + + // // Helper to read uint16 little-endian + int readUint16LE(Uint8List data, int offset) { + return data[offset] | (data[offset + 1] << 8); + } + String _frameDetail(int code, Uint8List frame) { switch (code) { case respCodeSent: diff --git a/lib/services/message_retry_service.dart b/lib/services/message_retry_service.dart index b284425..1920418 100644 --- a/lib/services/message_retry_service.dart +++ b/lib/services/message_retry_service.dart @@ -11,7 +11,7 @@ import 'app_debug_log_service.dart'; class _AckHistoryEntry { final String messageId; - final List ackHashes; + final List ackHashes; final DateTime timestamp; _AckHistoryEntry({ @@ -77,7 +77,7 @@ class MessageRetryService extends ChangeNotifier { final Map _pendingContacts = {}; final Map> _attemptPathHistory = {}; final Map _ackHashToMessageId = {}; - final Map> _expectedAckHashes = {}; + final Map> _expectedAckHashes = {}; final List<_AckHistoryEntry> _ackHistory = []; final Map> _sendQueue = {}; final Set _activeMessages = {}; @@ -341,13 +341,11 @@ class MessageRetryService extends ChangeNotifier { config.sendMessage(contact, message.text, attempt, timestampSeconds); } - bool updateMessageFromSent(Uint8List ackHash, int timeoutMs) { + bool updateMessageFromSent(int ackHash, int timeoutMs) { final config = _config; if (config == null) return false; - final ackHashHex = ackHash - .map((b) => b.toRadixString(16).padLeft(2, '0')) - .join(); + final ackHashHex = ackHash.toRadixString(16).padLeft(8, '0'); // Try hash-based matching (fixes LoRa message drops causing mismatches) String? messageId = _expectedHashToMessageId.remove(ackHashHex); @@ -389,10 +387,8 @@ class MessageRetryService extends ChangeNotifier { // Add this ACK hash to the list of expected ACKs for this message (for history) _expectedAckHashes[messageId] ??= []; - if (!_expectedAckHashes[messageId]!.any( - (hash) => listEquals(hash, ackHash), - )) { - _expectedAckHashes[messageId]!.add(Uint8List.fromList(ackHash)); + if (!_expectedAckHashes[messageId]!.any((hash) => hash == ackHash)) { + _expectedAckHashes[messageId]!.add(ackHash); } // Calculate timeout: prefer ML prediction, then device-provided, then physics fallback @@ -559,10 +555,10 @@ class MessageRetryService extends ChangeNotifier { } } - bool _checkAckHistory(Uint8List ackHash) { + bool _checkAckHistory(int ackHash) { for (final entry in _ackHistory) { for (final expectedHash in entry.ackHashes) { - if (listEquals(expectedHash, ackHash)) { + if (expectedHash == ackHash) { return true; } } @@ -570,13 +566,11 @@ class MessageRetryService extends ChangeNotifier { return false; } - void handleAckReceived(Uint8List ackHash, int tripTimeMs) { + void handleAckReceived(int ackHash, int tripTimeMs) { final config = _config; String? matchedMessageId; int? matchedAttemptIndex; - final ackHashHex = ackHash - .map((b) => b.toRadixString(16).padLeft(2, '0')) - .join(); + final ackHashHex = ackHash.toRadixString(16).padLeft(8, '0'); // Clean up old ACK hash mappings (older than 15 minutes) final cutoffTime = DateTime.now().subtract(const Duration(minutes: 15)); @@ -606,7 +600,7 @@ class MessageRetryService extends ChangeNotifier { final expectedHashes = entry.value; for (final expectedHash in expectedHashes) { - if (listEquals(expectedHash, ackHash)) { + if (expectedHash == ackHash) { matchedMessageId = messageId; matchedAttemptIndex = expectedHashes.indexOf(expectedHash); break; @@ -689,7 +683,7 @@ class MessageRetryService extends ChangeNotifier { for (var entry in _pendingMessages.entries) { final message = entry.value; if (message.expectedAckHash != null && - listEquals(message.expectedAckHash, ackHash)) { + message.expectedAckHash == ackHash) { final contact = _pendingContacts[entry.key]; return contact?.publicKeyHex; } diff --git a/lib/storage/message_store.dart b/lib/storage/message_store.dart index 44d3621..5550911 100644 --- a/lib/storage/message_store.dart +++ b/lib/storage/message_store.dart @@ -85,9 +85,7 @@ class MessageStore { 'messageId': msg.messageId, 'retryCount': msg.retryCount, 'estimatedTimeoutMs': msg.estimatedTimeoutMs, - 'expectedAckHash': msg.expectedAckHash != null - ? base64Encode(msg.expectedAckHash!) - : null, + 'expectedAckHash': msg.expectedAckHash, 'sentAt': msg.sentAt?.millisecondsSinceEpoch, 'deliveredAt': msg.deliveredAt?.millisecondsSinceEpoch, 'tripTimeMs': msg.tripTimeMs, @@ -119,9 +117,7 @@ class MessageStore { messageId: json['messageId'] as String?, retryCount: json['retryCount'] as int? ?? 0, estimatedTimeoutMs: json['estimatedTimeoutMs'] as int?, - expectedAckHash: json['expectedAckHash'] != null - ? Uint8List.fromList(base64Decode(json['expectedAckHash'] as String)) - : null, + expectedAckHash: json['expectedAckHash'] as int? ?? 0, sentAt: json['sentAt'] != null ? DateTime.fromMillisecondsSinceEpoch(json['sentAt'] as int) : null, diff --git a/lib/widgets/debug_frame_viewer.dart b/lib/widgets/debug_frame_viewer.dart index c8dc371..05e312b 100644 --- a/lib/widgets/debug_frame_viewer.dart +++ b/lib/widgets/debug_frame_viewer.dart @@ -10,6 +10,14 @@ class DebugFrameViewer { Uint8List frame, String title, ) { + // Helper to read uint32 little-endian + int readUint32LE(Uint8List data, int offset) { + return data[offset] | + (data[offset + 1] << 8) | + (data[offset + 2] << 16) | + (data[offset + 3] << 24); + } + final hexString = frame .map((b) => b.toRadixString(16).padLeft(2, '0')) .join(' '); From 767dc1164edae9b42be64911375bc3232819199c Mon Sep 17 00:00:00 2001 From: Winston Lowe Date: Sun, 22 Mar 2026 10:50:11 -0700 Subject: [PATCH 08/25] refactor: Replace string reading methods with CString equivalents and improve error handling --- lib/connector/meshcore_connector.dart | 92 ++++++++++------------ lib/connector/meshcore_protocol.dart | 71 +++++++---------- lib/models/channel_message.dart | 10 +-- lib/models/message.dart | 2 +- lib/services/ble_debug_log_service.dart | 13 --- lib/services/message_retry_service.dart | 9 +-- lib/widgets/debug_frame_viewer.dart | 8 -- test/services/retry_and_protocol_test.dart | 12 +-- 8 files changed, 82 insertions(+), 135 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index e8555ff..5dc660e 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -2127,9 +2127,7 @@ class MeshCoreConnector extends ChangeNotifier { outboundText, selfKey, ); - final ackHashHex = ackHash - .map((b) => b.toRadixString(16).padLeft(2, '0')) - .join(); + final ackHashHex = ackHashToHex(ackHash); final messageBytes = utf8.encode(outboundText).length; _pendingRepeaterAcks[ackHashHex]?.timeout?.cancel(); _pendingRepeaterAcks[ackHashHex] = _RepeaterAckContext( @@ -2901,7 +2899,7 @@ class MeshCoreConnector extends ChangeNotifier { _currentSf = reader.readByte(); _currentCr = reader.readByte(); - _selfName = reader.readString(); + _selfName = reader.readCString(); } catch (e) { _appDebugLogService?.error( 'Error parsing SELF_INFO frame: $e', @@ -3554,7 +3552,7 @@ class MeshCoreConnector extends ChangeNotifier { reader.skipBytes(4); // Skip extra 4 bytes for signed/plain variants } - final msgText = reader.readString(); + final msgText = reader.readCString(); final flags = txtType; final shiftedType = flags >> 2; @@ -3724,10 +3722,9 @@ class MeshCoreConnector extends ChangeNotifier { final packet = _parseRawPacket(raw); if (packet == null || packet.payloadType != _payloadTypeGroupText) return; - final payload = packet.payload; - if (payload.length <= _cipherMacSize) return; - final channelHash = payload[0]; - final encrypted = Uint8List.fromList(payload.sublist(1)); + final payload = BufferReader(packet.payload); + final channelHash = payload.readByte(); + final encrypted = Uint8List.fromList(payload.readRemainingBytes()); // Use cached channels as fallback if live channels not yet loaded final channelsToSearch = _channels.isNotEmpty @@ -3741,15 +3738,14 @@ class MeshCoreConnector extends ChangeNotifier { final decryptedBytes = _decryptPayload(channel.psk, encrypted); if (decryptedBytes == null || decryptedBytes.length < 6) return; final decrypted = BufferReader(decryptedBytes); - // Skip header + SNR + reserved (2) - decrypted.skipBytes(4); + + final timestampRaw = decrypted.readUInt32LE(); final txtType = decrypted.readByte(); if ((txtType >> 2) != 0) { return; } - final timestampRaw = decrypted.readUInt32LE(); - final text = decrypted.readString(); + final text = decrypted.readCString(); final parsed = _splitSenderText(text); final decodedText = Smaz.tryDecodePrefixed(parsed.text) ?? parsed.text; @@ -3811,13 +3807,13 @@ class MeshCoreConnector extends ChangeNotifier { try { final reader = BufferReader(frame); - reader.skipBytes(2); // code + is_flood + reader.skipBytes(2); //Skip code and is_flood final ackHash = reader.readUInt32LE(); final timeoutMs = reader.readUInt32LE(); // Check if this is a CLI command ACK - if so, ignore it if (_lastSentWasCliCommand) { - final ackHashHex = ackHash.toRadixString(16).padLeft(8, '0'); + final ackHashHex = ackHashToHex(ackHash); debugPrint('Ignoring CLI command ACK (sent): $ackHashHex'); _lastSentWasCliCommand = false; return; @@ -3949,7 +3945,7 @@ class MeshCoreConnector extends ChangeNotifier { } bool _handleRepeaterCommandSent(int ackHash, int timeoutMs) { - final ackHashHex = ackHash.toRadixString(16).padLeft(8, '0'); + final ackHashHex = ackHashToHex(ackHash); final entry = _pendingRepeaterAcks[ackHashHex]; if (entry == null) return false; @@ -3968,7 +3964,7 @@ class MeshCoreConnector extends ChangeNotifier { } bool _handleRepeaterCommandAck(int ackHash, int tripTimeMs) { - final ackHashHex = ackHash.toRadixString(16).padLeft(8, '0'); + final ackHashHex = ackHashToHex(ackHash); final entry = _pendingRepeaterAcks.remove(ackHashHex); if (entry == null) return false; entry.timeout?.cancel(); @@ -4319,36 +4315,34 @@ class MeshCoreConnector extends ChangeNotifier { } _RawPacket? _parseRawPacket(Uint8List raw) { - if (raw.length < 3) return null; - var index = 0; - final header = raw[index++]; - final routeType = header & _phRouteMask; - final hasTransport = - routeType == _routeTransportFlood || routeType == _routeTransportDirect; - if (hasTransport) { - if (raw.length < index + 4) return null; - index += 4; - } - if (raw.length <= index) return null; - final pathLenRaw = raw[index++]; - final pathByteLen = _decodePathByteLen(pathLenRaw); - if (raw.length < index + pathByteLen) return null; - final pathBytes = Uint8List.fromList( - raw.sublist(index, index + pathByteLen), - ); - index += pathByteLen; - if (raw.length <= index) return null; - final payload = Uint8List.fromList(raw.sublist(index)); + try { + final reader = BufferReader(raw); + final header = reader.readByte(); + final routeType = header & _phRouteMask; + final hasTransport = + routeType == _routeTransportFlood || + routeType == _routeTransportDirect; + if (hasTransport) { + reader.skipBytes(2); // Skip reserved bytes in transport header + } + final pathLenRaw = reader.readByte(); + final pathByteLen = _decodePathByteLen(pathLenRaw); + final pathBytes = reader.readBytes(pathByteLen); + final payload = reader.readBytes(reader.remaining); - return _RawPacket( - header: header, - routeType: routeType, - payloadType: (header >> _phTypeShift) & _phTypeMask, - payloadVer: (header >> _phVerShift) & _phVerMask, - pathLenRaw: pathLenRaw, - pathBytes: pathBytes, - payload: payload, - ); + return _RawPacket( + header: header, + routeType: routeType, + payloadType: (header >> _phTypeShift) & _phTypeMask, + payloadVer: (header >> _phVerShift) & _phVerMask, + pathLenRaw: pathLenRaw, + pathBytes: pathBytes, + payload: payload, + ); + } catch (e) { + appLogger.warn('Error parsing raw packet: $e'); + return null; + } } int _computeChannelHash(Uint8List psk) { @@ -4767,7 +4761,7 @@ class MeshCoreConnector extends ChangeNotifier { void _handleCustomVars(Uint8List frame) { final buf = BufferReader(frame.sublist(1)); try { - _currentCustomVars = _parseKeyValueString(buf.readString()); + _currentCustomVars = _parseKeyValueString(buf.readCString()); } catch (e) { appLogger.warn('Malformed custom vars frame: $e', tag: 'Connector'); } @@ -4924,7 +4918,7 @@ class MeshCoreConnector extends ChangeNotifier { longitude = packet.readInt32LE() / 1e6; } if (hasName && packet.remaining > 0) { - name = packet.readString(); + name = packet.readCString(); } } catch (e) { appLogger.warn('Malformed advert frame: $e', tag: 'Connector'); @@ -4986,7 +4980,7 @@ class MeshCoreConnector extends ChangeNotifier { longitude = advert.readInt32LE() / 1e6; } if (hasName && advert.remaining > 0) { - name = advert.readString(); + name = advert.readCString(); } } catch (e) { appLogger.warn('Malformed advert frame: $e', tag: 'Connector'); diff --git a/lib/connector/meshcore_protocol.dart b/lib/connector/meshcore_protocol.dart index a2e20cd..b368756 100644 --- a/lib/connector/meshcore_protocol.dart +++ b/lib/connector/meshcore_protocol.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:flutter/widgets.dart'; + // Buffer Reader - sequential binary data reader with pointer tracking class BufferReader { int _pointer = 0; @@ -37,16 +39,6 @@ class BufferReader { Uint8List readRemainingBytes() => readBytes(remaining); - String readString() { - _lastPointer = _pointer; - final value = readRemainingBytes(); - try { - return utf8.decode(Uint8List.fromList(value), allowMalformed: true); - } catch (e) { - return String.fromCharCodes(value); // Latin-1 fallback - } - } - String readCStringGreedy(int maxLength) { _lastPointer = _pointer; final value = []; @@ -62,11 +54,12 @@ class BufferReader { } } - String readCString(int maxLength) { + String readCString({int maxLength = -1}) { final backupPointer = _pointer; final value = []; int counter = 0; - while (counter < maxLength) { + final maxLen = maxLength >= 0 ? maxLength : remaining; + while (counter < maxLen) { final byte = readByte(); if (byte == 0) break; value.add(byte); @@ -409,48 +402,40 @@ ParsedContactText? parseContactMessageText(Uint8List frame) { // Signed messages have a 4-byte signature after the timestamp, before the text message.skipBytes(4); } - final text = message.readString(); + final text = message.readCString(); if (text.isEmpty) return null; return ParsedContactText(senderPrefix: senderPrefix, text: text); } catch (e) { + debugPrint('Error parsing contact message text: $e'); return null; } } -// // Helper to read uint32 little-endian -// int readUint32LE(Uint8List data, int offset) { -// return data[offset] | -// (data[offset + 1] << 8) | -// (data[offset + 2] << 16) | -// (data[offset + 3] << 24); -// } +// Helper to read uint32 little-endian +int readUint32LE(Uint8List data, int offset) { + return data[offset] | + (data[offset + 1] << 8) | + (data[offset + 2] << 16) | + (data[offset + 3] << 24); +} -// // Helper to read uint16 little-endian -// int readUint16LE(Uint8List data, int offset) { -// return data[offset] | (data[offset + 1] << 8); -// } +// Helper to read uint16 little-endian +int readUint16LE(Uint8List data, int offset) { + return data[offset] | (data[offset + 1] << 8); +} -// // Helper to read int32 little-endian -// int readInt32LE(Uint8List data, int offset) { -// int val = readUint32LE(data, offset); -// if (val >= 0x80000000) val -= 0x100000000; -// return val; -// } +// Helper to read int32 little-endian +int readInt32LE(Uint8List data, int offset) { + int val = readUint32LE(data, offset); + if (val >= 0x80000000) val -= 0x100000000; + return val; +} -// // Helper to read null-terminated UTF-8 string -// String readCString(Uint8List data, int offset, int maxLen) { -// int end = offset; -// while (end < offset + maxLen && end < data.length && data[end] != 0) { -// end++; -// } -// try { -// return utf8.decode(data.sublist(offset, end), allowMalformed: true); -// } catch (e) { -// // Fallback to Latin-1 if UTF-8 decoding fails -// return String.fromCharCodes(data.sublist(offset, end)); -// } -// } +// Helper to convert uint32 to hex string +String ackHashToHex(int ackHash) { + return ackHash.toRadixString(16).padLeft(8, '0'); +} // Helper to convert public key to hex string String pubKeyToHex(Uint8List pubKey) { diff --git a/lib/models/channel_message.dart b/lib/models/channel_message.dart index ab81630..98a8f1d 100644 --- a/lib/models/channel_message.dart +++ b/lib/models/channel_message.dart @@ -2,6 +2,7 @@ import 'dart:typed_data'; import '../connector/meshcore_protocol.dart'; import '../helpers/reaction_helper.dart'; import '../helpers/smaz.dart'; +import '../utils/app_logger.dart'; enum ChannelMessageStatus { pending, sent, failed } @@ -114,8 +115,8 @@ class ChannelMessage { // V3: [0]=code [1]=SNR [2]=rsv1 [3]=rsv2 [4]=channel_idx [5]=path_len [path... optional] [txt_type] [timestamp x4] [text...] // Non-V3: [0]=code [1]=channel_idx [2]=path_len [3]=txt_type [4-7]=timestamp [8+]=text if (frame.length < 8) return null; - final reader = BufferReader(frame); try { + final reader = BufferReader(frame); final code = reader.readByte(); if (code != respCodeChannelMsgRecv && code != respCodeChannelMsgRecvV3) { return null; @@ -133,11 +134,9 @@ class ChannelMessage { pathLen = reader.readByte(); txtType = reader.readByte(); final hasPath = (flags & 0x01) != 0; - if (hasPath) { + if (hasPath && pathLen > 0) { reader.rewind(); // Rewind to read path length again for pathBytes pathBytes = reader.readBytes(pathLen); - // Force text type to plain if path is present - txtType = txtTypePlain; } else { pathLen = 0; } @@ -152,7 +151,7 @@ class ChannelMessage { return null; } - final text = reader.readString(); + final text = reader.readCString(); // Extract sender name and actual message from "name: msg" format String senderName = 'Unknown'; @@ -185,6 +184,7 @@ class ChannelMessage { channelIndex: channelIdx, ); } catch (e) { + appLogger.error('Error parsing channel message frame: $e'); // If parsing fails, return null to avoid crashes return null; } diff --git a/lib/models/message.dart b/lib/models/message.dart index d1660dd..6b930c0 100644 --- a/lib/models/message.dart +++ b/lib/models/message.dart @@ -105,7 +105,7 @@ class Message { if ((flags >> 2) != txtTypePlain) { return null; } - final text = reader.readString(); + final text = reader.readCString(); return Message( senderKey: senderKey, diff --git a/lib/services/ble_debug_log_service.dart b/lib/services/ble_debug_log_service.dart index 745b243..df2822b 100644 --- a/lib/services/ble_debug_log_service.dart +++ b/lib/services/ble_debug_log_service.dart @@ -245,19 +245,6 @@ class BleDebugLogService extends ChangeNotifier { } } - // Helper to read uint32 little-endian - int readUint32LE(Uint8List data, int offset) { - return data[offset] | - (data[offset + 1] << 8) | - (data[offset + 2] << 16) | - (data[offset + 3] << 24); - } - - // // Helper to read uint16 little-endian - int readUint16LE(Uint8List data, int offset) { - return data[offset] | (data[offset + 1] << 8); - } - String _frameDetail(int code, Uint8List frame) { switch (code) { case respCodeSent: diff --git a/lib/services/message_retry_service.dart b/lib/services/message_retry_service.dart index 1920418..94f3caf 100644 --- a/lib/services/message_retry_service.dart +++ b/lib/services/message_retry_service.dart @@ -98,7 +98,7 @@ class MessageRetryService extends ChangeNotifier { /// Compute expected ACK hash using same algorithm as firmware: /// SHA256([timestamp(4)][attempt(1)][text][sender_pubkey(32)]) -> first 4 bytes - static Uint8List computeExpectedAckHash( + static int computeExpectedAckHash( int timestampSeconds, int attempt, String text, @@ -126,7 +126,8 @@ class MessageRetryService extends ChangeNotifier { // Compute SHA256 and return first 4 bytes final hash = sha256.convert(buffer); - return Uint8List.fromList(hash.bytes.sublist(0, 4)); + final bytes = Uint8List.fromList(hash.bytes.sublist(0, 4)); + return (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; } Future sendMessageWithRetry({ @@ -324,9 +325,7 @@ class MessageRetryService extends ChangeNotifier { outboundText, selfPubKey, ); - final expectedHashHex = expectedHash - .map((b) => b.toRadixString(16).padLeft(2, '0')) - .join(); + final expectedHashHex = expectedHash.toRadixString(16).padLeft(8, '0'); _expectedHashToMessageId[expectedHashHex] = messageId; final shortText = message.text.length > 20 diff --git a/lib/widgets/debug_frame_viewer.dart b/lib/widgets/debug_frame_viewer.dart index 05e312b..c8dc371 100644 --- a/lib/widgets/debug_frame_viewer.dart +++ b/lib/widgets/debug_frame_viewer.dart @@ -10,14 +10,6 @@ class DebugFrameViewer { Uint8List frame, String title, ) { - // Helper to read uint32 little-endian - int readUint32LE(Uint8List data, int offset) { - return data[offset] | - (data[offset + 1] << 8) | - (data[offset + 2] << 16) | - (data[offset + 3] << 24); - } - final hexString = frame .map((b) => b.toRadixString(16).padLeft(2, '0')) .join(' '); diff --git a/test/services/retry_and_protocol_test.dart b/test/services/retry_and_protocol_test.dart index b58da45..48d4cfb 100644 --- a/test/services/retry_and_protocol_test.dart +++ b/test/services/retry_and_protocol_test.dart @@ -169,16 +169,6 @@ void main() { expect(first, equals(second)); }); - test('hash is exactly 4 bytes long', () { - final hash = MessageRetryService.computeExpectedAckHash( - fixedTs, - 0, - fixedText, - fixedKey, - ); - expect(hash.length, equals(4)); - }); - test('hash matches manual SHA-256 computation', () { for (int attempt = 0; attempt < 4; attempt++) { final actual = MessageRetryService.computeExpectedAckHash( @@ -509,7 +499,7 @@ void main() { fixedText, fixedKey, ); - final hex = hash.map((b) => b.toRadixString(16).padLeft(2, '0')).join(); + final hex = hash.toRadixString(16).padLeft(8, '0'); expect( hashes.containsKey(hex), isFalse, From 9a75c912af9cad4f85791b8ca115f8ed7598b7e6 Mon Sep 17 00:00:00 2001 From: Sebastian Szymbor Date: Mon, 12 Jan 2026 16:29:23 +0100 Subject: [PATCH 09/25] Update Polish localization strings for clarity --- lib/l10n/app_localizations_pl.dart | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 176c17e..f55c29f 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -1622,20 +1622,10 @@ class AppLocalizationsPl extends AppLocalizations { String get map_showSharedMarkers => 'Pokaż współdzielone znaki.'; @override - String get map_showGuessedLocations => - 'Wyświetl lokalizacje zgadanych węzłów'; + String get map_lastSeenTime => 'Ostatni raz widziany'; @override - String get map_showDiscoveryContacts => 'Pokaż kontakty odkrywania'; - - @override - String get map_guessedLocation => 'Wydana lokalizacja'; - - @override - String get map_lastSeenTime => 'Ostatni raz widiany'; - - @override - String get map_sharedPin => 'Podzielony PIN'; + String get map_sharedPin => 'Współdzielony PIN'; @override String get map_joinRoom => 'Dołącz do pokoju'; @@ -1722,7 +1712,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get mapCache_downloadTilesButton => 'Pobierz Paski'; + String get mapCache_downloadTilesButton => 'Pobierz kafelki'; @override String get mapCache_clearCacheButton => 'Wyczyść pamięć podręczną'; @@ -2177,7 +2167,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_encryptedAdvertInterval => - 'Zaszyfrowany Interwał Reklamowy'; + 'Interwał Zaszyfrowanego Rozgłoszenia'; @override String get repeater_dangerZone => 'Strefa Zagrożeń'; @@ -2191,7 +2181,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_rebootRepeaterConfirm => - 'Czy na pewno chcesz zrestartować ten repeater?'; + 'Czy na pewno chcesz zrestartować ten powtarzacz?'; @override String get repeater_regenerateIdentityKey => 'Wygeneruj klucz tożsamości'; @@ -2202,7 +2192,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_regenerateIdentityKeyConfirm => - 'To zostanie wygenerowane nowe tożsamość dla powtarzacza. Kontynuować?'; + 'Zostanie wygenerowana nowa tożsamość dla powtarzacza. Kontynuować?'; @override String get repeater_eraseFileSystem => 'Wyczyść System Plików'; @@ -2337,7 +2327,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_cliQuickClock => 'Godzina'; @override - String get repeater_cliHelpAdvert => 'Wysyła pakiet reklamowy'; + String get repeater_cliHelpAdvert => 'Wysyła pakiet rozgłoszeniowy'; @override String get repeater_cliHelpReboot => @@ -2392,11 +2382,11 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpSetAdvertInterval => - 'Ustawia interwał timera w minutach do wysyłania pakietu reklamy lokalnej (bezpośredniej). Ustaw na 0, aby wyłączyć.'; + 'Ustawia interwał timera w minutach do wysyłania pakietu rozgłoszenia lokalnego (bezpośredniego). Ustaw na 0, aby wyłączyć.'; @override String get repeater_cliHelpSetFloodAdvertInterval => - 'Ustawia interwał timera w godzinach do wysłania pakietu reklamowego typu \"powiew\". Ustaw na 0, aby wyłączyć.'; + 'Ustawia interwał timera w godzinach do wysłania pakietu rozgłoszeniowego typu \"flood\". Ustaw na 0, aby wyłączyć.'; @override String get repeater_cliHelpSetGuestPassword => @@ -2478,7 +2468,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpNeighbors => - 'Wyświetla listę innych węzłów powtarzających się, które usłyszano dzięki reklamom zero-hop. Każda linia to: id-prefix-hex:timestamp:snr-times-4'; + 'Wyświetla listę innych węzłów powtarzających się, które usłyszano dzięki rozgłszeniom zero-hop. Każda linia to: id-prefix-hex:timestamp:snr-times-4'; @override String get repeater_cliHelpNeighborRemove => From 7c16dde989230442f4ec0aa62de871fdfa296497 Mon Sep 17 00:00:00 2001 From: thesebas Date: Wed, 18 Feb 2026 19:03:17 +0100 Subject: [PATCH 10/25] Update Polish localization strings --- lib/l10n/app_localizations_pl.dart | 2 +- lib/l10n/app_pl.arb | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index f55c29f..d838732 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -2468,7 +2468,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpNeighbors => - 'Wyświetla listę innych węzłów powtarzających się, które usłyszano dzięki rozgłszeniom zero-hop. Każda linia to: id-prefix-hex:timestamp:snr-times-4'; + 'Wyświetla listę innych węzłów powtarzających się, które usłyszano dzięki rozgłoszeniom zero-hop. Każda linia to: id-prefix-hex:timestamp:snr-times-4'; @override String get repeater_cliHelpNeighborRemove => diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index c6e3fc4..ea8fbc0 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -680,8 +680,8 @@ "map_publicKeyPrefix": "Przewód klucza publicznego", "map_markers": "Oznaczarki", "map_showSharedMarkers": "Pokaż współdzielone znaki.", - "map_lastSeenTime": "Ostatni raz widiany", - "map_sharedPin": "Podzielony PIN", + "map_lastSeenTime": "Ostatni raz widziany", + "map_sharedPin": "Współdzielony PIN", "map_joinRoom": "Dołącz do pokoju", "map_manageRepeater": "Zarządzaj Powtórzami", "mapCache_title": "Bufor Map Offline", @@ -742,7 +742,7 @@ } } }, - "mapCache_downloadTilesButton": "Pobierz Paski", + "mapCache_downloadTilesButton": "Pobierz kafelki", "mapCache_clearCacheButton": "Wyczyść pamięć podręczną", "mapCache_failedDownloads": "Nieudane pobrania: {count}", "@mapCache_failedDownloads": { @@ -1029,14 +1029,14 @@ } } }, - "repeater_encryptedAdvertInterval": "Zaszyfrowany Interwał Reklamowy", + "repeater_encryptedAdvertInterval": "Interwał Zaszyfrowanego Rozgłoszenia", "repeater_dangerZone": "Strefa Zagrożeń", "repeater_rebootRepeater": "Zrestartuj Powtarzacz", "repeater_rebootRepeaterSubtitle": "Zrestartuj urządzenie powtarzające.", - "repeater_rebootRepeaterConfirm": "Czy na pewno chcesz zrestartować ten repeater?", + "repeater_rebootRepeaterConfirm": "Czy na pewno chcesz zrestartować ten powtarzacz?", "repeater_regenerateIdentityKey": "Wygeneruj klucz tożsamości", "repeater_regenerateIdentityKeySubtitle": "Wygeneruj nową parę kluczy publicznych/prywatnych", - "repeater_regenerateIdentityKeyConfirm": "To zostanie wygenerowane nowe tożsamość dla powtarzacza. Kontynuować?", + "repeater_regenerateIdentityKeyConfirm": "Zostanie wygenerowana nowa tożsamość dla powtarzacza. Kontynuować?", "repeater_eraseFileSystem": "Wyczyść System Plików", "repeater_eraseFileSystemSubtitle": "Sformatuj system plików powielacza", "repeater_eraseFileSystemConfirm": "OSTRZEŻENIE: To spowoduje usunięcie wszystkich danych z powtarzacza. Nie da się tego cofnąć!", @@ -1117,7 +1117,7 @@ "repeater_cliQuickVersion": "Wersja", "repeater_cliQuickAdvertise": "Reklama", "repeater_cliQuickClock": "Godzina", - "repeater_cliHelpAdvert": "Wysyła pakiet reklamowy", + "repeater_cliHelpAdvert": "Wysyła pakiet rozgłoszeniowy", "repeater_cliHelpReboot": "Zresetuj urządzenie. (Uwaga, może pojawić się 'Timeout', co jest normalne)", "repeater_cliHelpClock": "Wyświetla aktualny czas zgodnie z zegarem urządzenia.", "repeater_cliHelpPassword": "Ustawia nowe hasło administratora dla urządzenia.", @@ -1131,8 +1131,8 @@ "repeater_cliHelpSetIntThresh": "Ustawia Próg Interferencji (w dB). Domyślnie wynosi 14. Ustaw na 0, aby wyłączyć wykrywanie zakłóceń kanału.", "repeater_cliHelpSetAgcResetInterval": "Ustawia interwał do zresetowania Automatycznego Sterownika Głośności. Ustaw na 0, aby wyłączyć.", "repeater_cliHelpSetMultiAcks": "Włącza lub wyłącza funkcję 'podwójnych potwierdzeń'.", - "repeater_cliHelpSetAdvertInterval": "Ustawia interwał timera w minutach do wysyłania pakietu reklamy lokalnej (bezpośredniej). Ustaw na 0, aby wyłączyć.", - "repeater_cliHelpSetFloodAdvertInterval": "Ustawia interwał timera w godzinach do wysłania pakietu reklamowego typu \"powiew\". Ustaw na 0, aby wyłączyć.", + "repeater_cliHelpSetAdvertInterval": "Ustawia interwał timera w minutach do wysyłania pakietu rozgłoszenia lokalnego (bezpośredniego). Ustaw na 0, aby wyłączyć.", + "repeater_cliHelpSetFloodAdvertInterval": "Ustawia interwał timera w godzinach do wysłania pakietu rozgłoszeniowego typu \"flood\". Ustaw na 0, aby wyłączyć.", "repeater_cliHelpSetGuestPassword": "Ustawia/aktualizuje hasło gościa. (dla repeaterów, loginy gości mogą wysyłać żądanie \"Get Stats\")", "repeater_cliHelpSetName": "Ustawia nazwę reklamy.", "repeater_cliHelpSetLat": "Ustawia współrzędną geograficzne (w stopniach dziesiętnych) mapy reklam.", @@ -1153,7 +1153,7 @@ "repeater_cliHelpLogStart": "Rozpoczyna się logowanie pakietów do systemu plików.", "repeater_cliHelpLogStop": "Zatrzymuje logowanie pakietów do systemu plików.", "repeater_cliHelpLogErase": "Usuwa logi pakietów z systemu plików.", - "repeater_cliHelpNeighbors": "Wyświetla listę innych węzłów powtarzających się, które usłyszano dzięki reklamom zero-hop. Każda linia to: id-prefix-hex:timestamp:snr-times-4", + "repeater_cliHelpNeighbors": "Wyświetla listę innych węzłów powtarzających się, które usłyszano dzięki rozgłoszeniom zero-hop. Każda linia to: id-prefix-hex:timestamp:snr-times-4", "repeater_cliHelpNeighborRemove": "Usuwa pierwszy pasujący wpis (z prefiksem pubkey (hex)) z listy sąsiadów.", "repeater_cliHelpRegion": "(tylko seria) Wyświetla wszystkie zdefiniowane regiony i aktualne uprawnienia do powodzi.", "repeater_cliHelpRegionLoad": "ZAPOMNIJ: to jest specjalne wywołanie wielokomendowe. Każda następna komenda jest nazwą regionu (wcięta spacjami, aby wskazywać hierarchię nadrzędną, z minimum jedną spacją). Zakończona wysłaniem pustej linii/komendy.", From 5321974cbbf427e535cbe871b9cd7cc3ad4a5071 Mon Sep 17 00:00:00 2001 From: thesebas Date: Wed, 18 Feb 2026 19:10:07 +0100 Subject: [PATCH 11/25] Update Polish localization strings for consistency and clarity --- lib/l10n/app_localizations_pl.dart | 54 +++++++++++++++--------------- lib/l10n/app_pl.arb | 54 +++++++++++++++--------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index d838732..9aa74b8 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -389,11 +389,11 @@ class AppLocalizationsPl extends AppLocalizations { @override String get settings_privacyModeSubtitle => - 'Ukryj imię/lokalizację w reklamach'; + 'Ukryj imię/lokalizację w rozgłoszeniach'; @override String get settings_privacyModeToggle => - 'Włącz tryb prywatności, aby ukryć swoje imię i lokalizację w reklamach.'; + 'Włącz tryb prywatności, aby ukryć swoje imię i lokalizację w rozgłoszeniach.'; @override String get settings_privacyModeEnabled => 'Tryb prywatności włączony'; @@ -405,14 +405,14 @@ class AppLocalizationsPl extends AppLocalizations { String get settings_actions => 'Działania'; @override - String get settings_sendAdvertisement => 'Wyślij Reklamę'; + String get settings_sendAdvertisement => 'Wyślij rozgłoszenie'; @override String get settings_sendAdvertisementSubtitle => 'Obecność transmisji jest teraz'; @override - String get settings_advertisementSent => 'Reklama wysłana'; + String get settings_advertisementSent => 'Rozgłoszenie wysłane'; @override String get settings_syncTime => 'Czas synchronizacji'; @@ -628,7 +628,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get appSettings_enableNotificationsSubtitle => - 'Otrzymuj powiadomienia o wiadomościach i reklamach.'; + 'Otrzymuj powiadomienia o wiadomościach i rozgłoszeniach.'; @override String get appSettings_notificationPermissionDenied => @@ -658,11 +658,11 @@ class AppLocalizationsPl extends AppLocalizations { @override String get appSettings_advertisementNotifications => - 'Powiadomienia Reklamowe'; + 'Powiadomienia o rozgłoszeniach'; @override String get appSettings_advertisementNotificationsSubtitle => - 'Wyświetl powiadomienie, gdy zostaną odkryte nowe węzły.'; + 'Wyświetl powiadomienie, gdy zostaną wykryte nowe węzły.'; @override String get appSettings_messaging => 'Wiadomości'; @@ -867,7 +867,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get contacts_contactsWillAppear => - 'Kontakty będą wyświetlane, gdy urządzenia reklamują się.'; + 'Kontakty będą wyświetlane, gdy urządzenia nadają rozgłoszenia.'; @override String get contacts_unread => 'Nieprzeczytane'; @@ -1354,10 +1354,10 @@ class AppLocalizationsPl extends AppLocalizations { 'Historia ścieżek jest pełna. Usuń wpisy, aby dodać nowe.'; @override - String get chat_hopSingular => 'Skacz'; + String get chat_hopSingular => 'skok'; @override - String get chat_hopPlural => 'skoczkowie'; + String get chat_hopPlural => 'skoki'; @override String chat_hopsCount(int count) { @@ -1450,7 +1450,7 @@ class AppLocalizationsPl extends AppLocalizations { String get chat_compressOutgoingMessages => 'Kompresuj wychodzące wiadomości'; @override - String get chat_floodForced => 'Powodowana Powódź'; + String get chat_floodForced => 'Zalew (wymuszony)'; @override String get chat_directForced => 'Bezpośrednio (wymuszono)'; @@ -1461,7 +1461,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get chat_floodAuto => 'Powodzie (automatyczne)'; + String get chat_floodAuto => 'Zalew (automatyczny)'; @override String get chat_direct => 'Bezpośrednio'; @@ -2012,10 +2012,10 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_noiseFloor => 'Poziom Szumów'; @override - String get repeater_txAirtime => 'TX Airtime'; + String get repeater_txAirtime => 'Czas nadawania TX'; @override - String get repeater_rxAirtime => 'RX Airtime'; + String get repeater_rxAirtime => 'Czas odbioru RX'; @override String get repeater_packetStatistics => 'Statystyki pakietów'; @@ -2093,7 +2093,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_frequencyHelper => '300-2500 MHz'; @override - String get repeater_txPower => 'TX Power'; + String get repeater_txPower => 'Moc TX'; @override String get repeater_txPowerHelper => '1-30 dBm'; @@ -2102,7 +2102,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_bandwidth => 'Przepustowość'; @override - String get repeater_spreadingFactor => 'Rozkład Czynnika'; + String get repeater_spreadingFactor => 'Współczynnik rozpraszania'; @override String get repeater_codingRate => 'Stawka kodowania'; @@ -2144,13 +2144,13 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_privacyModeSubtitle => - 'Ukryj imię/lokalizację w reklamach'; + 'Ukryj imię/lokalizację w rozgłoszeniach'; @override - String get repeater_advertisementSettings => 'Ustawienia Reklam'; + String get repeater_advertisementSettings => 'Ustawienia rozgłoszeń'; @override - String get repeater_localAdvertInterval => 'Interwał Reklamy Lokalnej'; + String get repeater_localAdvertInterval => 'Interwał rozgłoszenia lokalnego'; @override String repeater_localAdvertIntervalMinutes(int minutes) { @@ -2158,7 +2158,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get repeater_floodAdvertInterval => 'Interwał Reklamy Powodziowej'; + String get repeater_floodAdvertInterval => 'Interwał rozgłoszenia zalewowego'; @override String repeater_floodAdvertIntervalHours(int hours) { @@ -2237,7 +2237,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_refreshRadioSettings => 'Odśwież Ustawienia Radio'; @override - String get repeater_refreshTxPower => 'Odśwież TX power'; + String get repeater_refreshTxPower => 'Odśwież moc TX'; @override String get repeater_refreshLocationSettings => @@ -2393,15 +2393,15 @@ class AppLocalizationsPl extends AppLocalizations { 'Ustawia/aktualizuje hasło gościa. (dla repeaterów, loginy gości mogą wysyłać żądanie \"Get Stats\")'; @override - String get repeater_cliHelpSetName => 'Ustawia nazwę reklamy.'; + String get repeater_cliHelpSetName => 'Ustawia nazwę rozgłoszenia.'; @override String get repeater_cliHelpSetLat => - 'Ustawia współrzędną geograficzne (w stopniach dziesiętnych) mapy reklam.'; + 'Ustawia współrzędną geograficzną (w stopniach dziesiętnych) mapy rozgłoszeń.'; @override String get repeater_cliHelpSetLon => - 'Ustawia współrzędną długościową mapy reklamy. (stopnie dziesiętne)'; + 'Ustawia współrzędną długościową mapy rozgłoszeń. (stopnie dziesiętne)'; @override String get repeater_cliHelpSetRadio => @@ -2530,11 +2530,11 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpGpsAdvert => - 'Udostępnia konfigurację reklamy lokalizacji węzła:\n- brak: nie uwzględniaj lokalizacji w reklamach\n- udostępnia: udostępnia lokalizację GPS (z SensorManager)\n- ustawienia: reklamuj lokalizację przechowywaną w ustawieniach'; + 'Udostępnia konfigurację rozgłoszeń lokalizacji węzła:\n- brak: nie uwzględniaj lokalizacji w rozgłoszeniach\n- udostępnia: udostępnia lokalizację GPS (z SensorManager)\n- ustawienia: rozgłaszaj lokalizację przechowywaną w ustawieniach'; @override String get repeater_cliHelpGpsAdvertSet => - 'Ustawia konfigurację reklamy w lokalizacji.'; + 'Ustawia konfigurację rozgłoszeń lokalizacji.'; @override String get repeater_commandsListTitle => 'Lista poleceń'; @@ -2714,7 +2714,7 @@ class AppLocalizationsPl extends AppLocalizations { String get channelPath_unknownPath => 'Nieznane'; @override - String get channelPath_floodPath => 'Powodzenie'; + String get channelPath_floodPath => 'Zalew'; @override String get channelPath_directPath => 'Bezpośrednio'; diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index ea8fbc0..8837732 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -99,14 +99,14 @@ "settings_latitude": "Szerokość", "settings_longitude": "Długość", "settings_privacyMode": "Tryb Prywatny", - "settings_privacyModeSubtitle": "Ukryj imię/lokalizację w reklamach", - "settings_privacyModeToggle": "Włącz tryb prywatności, aby ukryć swoje imię i lokalizację w reklamach.", + "settings_privacyModeSubtitle": "Ukryj imię/lokalizację w rozgłoszeniach", + "settings_privacyModeToggle": "Włącz tryb prywatności, aby ukryć swoje imię i lokalizację w rozgłoszeniach.", "settings_privacyModeEnabled": "Tryb prywatności włączony", "settings_privacyModeDisabled": "Tryb prywatności wyłączony", "settings_actions": "Działania", - "settings_sendAdvertisement": "Wyślij Reklamę", + "settings_sendAdvertisement": "Wyślij rozgłoszenie", "settings_sendAdvertisementSubtitle": "Obecność transmisji jest teraz", - "settings_advertisementSent": "Reklama wysłana", + "settings_advertisementSent": "Rozgłoszenie wysłane", "settings_syncTime": "Czas synchronizacji", "settings_syncTimeSubtitle": "Ustaw zegar urządzenia na czas telefonu.", "settings_timeSynchronized": "Synchronizacja czasu", @@ -179,7 +179,7 @@ "appSettings_languageBg": "Български", "appSettings_notifications": "Powiadomienia", "appSettings_enableNotifications": "Włącz Powiadomienia", - "appSettings_enableNotificationsSubtitle": "Otrzymuj powiadomienia o wiadomościach i reklamach.", + "appSettings_enableNotificationsSubtitle": "Otrzymuj powiadomienia o wiadomościach i rozgłoszeniach.", "appSettings_notificationPermissionDenied": "Odmowa zezwolenia na powiadomienia", "appSettings_notificationsEnabled": "Powiadomienia włączone", "appSettings_notificationsDisabled": "Powiadomienia wyłączone", @@ -187,8 +187,8 @@ "appSettings_messageNotificationsSubtitle": "Pokaż powiadomienie przy otrzymywaniu nowych wiadomości", "appSettings_channelMessageNotifications": "Powiadomienia o Wiadomościach na Kanałach", "appSettings_channelMessageNotificationsSubtitle": "Pokaż powiadomienie przy odbieraniu wiadomości z kanału", - "appSettings_advertisementNotifications": "Powiadomienia Reklamowe", - "appSettings_advertisementNotificationsSubtitle": "Wyświetl powiadomienie, gdy zostaną odkryte nowe węzły.", + "appSettings_advertisementNotifications": "Powiadomienia o rozgłoszeniach", + "appSettings_advertisementNotificationsSubtitle": "Wyświetl powiadomienie, gdy zostaną wykryte nowe węzły.", "appSettings_messaging": "Wiadomości", "appSettings_clearPathOnMaxRetry": "Wyczyść Ścieżkę na Maksymalnej Próbie", "appSettings_clearPathOnMaxRetrySubtitle": "Resetuj ścieżkę kontaktu po 5 nieudanych próbach wysłania", @@ -256,7 +256,7 @@ "appSettings_appDebugLoggingDisabled": "Zasubskrybowane logi debugowania aplikacji wyłączone.", "contacts_title": "Kontakty", "contacts_noContacts": "Brak jeszcze kontaktów.", - "contacts_contactsWillAppear": "Kontakty będą wyświetlane, gdy urządzenia reklamują się.", + "contacts_contactsWillAppear": "Kontakty będą wyświetlane, gdy urządzenia nadają rozgłoszenia.", "contacts_searchContacts": "Wyszukaj kontakty...", "contacts_noUnreadContacts": "Brak nieprzeczytanych kontaktów", "contacts_noContactsFound": "Brak znalezionych kontaktów ani grup.", @@ -548,8 +548,8 @@ "chat_forceFloodMode": "Wymusz Tryb Powodowany", "chat_recentAckPaths": "Ostatnie ścieżki ACK (naciśnij, aby użyć):", "chat_pathHistoryFull": "Historia ścieżek jest pełna. Usuń wpisy, aby dodać nowe.", - "chat_hopSingular": "Skacz", - "chat_hopPlural": "skoczkowie", + "chat_hopSingular": "skok", + "chat_hopPlural": "skoki", "chat_hopsCount": "{count} {count, plural, =1{hop} other{hops}}", "@chat_hopsCount": { "placeholders": { @@ -589,7 +589,7 @@ "chat_path": "Ścieżka", "chat_publicKey": "Klucz Publiczny", "chat_compressOutgoingMessages": "Kompresuj wychodzące wiadomości", - "chat_floodForced": "Powodowana Powódź", + "chat_floodForced": "Zalew (wymuszony)", "chat_directForced": "Bezpośrednio (wymuszono)", "chat_hopsForced": "{count} skoków (wymuszonych)", "@chat_hopsForced": { @@ -599,7 +599,7 @@ } } }, - "chat_floodAuto": "Powodzie (automatyczne)", + "chat_floodAuto": "Zalew (automatyczny)", "chat_direct": "Bezpośrednio", "chat_poiShared": "Wspólny POI", "chat_unread": "Niezgłoszone: {count}", @@ -912,8 +912,8 @@ "repeater_lastRssi": "Ostatni RSSI", "repeater_lastSnr": "Ostatnie SNR", "repeater_noiseFloor": "Poziom Szumów", - "repeater_txAirtime": "TX Airtime", - "repeater_rxAirtime": "RX Airtime", + "repeater_txAirtime": "Czas nadawania TX", + "repeater_rxAirtime": "Czas odbioru RX", "repeater_packetStatistics": "Statystyki pakietów", "repeater_sent": "Wysłane", "repeater_received": "Otrzymano", @@ -993,10 +993,10 @@ "repeater_radioSettings": "Ustawienia radia", "repeater_frequencyMhz": "Częstotliwość (MHz)", "repeater_frequencyHelper": "300-2500 MHz", - "repeater_txPower": "TX Power", + "repeater_txPower": "Moc TX", "repeater_txPowerHelper": "1-30 dBm", "repeater_bandwidth": "Przepustowość", - "repeater_spreadingFactor": "Rozkład Czynnika", + "repeater_spreadingFactor": "Współczynnik rozpraszania", "repeater_codingRate": "Stawka kodowania", "repeater_locationSettings": "Ustawienia Lokalizacji", "repeater_latitude": "Szerokość", @@ -1009,9 +1009,9 @@ "repeater_guestAccess": "Dostęp dla gości", "repeater_guestAccessSubtitle": "Umożliw dostęp tylko do odczytu dla gości.", "repeater_privacyMode": "Tryb Prywatności", - "repeater_privacyModeSubtitle": "Ukryj imię/lokalizację w reklamach", - "repeater_advertisementSettings": "Ustawienia Reklam", - "repeater_localAdvertInterval": "Interwał Reklamy Lokalnej", + "repeater_privacyModeSubtitle": "Ukryj imię/lokalizację w rozgłoszeniach", + "repeater_advertisementSettings": "Ustawienia rozgłoszeń", + "repeater_localAdvertInterval": "Interwał rozgłoszenia lokalnego", "repeater_localAdvertIntervalMinutes": "{minutes} minut", "@repeater_localAdvertIntervalMinutes": { "placeholders": { @@ -1020,7 +1020,7 @@ } } }, - "repeater_floodAdvertInterval": "Interwał Reklamy Powodziowej", + "repeater_floodAdvertInterval": "Interwał rozgłoszenia zalewowego", "repeater_floodAdvertIntervalHours": "{hours} godzin", "@repeater_floodAdvertIntervalHours": { "placeholders": { @@ -1069,7 +1069,7 @@ }, "repeater_refreshBasicSettings": "Odśwież Podstawowe Ustawienia", "repeater_refreshRadioSettings": "Odśwież Ustawienia Radio", - "repeater_refreshTxPower": "Odśwież TX power", + "repeater_refreshTxPower": "Odśwież moc TX", "repeater_refreshLocationSettings": "Odśwież Ustawienia Lokalizacji", "repeater_refreshPacketForwarding": "Odśwież trasowanie pakietów", "repeater_refreshGuestAccess": "Odśwież dostęp gościa", @@ -1134,9 +1134,9 @@ "repeater_cliHelpSetAdvertInterval": "Ustawia interwał timera w minutach do wysyłania pakietu rozgłoszenia lokalnego (bezpośredniego). Ustaw na 0, aby wyłączyć.", "repeater_cliHelpSetFloodAdvertInterval": "Ustawia interwał timera w godzinach do wysłania pakietu rozgłoszeniowego typu \"flood\". Ustaw na 0, aby wyłączyć.", "repeater_cliHelpSetGuestPassword": "Ustawia/aktualizuje hasło gościa. (dla repeaterów, loginy gości mogą wysyłać żądanie \"Get Stats\")", - "repeater_cliHelpSetName": "Ustawia nazwę reklamy.", - "repeater_cliHelpSetLat": "Ustawia współrzędną geograficzne (w stopniach dziesiętnych) mapy reklam.", - "repeater_cliHelpSetLon": "Ustawia współrzędną długościową mapy reklamy. (stopnie dziesiętne)", + "repeater_cliHelpSetName": "Ustawia nazwę rozgłoszenia.", + "repeater_cliHelpSetLat": "Ustawia współrzędną geograficzną (w stopniach dziesiętnych) mapy rozgłoszeń.", + "repeater_cliHelpSetLon": "Ustawia współrzędną długościową mapy rozgłoszeń. (stopnie dziesiętne)", "repeater_cliHelpSetRadio": "Ustawia nowe parametry radia i zapisuje je w preferencjach. Wymaga polecenia \"reboot\" do zastosowania.", "repeater_cliHelpSetRxDelay": "Ustawienia (eksperymentalne) bazowe (muszą być > 1, aby działać) do stosowania lekkiego opóźnienia dla odebranych pakietów, w oparciu o siłę sygnału/wynik. Ustaw na 0, aby wyłączyć.", "repeater_cliHelpSetTxDelay": "Ustawia czynnik mnożony przez czas utrzymania w trybie zalewowym dla pakietu oraz z wykorzystaniem losowego systemu slotów, aby opóźnić jego przesyłanie (zmniejszając prawdopodobieństwo kolizji).", @@ -1169,8 +1169,8 @@ "repeater_cliHelpGpsOnOff": "Włącza/wyłącza nawigację GPS.", "repeater_cliHelpGpsSync": "Synchronizuje czas węzła z zegarem GPS.", "repeater_cliHelpGpsSetLoc": "Ustawia pozycję węzła na współrzędne GPS i zapisuje preferencje.", - "repeater_cliHelpGpsAdvert": "Udostępnia konfigurację reklamy lokalizacji węzła:\n- brak: nie uwzględniaj lokalizacji w reklamach\n- udostępnia: udostępnia lokalizację GPS (z SensorManager)\n- ustawienia: reklamuj lokalizację przechowywaną w ustawieniach", - "repeater_cliHelpGpsAdvertSet": "Ustawia konfigurację reklamy w lokalizacji.", + "repeater_cliHelpGpsAdvert": "Udostępnia konfigurację rozgłoszeń lokalizacji węzła:\n- brak: nie uwzględniaj lokalizacji w rozgłoszeniach\n- udostępnia: udostępnia lokalizację GPS (z SensorManager)\n- ustawienia: rozgłaszaj lokalizację przechowywaną w ustawieniach", + "repeater_cliHelpGpsAdvertSet": "Ustawia konfigurację rozgłoszeń lokalizacji.", "repeater_commandsListTitle": "Lista poleceń", "repeater_commandsListNote": "ZAPAMIĘTAJ: dla różnych poleceń \"set ...\" istnieje również polecenie \"get ...\".", "repeater_general": "Ogólne", @@ -1290,7 +1290,7 @@ } }, "channelPath_unknownPath": "Nieznane", - "channelPath_floodPath": "Powodzenie", + "channelPath_floodPath": "Zalew", "channelPath_directPath": "Bezpośrednio", "channelPath_observedZeroOf": "0 z {total} skoków", "@channelPath_observedZeroOf": { From 532401cc94379c301d82dce634ce585dd54bb7e6 Mon Sep 17 00:00:00 2001 From: thesebas Date: Wed, 18 Feb 2026 19:47:35 +0100 Subject: [PATCH 12/25] Refactor code structure for improved readability and maintainability --- lib/l10n/app_localizations_pl.dart | 256 +++++++++++++++-------------- lib/l10n/app_pl.arb | 245 +++++++++++++-------------- 2 files changed, 254 insertions(+), 247 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 9aa74b8..f2dcda2 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -42,7 +42,7 @@ class AppLocalizationsPl extends AppLocalizations { String get common_deleteAll => 'Usuń wszystko'; @override - String get common_close => 'Zamknąć'; + String get common_close => 'Zamknij'; @override String get common_edit => 'Edytuj'; @@ -75,7 +75,7 @@ class AppLocalizationsPl extends AppLocalizations { String get common_copy => 'Kopiuj'; @override - String get common_retry => 'Spróbować'; + String get common_retry => 'Ponów'; @override String get common_hide => 'Ukryj'; @@ -87,10 +87,10 @@ class AppLocalizationsPl extends AppLocalizations { String get common_enable => 'Włącz'; @override - String get common_disable => 'Wyłączyć'; + String get common_disable => 'Wyłącz'; @override - String get common_reboot => 'Zrestartować'; + String get common_reboot => 'Uruchom ponownie'; @override String get common_loading => 'Ładowanie...'; @@ -253,7 +253,7 @@ class AppLocalizationsPl extends AppLocalizations { String get scanner_disconnecting => 'Odłączanie...'; @override - String get scanner_notConnected => 'Niepołączony'; + String get scanner_notConnected => 'Nie połączono'; @override String scanner_connectedTo(String deviceName) { @@ -327,7 +327,7 @@ class AppLocalizationsPl extends AppLocalizations { String get settings_nodeNameHint => 'Wprowadź nazwę węzła'; @override - String get settings_nodeNameUpdated => 'Imię zaktualizowane'; + String get settings_nodeNameUpdated => 'Nazwa zaktualizowana'; @override String get settings_radioSettings => 'Ustawienia radia'; @@ -378,14 +378,14 @@ class AppLocalizationsPl extends AppLocalizations { String get settings_longitude => 'Długość'; @override - String get settings_contactSettings => 'Ustawienia kontaktowe'; + String get settings_contactSettings => 'Ustawienia kontaktów'; @override String get settings_contactSettingsSubtitle => 'Ustawienia dotyczące sposobu dodawania kontaktów'; @override - String get settings_privacyMode => 'Tryb Prywatny'; + String get settings_privacyMode => 'Tryb prywatności'; @override String get settings_privacyModeSubtitle => @@ -408,21 +408,20 @@ class AppLocalizationsPl extends AppLocalizations { String get settings_sendAdvertisement => 'Wyślij rozgłoszenie'; @override - String get settings_sendAdvertisementSubtitle => - 'Obecność transmisji jest teraz'; + String get settings_sendAdvertisementSubtitle => 'Nadaj obecność teraz'; @override String get settings_advertisementSent => 'Rozgłoszenie wysłane'; @override - String get settings_syncTime => 'Czas synchronizacji'; + String get settings_syncTime => 'Synchronizacja czasu'; @override String get settings_syncTimeSubtitle => 'Ustaw zegar urządzenia na czas telefonu.'; @override - String get settings_timeSynchronized => 'Synchronizacja czasu'; + String get settings_timeSynchronized => 'Czas zsynchronizowany'; @override String get settings_refreshContacts => 'Odśwież Kontakty'; @@ -445,20 +444,20 @@ class AppLocalizationsPl extends AppLocalizations { String get settings_debug => 'Debug'; @override - String get settings_bleDebugLog => 'Log błędów BLE'; + String get settings_bleDebugLog => 'Dziennik debugowania BLE'; @override String get settings_bleDebugLogSubtitle => 'Polecenia BLE, odpowiedzi i surowe dane'; @override - String get settings_appDebugLog => 'Log Wykonywania Aplikacji'; + String get settings_appDebugLog => 'Dziennik debugowania aplikacji'; @override String get settings_appDebugLogSubtitle => 'Komunikaty debugowania aplikacji'; @override - String get settings_about => 'O mnie'; + String get settings_about => 'O aplikacji'; @override String settings_aboutVersion(String version) { @@ -477,7 +476,7 @@ class AppLocalizationsPl extends AppLocalizations { 'Dane wysokościowe LOS: Open-Meteo (CC BY 4.0)'; @override - String get settings_infoName => 'Imię'; + String get settings_infoName => 'Nazwa'; @override String get settings_infoId => 'ID'; @@ -498,7 +497,7 @@ class AppLocalizationsPl extends AppLocalizations { String get settings_infoChannelCount => 'Liczba kanałów'; @override - String get settings_presets => 'Preset'; + String get settings_presets => 'Presety'; @override String get settings_frequency => 'Częstotliwość (MHz)'; @@ -514,13 +513,13 @@ class AppLocalizationsPl extends AppLocalizations { String get settings_bandwidth => 'Przepustowość'; @override - String get settings_spreadingFactor => 'Rozkład Czynnika'; + String get settings_spreadingFactor => 'Współczynnik rozpraszania'; @override - String get settings_codingRate => 'Stawka Kodowania'; + String get settings_codingRate => 'Współczynnik kodowania'; @override - String get settings_txPower => 'TX Moc (dBm)'; + String get settings_txPower => 'Moc TX (dBm)'; @override String get settings_txPowerHelper => '0 - 22'; @@ -566,7 +565,7 @@ class AppLocalizationsPl extends AppLocalizations { String get appSettings_language => 'Język'; @override - String get appSettings_languageSystem => 'Domyślny systemowy'; + String get appSettings_languageSystem => 'Domyślny systemu'; @override String get appSettings_languageEn => 'English'; @@ -669,7 +668,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get appSettings_clearPathOnMaxRetry => - 'Wyczyść Ścieżkę na Maksymalnej Próbie'; + 'Wyczyść ścieżkę po maks. liczbie prób'; @override String get appSettings_clearPathOnMaxRetrySubtitle => @@ -677,14 +676,14 @@ class AppLocalizationsPl extends AppLocalizations { @override String get appSettings_pathsWillBeCleared => - 'Droga będzie wyczyszczona po 5 nieudanych próbach.'; + 'Ścieżka zostanie wyczyszczona po 5 nieudanych próbach.'; @override String get appSettings_pathsWillNotBeCleared => - 'Droga nie zostanie automatycznie wyczyszczona.'; + 'Ścieżka nie zostanie automatycznie wyczyszczona.'; @override - String get appSettings_autoRouteRotation => 'Automatyczne Rotowanie Trasy'; + String get appSettings_autoRouteRotation => 'Automatyczna rotacja trasy'; @override String get appSettings_autoRouteRotationSubtitle => @@ -749,7 +748,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String appSettings_batteryChemistryPerDevice(String deviceName) { - return 'Ustawione na urządzenie ($deviceName)'; + return 'Ustaw dla urządzenia ($deviceName)'; } @override @@ -769,11 +768,11 @@ class AppLocalizationsPl extends AppLocalizations { String get appSettings_mapDisplay => 'Wyświetlanie mapy'; @override - String get appSettings_showRepeaters => 'Pokaż Powtórniki'; + String get appSettings_showRepeaters => 'Pokaż powtarzacze'; @override String get appSettings_showRepeatersSubtitle => - 'Wyświetl węzły powtarzające się na mapie'; + 'Wyświetl węzły powtarzaczy na mapie'; @override String get appSettings_showChatNodes => 'Pokaż Węzły Rozmowy'; @@ -807,7 +806,7 @@ class AppLocalizationsPl extends AppLocalizations { String get appSettings_showNodesDiscoveredWithin => 'Pokaż węzły odkryte w:'; @override - String get appSettings_allTime => 'Wszystko czasowo'; + String get appSettings_allTime => 'Cały czas'; @override String get appSettings_lastHour => 'Ostatnia godzina'; @@ -819,10 +818,10 @@ class AppLocalizationsPl extends AppLocalizations { String get appSettings_last24Hours => 'Ostatnie 24 godziny'; @override - String get appSettings_lastWeek => 'Tydzień temu'; + String get appSettings_lastWeek => 'Ostatni tydzień'; @override - String get appSettings_offlineMapCache => 'Bufor Map Offline'; + String get appSettings_offlineMapCache => 'Pamięć podręczna map offline'; @override String get appSettings_unitsTitle => 'Jednostki'; @@ -853,11 +852,11 @@ class AppLocalizationsPl extends AppLocalizations { @override String get appSettings_appDebugLoggingEnabled => - 'Zdebugowanie aplikacji włączone'; + 'Logowanie debugowania aplikacji włączone'; @override String get appSettings_appDebugLoggingDisabled => - 'Zasubskrybowane logi debugowania aplikacji wyłączone.'; + 'Logowanie debugowania aplikacji wyłączone.'; @override String get contacts_title => 'Kontakty'; @@ -916,7 +915,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get contacts_manageRepeater => 'Zarządzaj Powtórzami'; + String get contacts_manageRepeater => 'Zarządzaj powtarzaczem'; @override String get contacts_manageRoom => 'Zarządzaj Serwerem Pokoju'; @@ -966,11 +965,11 @@ class AppLocalizationsPl extends AppLocalizations { String get contacts_noMembers => 'Brak członków'; @override - String get contacts_lastSeenNow => 'Ostatnie połączenie'; + String get contacts_lastSeenNow => 'Widziano przed chwilą'; @override String contacts_lastSeenMinsAgo(int minutes) { - return 'Ostatnie połączenie $minutes min temu'; + return 'Widziano $minutes min temu'; } @override @@ -978,7 +977,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String contacts_lastSeenHoursAgo(int hours) { - return 'Ostatnie połączenie $hours godzin temu'; + return 'Widziano $hours godz. temu'; } @override @@ -986,7 +985,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String contacts_lastSeenDaysAgo(int days) { - return 'Ostatnie połączenie $days dni temu'; + return 'Widziano $days dni temu'; } @override @@ -1016,7 +1015,7 @@ class AppLocalizationsPl extends AppLocalizations { String get channels_public => 'Publiczny'; @override - String get channels_private => 'Prywatne'; + String get channels_private => 'Prywatny'; @override String get channels_publicChannel => 'Kanał publiczny'; @@ -1064,7 +1063,7 @@ class AppLocalizationsPl extends AppLocalizations { String get channels_usePublicChannel => 'Użyj kanału publicznego'; @override - String get channels_standardPublicPsk => 'Standard public PSK'; + String get channels_standardPublicPsk => 'Standardowy publiczny PSK'; @override String get channels_pskHex => 'PSK (Hex)'; @@ -1112,7 +1111,7 @@ class AppLocalizationsPl extends AppLocalizations { String get channels_sortLatestMessages => 'Najnowsze wiadomości'; @override - String get channels_sortUnread => 'Niezgłoszone'; + String get channels_sortUnread => 'Nieprzeczytane'; @override String get channels_createPrivateChannel => 'Utwórz Prywatny Kanał'; @@ -1171,7 +1170,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String chat_replyTo(String name) { - return 'Odpowiedz $name'; + return 'Odpowiedz do $name'; } @override @@ -1197,11 +1196,11 @@ class AppLocalizationsPl extends AppLocalizations { String get chat_messageDeleted => 'Wiadomość usunięta'; @override - String get chat_retryingMessage => 'Próba ponowienia'; + String get chat_retryingMessage => 'Ponawianie wiadomości'; @override String chat_retryCount(int current, int max) { - return 'Spróbuj $current/$max'; + return 'Próba $current/$max'; } @override @@ -1220,10 +1219,10 @@ class AppLocalizationsPl extends AppLocalizations { String get emojiCategorySmileys => 'Emoji'; @override - String get emojiCategoryGestures => 'Gestikulacje'; + String get emojiCategoryGestures => 'Gesty'; @override - String get emojiCategoryHearts => 'Serce'; + String get emojiCategoryHearts => 'Serca'; @override String get emojiCategoryObjects => 'Obiekty'; @@ -1275,10 +1274,10 @@ class AppLocalizationsPl extends AppLocalizations { 'Włącz logowanie debugowania aplikacji w ustawieniach'; @override - String get debugLog_frames => 'Ramy'; + String get debugLog_frames => 'Ramki'; @override - String get debugLog_rawLogRx => 'Surowe Log-RX'; + String get debugLog_rawLogRx => 'Surowy log RX'; @override String get debugLog_noBleActivity => 'Brak aktywności BLE jeszcze.'; @@ -1298,7 +1297,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String debugFrame_destinationPubKey(String pubKey) { - return '- Oznaczenie PubKey: $pubKey'; + return '- Docelowy klucz publiczny: $pubKey'; } @override @@ -1320,7 +1319,7 @@ class AppLocalizationsPl extends AppLocalizations { String get debugFrame_textTypeCli => 'CLI'; @override - String get debugFrame_textTypePlain => 'Proste'; + String get debugFrame_textTypePlain => 'Zwykły'; @override String debugFrame_text(String text) { @@ -1328,7 +1327,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get debugFrame_hexDump => 'Wyjście SzESZCZNULNE:'; + String get debugFrame_hexDump => 'Zrzut hex:'; @override String get chat_pathManagement => 'Zarządzanie ścieżkami'; @@ -1343,7 +1342,7 @@ class AppLocalizationsPl extends AppLocalizations { String get chat_autoUseSavedPath => 'Automatyczne (użyj zapisanej ścieżki)'; @override - String get chat_forceFloodMode => 'Wymusz Tryb Powodowany'; + String get chat_forceFloodMode => 'Wymuś tryb zalewowy'; @override String get chat_recentAckPaths => @@ -1364,8 +1363,10 @@ class AppLocalizationsPl extends AppLocalizations { String _temp0 = intl.Intl.pluralLogic( count, locale: localeName, - other: 'hops', - one: 'hop', + other: 'skoków', + many: 'skoków', + few: 'skoki', + one: 'skok', ); return '$count $_temp0'; } @@ -1384,7 +1385,7 @@ class AppLocalizationsPl extends AppLocalizations { String get chat_pathActions => 'Działania ścieżki:'; @override - String get chat_setCustomPath => 'Ustaw Ścieżkę Dostosowaną'; + String get chat_setCustomPath => 'Ustaw ścieżkę niestandardową'; @override String get chat_setCustomPathSubtitle => 'Ręcznie określ trasę.'; @@ -1394,11 +1395,11 @@ class AppLocalizationsPl extends AppLocalizations { @override String get chat_clearPathSubtitle => - 'Zmusz do ponownej identyfikacji przy następnym wysłaniu'; + 'Wymuś ponowne wyznaczenie trasy przy następnym wysłaniu'; @override String get chat_pathCleared => - 'Ścieżka oczyszczona. Kolejne powiadomienie odnajdzie trasę.'; + 'Ścieżka wyczyszczona. Następna wiadomość odnajdzie trasę.'; @override String get chat_floodModeSubtitle => @@ -1438,7 +1439,7 @@ class AppLocalizationsPl extends AppLocalizations { 'Urządzenie nie zostało jeszcze potwierdzone.'; @override - String get chat_type => 'Wprowadź'; + String get chat_type => 'Typ'; @override String get chat_path => 'Ścieżka'; @@ -1471,7 +1472,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String chat_unread(int count) { - return 'Niezgłoszone: $count'; + return 'Nieprzeczytane: $count'; } @override @@ -1515,7 +1516,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String map_pinsCount(int count) { - return 'Pinki: $count'; + return 'Pinezki: $count'; } @override @@ -1531,13 +1532,13 @@ class AppLocalizationsPl extends AppLocalizations { String get map_sensor => 'Czujnik'; @override - String get map_pinDm => 'Zablokuj (DM)'; + String get map_pinDm => 'Pinezka (DM)'; @override - String get map_pinPrivate => 'Zablokuj (Prywatnie)'; + String get map_pinPrivate => 'Pinezka (prywatna)'; @override - String get map_pinPublic => 'Oznacz jako publiczne'; + String get map_pinPublic => 'Pinezka (publiczna)'; @override String get map_lastSeen => 'Ostatni raz widziany'; @@ -1559,10 +1560,10 @@ class AppLocalizationsPl extends AppLocalizations { String get map_shareMarkerHere => 'Udostępnij znacznik tutaj'; @override - String get map_setAsMyLocation => 'Ustaw jako moje lokalizację'; + String get map_setAsMyLocation => 'Ustaw jako moją lokalizację'; @override - String get map_pinLabel => 'Oznacz etykietę'; + String get map_pinLabel => 'Etykieta pinezki'; @override String get map_label => 'Etykieta'; @@ -1584,12 +1585,12 @@ class AppLocalizationsPl extends AppLocalizations { @override String map_publicLocationShareConfirm(String channelLabel) { - return 'Wkrótce udostępnisz lokalizację w $channelLabel. Ten kanał jest publiczny i każdy z PSK może go zobaczyć.'; + return 'Zamierzasz udostępnić lokalizację w $channelLabel. Ten kanał jest publiczny i każdy z PSK może go zobaczyć.'; } @override String get map_connectToShareMarkers => - 'Połącz się z urządzeniem, aby udostępniać znacznik.'; + 'Połącz się z urządzeniem, aby udostępniać znaczniki.'; @override String get map_filterNodes => 'Filtruj Węzły'; @@ -1613,25 +1614,25 @@ class AppLocalizationsPl extends AppLocalizations { String get map_filterByKeyPrefix => 'Filtruj po prefiksie klucza'; @override - String get map_publicKeyPrefix => 'Przewód klucza publicznego'; + String get map_publicKeyPrefix => 'Prefiks klucza publicznego'; @override - String get map_markers => 'Oznaczarki'; + String get map_markers => 'Znaczniki'; @override - String get map_showSharedMarkers => 'Pokaż współdzielone znaki.'; + String get map_showSharedMarkers => 'Pokaż udostępnione znaczniki.'; @override String get map_lastSeenTime => 'Ostatni raz widziany'; @override - String get map_sharedPin => 'Współdzielony PIN'; + String get map_sharedPin => 'Udostępniona pinezka'; @override String get map_joinRoom => 'Dołącz do pokoju'; @override - String get map_manageRepeater => 'Zarządzaj Powtórzami'; + String get map_manageRepeater => 'Zarządzaj powtarzaczem'; @override String get map_tapToAdd => 'Kliknij na węzły, aby dodać je do ścieżki.'; @@ -1669,12 +1670,12 @@ class AppLocalizationsPl extends AppLocalizations { @override String mapCache_cachedTiles(int count) { - return 'Pamiętanych $count płytek'; + return 'Zapisano $count płytek w pamięci podręcznej'; } @override String mapCache_cachedTilesWithFailed(int downloaded, int failed) { - return 'Pamiętane $downloaded płytki ($failed nieudane)'; + return 'Zapisano $downloaded płytek ($failed nieudane)'; } @override @@ -1772,13 +1773,13 @@ class AppLocalizationsPl extends AppLocalizations { String get time_month => 'miesiąc'; @override - String get time_months => 'miesiace'; + String get time_months => 'miesiące'; @override String get time_minutes => 'minuty'; @override - String get time_allTime => 'Wszystko czasowo'; + String get time_allTime => 'Cały czas'; @override String get dialog_disconnect => 'Odłącz'; @@ -1788,7 +1789,7 @@ class AppLocalizationsPl extends AppLocalizations { 'Czy na pewno chcesz się odłączyć od tego urządzenia?'; @override - String get login_repeaterLogin => 'Powtórz Logowanie'; + String get login_repeaterLogin => 'Logowanie do powtarzacza'; @override String get login_roomLogin => 'Logowanie do pokoju'; @@ -1815,7 +1816,7 @@ class AppLocalizationsPl extends AppLocalizations { 'Wprowadź hasło do pokoju, aby uzyskać dostęp do ustawień i statusu.'; @override - String get login_routing => 'Przekierowanie'; + String get login_routing => 'Trasowanie'; @override String get login_routingMode => 'Tryb routingu'; @@ -1824,7 +1825,7 @@ class AppLocalizationsPl extends AppLocalizations { String get login_autoUseSavedPath => 'Automatycznie (użyj zapisanej ścieżki)'; @override - String get login_forceFloodMode => 'Wymusz Tryb Powodowany'; + String get login_forceFloodMode => 'Wymuś tryb zalewowy'; @override String get login_managePaths => 'Zarządzaj Ścieżkami'; @@ -1847,14 +1848,14 @@ class AppLocalizationsPl extends AppLocalizations { 'Logowanie nie powiodło się. Hasło jest nieprawidłowe albo repeater jest nieosiągalny.'; @override - String get common_reload => 'Ponownie załadować'; + String get common_reload => 'Odśwież'; @override String get common_clear => 'Wyczyść'; @override String path_currentPath(String path) { - return 'Aktualny ścieżka: $path'; + return 'Aktualna ścieżka: $path'; } @override @@ -1862,8 +1863,10 @@ class AppLocalizationsPl extends AppLocalizations { String _temp0 = intl.Intl.pluralLogic( count, locale: localeName, - other: 'hops', - one: 'hop', + other: 'skoków', + many: 'skoków', + few: 'skoki', + one: 'skok', ); return 'Użyj ścieżki $count $_temp0.'; } @@ -1872,7 +1875,7 @@ class AppLocalizationsPl extends AppLocalizations { String get path_enterCustomPath => 'Wprowadź własną ścieżkę'; @override - String get path_currentPathLabel => 'Aktualny ścieżka'; + String get path_currentPathLabel => 'Aktualna ścieżka'; @override String get path_hexPrefixInstructions => @@ -1880,14 +1883,14 @@ class AppLocalizationsPl extends AppLocalizations { @override String get path_hexPrefixExample => - 'A1,F2,3C (każedy węzeł używa pierwszego bajtu swojego klucza publicznego)'; + 'A1,F2,3C (każdy węzeł używa pierwszego bajtu swojego klucza publicznego)'; @override - String get path_labelHexPrefixes => 'Ścieżka (przesunięcia bitowe)'; + String get path_labelHexPrefixes => 'Ścieżka (prefiksy hex)'; @override String get path_helperMaxHops => - 'Maksymalnie 64 skoki. Każda prefiks ma 2 znaki szesnastkowe (1 bajt).'; + 'Maksymalnie 64 skoki. Każdy prefiks ma 2 znaki szesnastkowe (1 bajt).'; @override String get path_selectFromContacts => 'Albo wybierz z kontaktów:'; @@ -1913,7 +1916,7 @@ class AppLocalizationsPl extends AppLocalizations { String get path_setPath => 'Ustaw Ścieżkę'; @override - String get repeater_management => 'Zarządzanie Powtórzami'; + String get repeater_management => 'Zarządzanie powtarzaczami'; @override String get room_management => 'Zarządzanie Serwerem Pokoju'; @@ -1929,7 +1932,7 @@ class AppLocalizationsPl extends AppLocalizations { 'Wyświetl status powtarzacza, statystyki i sąsiadów.'; @override - String get repeater_telemetry => 'Telemetry'; + String get repeater_telemetry => 'Telemetria'; @override String get repeater_telemetrySubtitle => @@ -1939,7 +1942,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_cli => 'CLI'; @override - String get repeater_cliSubtitle => 'Wyślij polecenia do powielacza'; + String get repeater_cliSubtitle => 'Wyślij polecenia do powtarzacza'; @override String get repeater_neighbors => 'Sąsiedzi'; @@ -1965,7 +1968,7 @@ class AppLocalizationsPl extends AppLocalizations { 'Automatycznie (użyj zapisanej ścieżki)'; @override - String get repeater_forceFloodMode => 'Wymusz Tryb Powodowany'; + String get repeater_forceFloodMode => 'Wymuś tryb zalewowy'; @override String get repeater_pathManagement => 'Zarządzanie ścieżkami'; @@ -1974,7 +1977,8 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_refresh => 'Odśwież'; @override - String get repeater_statusRequestTimeout => 'Życzenie statusu timed out.'; + String get repeater_statusRequestTimeout => + 'Przekroczono czas oczekiwania na status.'; @override String repeater_errorLoadingStatus(String error) { @@ -1991,7 +1995,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_clockAtLogin => 'Godzina (przy logowaniu)'; @override - String get repeater_uptime => 'Dostępność'; + String get repeater_uptime => 'Czas pracy'; @override String get repeater_queueLength => 'Długość kolejki'; @@ -2041,17 +2045,17 @@ class AppLocalizationsPl extends AppLocalizations { @override String repeater_packetTxTotal(int total, String flood, String direct) { - return 'Razem: $total, Powodzenie: $flood, Bezpośrednio: $direct'; + return 'Razem: $total, Zalew: $flood, Bezpośrednio: $direct'; } @override String repeater_packetRxTotal(int total, String flood, String direct) { - return 'Razem: $total, Powodzenie: $flood, Bezpośrednio: $direct'; + return 'Razem: $total, Zalew: $flood, Bezpośrednio: $direct'; } @override String repeater_duplicatesFloodDirect(String flood, String direct) { - return 'Powodzie: $flood, Bezpośrednie: $direct'; + return 'Zalew: $flood, Bezpośrednie: $direct'; } @override @@ -2060,7 +2064,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get repeater_settingsTitle => 'Ustawienia Powtórki'; + String get repeater_settingsTitle => 'Ustawienia powtarzacza'; @override String get repeater_basicSettings => 'Podstawowe Ustawienia'; @@ -2075,13 +2079,13 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_adminPassword => 'Hasło Administracyjne'; @override - String get repeater_adminPasswordHelper => 'Pełny dostęp hasło'; + String get repeater_adminPasswordHelper => 'Hasło z pełnym dostępem'; @override String get repeater_guestPassword => 'Hasło gościa'; @override - String get repeater_guestPasswordHelper => 'Dostęp tylko do odczytu hasło'; + String get repeater_guestPasswordHelper => 'Hasło tylko do odczytu'; @override String get repeater_radioSettings => 'Ustawienia radia'; @@ -2105,7 +2109,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_spreadingFactor => 'Współczynnik rozpraszania'; @override - String get repeater_codingRate => 'Stawka kodowania'; + String get repeater_codingRate => 'Współczynnik kodowania'; @override String get repeater_locationSettings => 'Ustawienia Lokalizacji'; @@ -2130,7 +2134,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_packetForwardingSubtitle => - 'Włącz repeater, aby przekazywać pakiety.'; + 'Włącz powtarzacz, aby przekazywać pakiety.'; @override String get repeater_guestAccess => 'Dostęp dla gości'; @@ -2140,7 +2144,7 @@ class AppLocalizationsPl extends AppLocalizations { 'Umożliw dostęp tylko do odczytu dla gości.'; @override - String get repeater_privacyMode => 'Tryb Prywatności'; + String get repeater_privacyMode => 'Tryb prywatności'; @override String get repeater_privacyModeSubtitle => @@ -2199,7 +2203,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_eraseFileSystemSubtitle => - 'Sformatuj system plików powielacza'; + 'Sformatuj system plików powtarzacza'; @override String get repeater_eraseFileSystemConfirm => @@ -2234,7 +2238,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_refreshBasicSettings => 'Odśwież Podstawowe Ustawienia'; @override - String get repeater_refreshRadioSettings => 'Odśwież Ustawienia Radio'; + String get repeater_refreshRadioSettings => 'Odśwież ustawienia radia'; @override String get repeater_refreshTxPower => 'Odśwież moc TX'; @@ -2254,7 +2258,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_refreshAdvertisementSettings => - 'Odśwież Ustawienia Reklamy'; + 'Odśwież ustawienia rozgłoszeń'; @override String repeater_refreshed(String label) { @@ -2298,7 +2302,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_enterCommandFirst => 'Wprowadź najpierw polecenie'; @override - String get repeater_cliCommandFrameTitle => 'Określony Wyraz Polecenia CLI'; + String get repeater_cliCommandFrameTitle => 'Ramka polecenia CLI'; @override String repeater_cliCommandError(String error) { @@ -2306,10 +2310,10 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get repeater_cliQuickGetName => 'Pobierz imię'; + String get repeater_cliQuickGetName => 'Pobierz nazwę'; @override - String get repeater_cliQuickGetRadio => 'Uzyskaj Radio'; + String get repeater_cliQuickGetRadio => 'Pobierz radio'; @override String get repeater_cliQuickGetTx => 'Pobierz TX'; @@ -2321,7 +2325,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_cliQuickVersion => 'Wersja'; @override - String get repeater_cliQuickAdvertise => 'Reklama'; + String get repeater_cliQuickAdvertise => 'Rozgłoś'; @override String get repeater_cliQuickClock => 'Godzina'; @@ -2366,7 +2370,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpSetFloodMax => - 'Ustawia maksymalną liczbę skoków pakietu powrotnego (jeśli >= max, pakiet nie jest przekierowywany)'; + 'Ustawia maksymalną liczbę skoków pakietu zalewowego (jeśli >= max, pakiet nie jest przekierowywany)'; @override String get repeater_cliHelpSetIntThresh => @@ -2374,7 +2378,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpSetAgcResetInterval => - 'Ustawia interwał do zresetowania Automatycznego Sterownika Głośności. Ustaw na 0, aby wyłączyć.'; + 'Ustawia interwał do zresetowania automatycznego wzmocnienia (AGC). Ustaw na 0, aby wyłączyć.'; @override String get repeater_cliHelpSetMultiAcks => @@ -2452,7 +2456,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpGetBridgeType => - 'Uzyskano typ mostu: brak, rs232, espnow'; + 'Pobiera typ mostka: brak, rs232, espnow'; @override String get repeater_cliHelpLogStart => @@ -2476,11 +2480,11 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpRegion => - '(tylko seria) Wyświetla wszystkie zdefiniowane regiony i aktualne uprawnienia do powodzi.'; + '(tylko port szeregowy) Wyświetla wszystkie zdefiniowane regiony i aktualne uprawnienia do zalewu.'; @override String get repeater_cliHelpRegionLoad => - 'ZAPOMNIJ: to jest specjalne wywołanie wielokomendowe. Każda następna komenda jest nazwą regionu (wcięta spacjami, aby wskazywać hierarchię nadrzędną, z minimum jedną spacją). Zakończona wysłaniem pustej linii/komendy.'; + 'UWAGA: to jest specjalne wywołanie wielokomendowe. Każda następna komenda jest nazwą regionu (wcięta spacjami, aby wskazywać hierarchię nadrzędną, z minimum jedną spacją). Zakończona wysłaniem pustej linii/komendy.'; @override String get repeater_cliHelpRegionGet => @@ -2496,11 +2500,11 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpRegionAllowf => - 'Ustawia uprawnienia \'P\'łytkowe dla podanego regionu. (\'\' dla zakresu globalnego/starszego)'; + 'Ustawia uprawnienia \'F\' (zalewowe) dla podanego regionu. (\'\' dla zakresu globalnego/starszego)'; @override String get repeater_cliHelpRegionDenyf => - 'Usuwa uprawnienie \'Pływające\' dla podanej strefy. (ZALECANE: na tym etapie NIE zaleca się używania tego na globalnym/starszym zakresie!!).'; + 'Usuwa uprawnienie \'F\' (zalewowe) dla podanej strefy. (ZALECANE: na tym etapie NIE zaleca się używania tego na globalnym/starszym zakresie!!).'; @override String get repeater_cliHelpRegionHome => @@ -2541,7 +2545,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_commandsListNote => - 'ZAPAMIĘTAJ: dla różnych poleceń \"set ...\" istnieje również polecenie \"get ...\".'; + 'UWAGA: dla różnych poleceń \"set ...\" istnieje również polecenie \"get ...\".'; @override String get repeater_general => 'Ogólne'; @@ -2574,15 +2578,15 @@ class AppLocalizationsPl extends AppLocalizations { 'Polecenie GPS zostało wprowadzone w celu zarządzania tematami związanymi z lokalizacją.'; @override - String get telemetry_receivedData => 'Otrzymano Dane Telemetrii'; + String get telemetry_receivedData => 'Odebrane dane telemetrii'; @override String get telemetry_requestTimeout => - 'Życzenie o danych telemetrycznych nie udało się.'; + 'Przekroczono czas oczekiwania na telemetrię.'; @override String telemetry_errorLoading(String error) { - return 'Błąd podczas ładowania telemetry: $error'; + return 'Błąd podczas ładowania telemetrii: $error'; } @override @@ -2606,7 +2610,7 @@ class AppLocalizationsPl extends AppLocalizations { String get telemetry_temperatureLabel => 'Temperatura'; @override - String get telemetry_currentLabel => 'Obecny'; + String get telemetry_currentLabel => 'Prąd'; @override String telemetry_batteryValue(int percent, String volts) { @@ -2615,7 +2619,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String telemetry_voltageValue(String volts) { - return '${volts}W'; + return '${volts}V'; } @override @@ -2663,10 +2667,10 @@ class AppLocalizationsPl extends AppLocalizations { String get channelPath_viewMap => 'Wyświetl mapę'; @override - String get channelPath_otherObservedPaths => 'Inne Zauważone Ścieżki'; + String get channelPath_otherObservedPaths => 'Inne zaobserwowane ścieżki'; @override - String get channelPath_repeaterHops => 'Skoki Powtórki'; + String get channelPath_repeaterHops => 'Skoki powtarzaczy'; @override String get channelPath_noHopDetails => @@ -2694,7 +2698,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String channelPath_observedPathTitle(int index, String hops) { - return 'Obserwowany ścieżka $index • $hops'; + return 'Obserwowana ścieżka $index • $hops'; } @override @@ -2734,7 +2738,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get channelPath_noRepeaterLocations => - 'Brak dostępnych lokalizacji powtarzaczy dla tego ścieżki.'; + 'Brak dostępnych lokalizacji powtarzaczy dla tej ścieżki.'; @override String channelPath_primaryPath(int index) { diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 8837732..5c66b65 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -17,7 +17,7 @@ "common_unknownDevice": "Nieznane urządzenie", "common_save": "Zapisz", "common_delete": "Usuń", - "common_close": "Zamknąć", + "common_close": "Zamknij", "common_edit": "Edytuj", "common_add": "Dodaj", "common_settings": "Ustawienia", @@ -28,12 +28,12 @@ "common_continue": "Kontynuuj", "common_share": "Udostępnij", "common_copy": "Kopiuj", - "common_retry": "Spróbować", + "common_retry": "Ponów", "common_hide": "Ukryj", "common_remove": "Usuń", "common_enable": "Włącz", - "common_disable": "Wyłączyć", - "common_reboot": "Zrestartować", + "common_disable": "Wyłącz", + "common_reboot": "Uruchom ponownie", "common_loading": "Ładowanie...", "common_notAvailable": "—", "common_voltageValue": "{volts} V", @@ -56,7 +56,7 @@ "scanner_scanning": "Skanowanie urządzeń...", "scanner_connecting": "Łączenie...", "scanner_disconnecting": "Odłączanie...", - "scanner_notConnected": "Niepołączony", + "scanner_notConnected": "Nie połączono", "scanner_connectedTo": "Połączono z {deviceName}", "@scanner_connectedTo": { "placeholders": { @@ -87,7 +87,7 @@ "settings_nodeName": "Nazwa węzła", "settings_nodeNameNotSet": "Nie ustawione", "settings_nodeNameHint": "Wprowadź nazwę węzła", - "settings_nodeNameUpdated": "Imię zaktualizowane", + "settings_nodeNameUpdated": "Nazwa zaktualizowana", "settings_radioSettings": "Ustawienia radia", "settings_radioSettingsSubtitle": "Częstotliwość, moc, współczynnik rozpraszania", "settings_radioSettingsUpdated": "Ustawienia radia zostały zaktualizowane", @@ -98,29 +98,29 @@ "settings_locationInvalid": "Nieprawidłowa szerokość geograficzna lub długość geograficzna.", "settings_latitude": "Szerokość", "settings_longitude": "Długość", - "settings_privacyMode": "Tryb Prywatny", + "settings_privacyMode": "Tryb prywatności", "settings_privacyModeSubtitle": "Ukryj imię/lokalizację w rozgłoszeniach", "settings_privacyModeToggle": "Włącz tryb prywatności, aby ukryć swoje imię i lokalizację w rozgłoszeniach.", "settings_privacyModeEnabled": "Tryb prywatności włączony", "settings_privacyModeDisabled": "Tryb prywatności wyłączony", "settings_actions": "Działania", "settings_sendAdvertisement": "Wyślij rozgłoszenie", - "settings_sendAdvertisementSubtitle": "Obecność transmisji jest teraz", + "settings_sendAdvertisementSubtitle": "Nadaj obecność teraz", "settings_advertisementSent": "Rozgłoszenie wysłane", - "settings_syncTime": "Czas synchronizacji", + "settings_syncTime": "Synchronizacja czasu", "settings_syncTimeSubtitle": "Ustaw zegar urządzenia na czas telefonu.", - "settings_timeSynchronized": "Synchronizacja czasu", + "settings_timeSynchronized": "Czas zsynchronizowany", "settings_refreshContacts": "Odśwież Kontakty", "settings_refreshContactsSubtitle": "Odśwież listę kontaktów z urządzenia", "settings_rebootDevice": "Zrestartuj Urządzenie", "settings_rebootDeviceSubtitle": "Zrestartuj urządzenie MeshCore", "settings_rebootDeviceConfirm": "Czy na pewno chcesz zrestartować urządzenie? Będziesz odłączony.", "settings_debug": "Debug", - "settings_bleDebugLog": "Log błędów BLE", + "settings_bleDebugLog": "Dziennik debugowania BLE", "settings_bleDebugLogSubtitle": "Polecenia BLE, odpowiedzi i surowe dane", - "settings_appDebugLog": "Log Wykonywania Aplikacji", + "settings_appDebugLog": "Dziennik debugowania aplikacji", "settings_appDebugLogSubtitle": "Komunikaty debugowania aplikacji", - "settings_about": "O mnie", + "settings_about": "O aplikacji", "settings_aboutVersion": "MeshCore Open v{version}", "@settings_aboutVersion": { "placeholders": { @@ -131,21 +131,24 @@ }, "settings_aboutLegalese": "Projekt MeshCore Open Source 2026", "settings_aboutDescription": "Otwarty kod źródłowy klient Flutter dla urządzeń do sieci mesh LoRa MeshCore.", - "settings_infoName": "Imię", + "settings_infoName": "Nazwa", "settings_infoId": "ID", "settings_infoStatus": "Status", "settings_infoBattery": "Bateria", "settings_infoPublicKey": "Klucz Publiczny", "settings_infoContactsCount": "Liczba kontaktów", "settings_infoChannelCount": "Liczba kanałów", - "settings_presets": "Preset", + "settings_presets": "Presety", + "settings_preset915Mhz": "915 MHz", + "settings_preset868Mhz": "868 MHz", + "settings_preset433Mhz": "433 MHz", "settings_frequency": "Częstotliwość (MHz)", "settings_frequencyHelper": "300,0 - 2500,0", "settings_frequencyInvalid": "Nieprawidłowa częstotliwość (300-2500 MHz)", "settings_bandwidth": "Przepustowość", - "settings_spreadingFactor": "Rozkład Czynnika", - "settings_codingRate": "Stawka Kodowania", - "settings_txPower": "TX Moc (dBm)", + "settings_spreadingFactor": "Współczynnik rozpraszania", + "settings_codingRate": "Współczynnik kodowania", + "settings_txPower": "Moc TX (dBm)", "settings_txPowerHelper": "0 - 22", "settings_txPowerInvalid": "Nieprawidłowa moc TX (0-22 dBm)", "settings_error": "Błąd: {message}", @@ -163,7 +166,7 @@ "appSettings_themeLight": "Jasne", "appSettings_themeDark": "Ciemny", "appSettings_language": "Język", - "appSettings_languageSystem": "Domyślny systemowy", + "appSettings_languageSystem": "Domyślny systemu", "appSettings_languageEn": "English", "appSettings_languageFr": "Français", "appSettings_languageEs": "Español", @@ -190,17 +193,17 @@ "appSettings_advertisementNotifications": "Powiadomienia o rozgłoszeniach", "appSettings_advertisementNotificationsSubtitle": "Wyświetl powiadomienie, gdy zostaną wykryte nowe węzły.", "appSettings_messaging": "Wiadomości", - "appSettings_clearPathOnMaxRetry": "Wyczyść Ścieżkę na Maksymalnej Próbie", + "appSettings_clearPathOnMaxRetry": "Wyczyść ścieżkę po maks. liczbie prób", "appSettings_clearPathOnMaxRetrySubtitle": "Resetuj ścieżkę kontaktu po 5 nieudanych próbach wysłania", - "appSettings_pathsWillBeCleared": "Droga będzie wyczyszczona po 5 nieudanych próbach.", - "appSettings_pathsWillNotBeCleared": "Droga nie zostanie automatycznie wyczyszczona.", - "appSettings_autoRouteRotation": "Automatyczne Rotowanie Trasy", + "appSettings_pathsWillBeCleared": "Ścieżka zostanie wyczyszczona po 5 nieudanych próbach.", + "appSettings_pathsWillNotBeCleared": "Ścieżka nie zostanie automatycznie wyczyszczona.", + "appSettings_autoRouteRotation": "Automatyczna rotacja trasy", "appSettings_autoRouteRotationSubtitle": "Przełączaj się między najlepszymi ścieżkami a trybem zalewowym.", "appSettings_autoRouteRotationEnabled": "Automatyczne obracanie tras włączone", "appSettings_autoRouteRotationDisabled": "Automatyczne obracanie tras wyłączone", "appSettings_battery": "Bateria", "appSettings_batteryChemistry": "Chemia Baterii", - "appSettings_batteryChemistryPerDevice": "Ustawione na urządzenie ({deviceName})", + "appSettings_batteryChemistryPerDevice": "Ustaw dla urządzenia ({deviceName})", "@appSettings_batteryChemistryPerDevice": { "placeholders": { "deviceName": { @@ -213,8 +216,8 @@ "appSettings_batteryLifepo4": "LiFePO4 (2,6-3,65 V)", "appSettings_batteryLipo": "LiPo (3,0-4,2V)", "appSettings_mapDisplay": "Wyświetlanie mapy", - "appSettings_showRepeaters": "Pokaż Powtórniki", - "appSettings_showRepeatersSubtitle": "Wyświetl węzły powtarzające się na mapie", + "appSettings_showRepeaters": "Pokaż powtarzacze", + "appSettings_showRepeatersSubtitle": "Wyświetl węzły powtarzaczy na mapie", "appSettings_showChatNodes": "Pokaż Węzły Rozmowy", "appSettings_showChatNodesSubtitle": "Wyświetl węzły czatu na mapie", "appSettings_showOtherNodes": "Pokaż inne węzły", @@ -231,12 +234,12 @@ }, "appSettings_mapTimeFilter": "Filtrowanie Czasu Mapy", "appSettings_showNodesDiscoveredWithin": "Pokaż węzły odkryte w:", - "appSettings_allTime": "Wszystko czasowo", + "appSettings_allTime": "Cały czas", "appSettings_lastHour": "Ostatnia godzina", "appSettings_last6Hours": "Ostatnie 6 godzin", "appSettings_last24Hours": "Ostatnie 24 godziny", - "appSettings_lastWeek": "Tydzień temu", - "appSettings_offlineMapCache": "Bufor Map Offline", + "appSettings_lastWeek": "Ostatni tydzień", + "appSettings_offlineMapCache": "Pamięć podręczna map offline", "appSettings_noAreaSelected": "Nie zaznaczono żadnej powierzchni.", "appSettings_areaSelectedZoom": "Wybrany obszar (skala {minZoom}-{maxZoom})", "@appSettings_areaSelectedZoom": { @@ -252,8 +255,8 @@ "appSettings_debugCard": "Debug", "appSettings_appDebugLogging": "Logowanie Debugowania Aplikacji", "appSettings_appDebugLoggingSubtitle": "Loguj wiadomości debugowania aplikacji w celu rozwiązywania problemów.", - "appSettings_appDebugLoggingEnabled": "Zdebugowanie aplikacji włączone", - "appSettings_appDebugLoggingDisabled": "Zasubskrybowane logi debugowania aplikacji wyłączone.", + "appSettings_appDebugLoggingEnabled": "Logowanie debugowania aplikacji włączone", + "appSettings_appDebugLoggingDisabled": "Logowanie debugowania aplikacji wyłączone.", "contacts_title": "Kontakty", "contacts_noContacts": "Brak jeszcze kontaktów.", "contacts_contactsWillAppear": "Kontakty będą wyświetlane, gdy urządzenia nadają rozgłoszenia.", @@ -269,7 +272,7 @@ } } }, - "contacts_manageRepeater": "Zarządzaj Powtórzami", + "contacts_manageRepeater": "Zarządzaj powtarzaczem", "contacts_roomLogin": "Logowanie do pokoju", "contacts_openChat": "Otwórz czat", "contacts_editGroup": "Edytuj Grupę", @@ -297,8 +300,8 @@ "contacts_filterContacts": "Filtruj kontakty...", "contacts_noContactsMatchFilter": "Brak pasujących kontaktów do Twojego filtra", "contacts_noMembers": "Brak członków", - "contacts_lastSeenNow": "Ostatnie połączenie", - "contacts_lastSeenMinsAgo": "Ostatnie połączenie {minutes} min temu", + "contacts_lastSeenNow": "Widziano przed chwilą", + "contacts_lastSeenMinsAgo": "Widziano {minutes} min temu", "@contacts_lastSeenMinsAgo": { "placeholders": { "minutes": { @@ -307,7 +310,7 @@ } }, "contacts_lastSeenHourAgo": "Ostatni raz widziany 1 godzinę temu", - "contacts_lastSeenHoursAgo": "Ostatnie połączenie {hours} godzin temu", + "contacts_lastSeenHoursAgo": "Widziano {hours} godz. temu", "@contacts_lastSeenHoursAgo": { "placeholders": { "hours": { @@ -316,7 +319,7 @@ } }, "contacts_lastSeenDayAgo": "Ostatni raz widziany 1 dzień temu", - "contacts_lastSeenDaysAgo": "Ostatnie połączenie {days} dni temu", + "contacts_lastSeenDaysAgo": "Widziano {days} dni temu", "@contacts_lastSeenDaysAgo": { "placeholders": { "days": { @@ -339,7 +342,7 @@ }, "channels_hashtagChannel": "Kanał z hashtagami", "channels_public": "Publiczny", - "channels_private": "Prywatne", + "channels_private": "Prywatny", "channels_publicChannel": "Kanał publiczny", "channels_privateChannel": "Prywatny kanał", "channels_editChannel": "Edytuj kanał", @@ -366,7 +369,7 @@ "channels_channelIndexLabel": "Indeks kanału", "channels_channelName": "Nazwa kanału", "channels_usePublicChannel": "Użyj kanału publicznego", - "channels_standardPublicPsk": "Standard public PSK", + "channels_standardPublicPsk": "Standardowy publiczny PSK", "channels_pskHex": "PSK (Hex)", "channels_generateRandomPsk": "Wygeneruj losowy klucz PSK", "channels_enterChannelName": "Proszę podać nazwę kanału.", @@ -401,7 +404,7 @@ "channels_sortManual": "Ręczna", "channels_sortAZ": "A-Z", "channels_sortLatestMessages": "Najnowsze wiadomości", - "channels_sortUnread": "Niezgłoszone", + "channels_sortUnread": "Nieprzeczytane", "chat_noMessages": "Brak jeszcze wiadomości", "chat_sendMessageToStart": "Wyślij wiadomość, aby rozpocząć.", "chat_originalMessageNotFound": "Błąd: Nie znaleziono oryginalnego komunikatu", @@ -413,7 +416,7 @@ } } }, - "chat_replyTo": "Odpowiedz {name}", + "chat_replyTo": "Odpowiedz do {name}", "@chat_replyTo": { "placeholders": { "name": { @@ -441,8 +444,8 @@ }, "chat_messageCopied": "Wiadomość skopiowana", "chat_messageDeleted": "Wiadomość usunięta", - "chat_retryingMessage": "Próba ponowienia", - "chat_retryCount": "Spróbuj {current}/{max}", + "chat_retryingMessage": "Ponawianie wiadomości", + "chat_retryCount": "Próba {current}/{max}", "@chat_retryCount": { "placeholders": { "current": { @@ -458,8 +461,8 @@ "chat_addReaction": "Dodaj Reakcję", "chat_me": "Ja", "emojiCategorySmileys": "Emoji", - "emojiCategoryGestures": "Gestikulacje", - "emojiCategoryHearts": "Serce", + "emojiCategoryGestures": "Gesty", + "emojiCategoryHearts": "Serca", "emojiCategoryObjects": "Obiekty", "gifPicker_title": "Wybierz GIF", "gifPicker_searchHint": "Wyszukaj GIF-y...", @@ -476,8 +479,8 @@ "debugLog_bleCopied": "Skopiowany log BLE", "debugLog_noEntries": "Nie ma jeszcze żadnych logów debugowania.", "debugLog_enableInSettings": "Włącz logowanie debugowania aplikacji w ustawieniach", - "debugLog_frames": "Ramy", - "debugLog_rawLogRx": "Surowe Log-RX", + "debugLog_frames": "Ramki", + "debugLog_rawLogRx": "Surowy log RX", "debugLog_noBleActivity": "Brak aktywności BLE jeszcze.", "debugFrame_length": "Długość ramy: {count} bajtów", "@debugFrame_length": { @@ -496,7 +499,7 @@ } }, "debugFrame_textMessageHeader": "Wiadomość tekstowa:", - "debugFrame_destinationPubKey": "- Oznaczenie PubKey: {pubKey}", + "debugFrame_destinationPubKey": "- Docelowy klucz publiczny: {pubKey}", "@debugFrame_destinationPubKey": { "placeholders": { "pubKey": { @@ -532,7 +535,7 @@ } }, "debugFrame_textTypeCli": "CLI", - "debugFrame_textTypePlain": "Proste", + "debugFrame_textTypePlain": "Zwykły", "debugFrame_text": "- Tekst: \"{text}\"", "@debugFrame_text": { "placeholders": { @@ -541,16 +544,16 @@ } } }, - "debugFrame_hexDump": "Wyjście SzESZCZNULNE:", + "debugFrame_hexDump": "Zrzut hex:", "chat_pathManagement": "Zarządzanie ścieżkami", "chat_routingMode": "Tryb routingu", "chat_autoUseSavedPath": "Automatyczne (użyj zapisanej ścieżki)", - "chat_forceFloodMode": "Wymusz Tryb Powodowany", + "chat_forceFloodMode": "Wymuś tryb zalewowy", "chat_recentAckPaths": "Ostatnie ścieżki ACK (naciśnij, aby użyć):", "chat_pathHistoryFull": "Historia ścieżek jest pełna. Usuń wpisy, aby dodać nowe.", "chat_hopSingular": "skok", "chat_hopPlural": "skoki", - "chat_hopsCount": "{count} {count, plural, =1{hop} other{hops}}", + "chat_hopsCount": "{count} {count, plural, one{skok} few{skoki} many{skoków} other{skoków}}", "@chat_hopsCount": { "placeholders": { "count": { @@ -562,11 +565,11 @@ "chat_removePath": "Usuń ścieżkę", "chat_noPathHistoryYet": "Brak jeszcze historii ścieżek.\nWyślij wiadomość, aby odkryć ścieżki.", "chat_pathActions": "Działania ścieżki:", - "chat_setCustomPath": "Ustaw Ścieżkę Dostosowaną", + "chat_setCustomPath": "Ustaw ścieżkę niestandardową", "chat_setCustomPathSubtitle": "Ręcznie określ trasę.", "chat_clearPath": "Wyczyść Ścieżkę", - "chat_clearPathSubtitle": "Zmusz do ponownej identyfikacji przy następnym wysłaniu", - "chat_pathCleared": "Ścieżka oczyszczona. Kolejne powiadomienie odnajdzie trasę.", + "chat_clearPathSubtitle": "Wymuś ponowne wyznaczenie trasy przy następnym wysłaniu", + "chat_pathCleared": "Ścieżka wyczyszczona. Następna wiadomość odnajdzie trasę.", "chat_floodModeSubtitle": "Użyj przełącznika routingu w pasku narzędzi.", "chat_floodModeEnabled": "Tryb powodziowy włączony. Włącz ponownie za pomocą ikony routingu w pasku narzędzi.", "chat_fullPath": "Pełna ścieżka", @@ -585,7 +588,7 @@ "chat_pathSavedLocally": "Zapisano lokalnie. Połącz się, aby zsynchronizować.", "chat_pathDeviceConfirmed": "Urządzenie potwierdzone.", "chat_pathDeviceNotConfirmed": "Urządzenie nie zostało jeszcze potwierdzone.", - "chat_type": "Wprowadź", + "chat_type": "Typ", "chat_path": "Ścieżka", "chat_publicKey": "Klucz Publiczny", "chat_compressOutgoingMessages": "Kompresuj wychodzące wiadomości", @@ -602,7 +605,7 @@ "chat_floodAuto": "Zalew (automatyczny)", "chat_direct": "Bezpośrednio", "chat_poiShared": "Wspólny POI", - "chat_unread": "Niezgłoszone: {count}", + "chat_unread": "Nieprzeczytane: {count}", "@chat_unread": { "placeholders": { "count": { @@ -633,7 +636,7 @@ } } }, - "map_pinsCount": "Pinki: {count}", + "map_pinsCount": "Pinezki: {count}", "@map_pinsCount": { "placeholders": { "count": { @@ -645,23 +648,23 @@ "map_repeater": "Powtórzacz", "map_room": "Pokój", "map_sensor": "Czujnik", - "map_pinDm": "Zablokuj (DM)", - "map_pinPrivate": "Zablokuj (Prywatnie)", - "map_pinPublic": "Oznacz jako publiczne", + "map_pinDm": "Pinezka (DM)", + "map_pinPrivate": "Pinezka (prywatna)", + "map_pinPublic": "Pinezka (publiczna)", "map_lastSeen": "Ostatni raz widziany", "map_disconnectConfirm": "Czy na pewno chcesz się odłączyć od tego urządzenia?", "map_from": "Od", "map_source": "Źródło", "map_flags": "Flagi", "map_shareMarkerHere": "Udostępnij znacznik tutaj", - "map_pinLabel": "Oznacz etykietę", + "map_pinLabel": "Etykieta pinezki", "map_label": "Etykieta", "map_pointOfInterest": "Punkt zainteresowań", "map_sendToContact": "Wyślij do kontaktu", "map_sendToChannel": "Wyślij do kanału", "map_noChannelsAvailable": "Brak dostępnych kanałów", "map_publicLocationShare": "Udostępnij lokalizację publicznie", - "map_publicLocationShareConfirm": "Wkrótce udostępnisz lokalizację w {channelLabel}. Ten kanał jest publiczny i każdy z PSK może go zobaczyć.", + "map_publicLocationShareConfirm": "Zamierzasz udostępnić lokalizację w {channelLabel}. Ten kanał jest publiczny i każdy z PSK może go zobaczyć.", "@map_publicLocationShareConfirm": { "placeholders": { "channelLabel": { @@ -669,7 +672,7 @@ } } }, - "map_connectToShareMarkers": "Połącz się z urządzeniem, aby udostępniać znacznik.", + "map_connectToShareMarkers": "Połącz się z urządzeniem, aby udostępniać znaczniki.", "map_filterNodes": "Filtruj Węzły", "map_nodeTypes": "Typy węzłów", "map_chatNodes": "Węzły czatu", @@ -677,13 +680,13 @@ "map_otherNodes": "Inne węzły", "map_keyPrefix": "Prefiks klucza", "map_filterByKeyPrefix": "Filtruj po prefiksie klucza", - "map_publicKeyPrefix": "Przewód klucza publicznego", - "map_markers": "Oznaczarki", - "map_showSharedMarkers": "Pokaż współdzielone znaki.", + "map_publicKeyPrefix": "Prefiks klucza publicznego", + "map_markers": "Znaczniki", + "map_showSharedMarkers": "Pokaż udostępnione znaczniki.", "map_lastSeenTime": "Ostatni raz widziany", - "map_sharedPin": "Współdzielony PIN", + "map_sharedPin": "Udostępniona pinezka", "map_joinRoom": "Dołącz do pokoju", - "map_manageRepeater": "Zarządzaj Powtórzami", + "map_manageRepeater": "Zarządzaj powtarzaczem", "mapCache_title": "Bufor Map Offline", "mapCache_selectAreaFirst": "Wybierz obszar do wstępnego pobrania.", "mapCache_noTilesToDownload": "Brak dostępnych płytek do pobrania dla tego obszaru.", @@ -697,7 +700,7 @@ } }, "mapCache_downloadAction": "Pobierz", - "mapCache_cachedTiles": "Pamiętanych {count} płytek", + "mapCache_cachedTiles": "Zapisano {count} płytek w pamięci podręcznej", "@mapCache_cachedTiles": { "placeholders": { "count": { @@ -705,7 +708,7 @@ } } }, - "mapCache_cachedTilesWithFailed": "Pamiętane {downloaded} płytki ({failed} nieudane)", + "mapCache_cachedTilesWithFailed": "Zapisano {downloaded} płytek ({failed} nieudane)", "@mapCache_cachedTilesWithFailed": { "placeholders": { "downloaded": { @@ -801,12 +804,12 @@ "time_week": "tydzień", "time_weeks": "tygodnie", "time_month": "miesiąc", - "time_months": "miesiace", + "time_months": "miesiące", "time_minutes": "minuty", - "time_allTime": "Wszystko czasowo", + "time_allTime": "Cały czas", "dialog_disconnect": "Odłącz", "dialog_disconnectConfirm": "Czy na pewno chcesz się odłączyć od tego urządzenia?", - "login_repeaterLogin": "Powtórz Logowanie", + "login_repeaterLogin": "Logowanie do powtarzacza", "login_roomLogin": "Logowanie do pokoju", "login_password": "Hasło", "login_enterPassword": "Wprowadź hasło", @@ -814,10 +817,10 @@ "login_savePasswordSubtitle": "Hasło będzie bezpiecznie przechowywane na tym urządzeniu.", "login_repeaterDescription": "Wprowadź hasło do powtarzacza, aby uzyskać dostęp do ustawień i statusu.", "login_roomDescription": "Wprowadź hasło do pokoju, aby uzyskać dostęp do ustawień i statusu.", - "login_routing": "Przekierowanie", + "login_routing": "Trasowanie", "login_routingMode": "Tryb routingu", "login_autoUseSavedPath": "Automatycznie (użyj zapisanej ścieżki)", - "login_forceFloodMode": "Wymusz Tryb Powodowany", + "login_forceFloodMode": "Wymuś tryb zalewowy", "login_managePaths": "Zarządzaj Ścieżkami", "login_login": "Zaloguj się", "login_attempt": "Próba {current}/{max}", @@ -840,9 +843,9 @@ } }, "login_failedMessage": "Logowanie nie powiodło się. Hasło jest nieprawidłowe albo repeater jest nieosiągalny.", - "common_reload": "Ponownie załadować", + "common_reload": "Odśwież", "common_clear": "Wyczyść", - "path_currentPath": "Aktualny ścieżka: {path}", + "path_currentPath": "Aktualna ścieżka: {path}", "@path_currentPath": { "placeholders": { "path": { @@ -850,7 +853,7 @@ } } }, - "path_usingHopsPath": "Użyj ścieżki {count} {count, plural, =1{hop} other{hops}}.", + "path_usingHopsPath": "Użyj ścieżki {count} {count, plural, one{skok} few{skoki} many{skoków} other{skoków}}.", "@path_usingHopsPath": { "placeholders": { "count": { @@ -859,11 +862,11 @@ } }, "path_enterCustomPath": "Wprowadź własną ścieżkę", - "path_currentPathLabel": "Aktualny ścieżka", + "path_currentPathLabel": "Aktualna ścieżka", "path_hexPrefixInstructions": "Wprowadź 2-znakowe prefiksy szesnastkowe dla każdego skoku, oddzielone przecinkami.", - "path_hexPrefixExample": "A1,F2,3C (każedy węzeł używa pierwszego bajtu swojego klucza publicznego)", - "path_labelHexPrefixes": "Ścieżka (przesunięcia bitowe)", - "path_helperMaxHops": "Maksymalnie 64 skoki. Każda prefiks ma 2 znaki szesnastkowe (1 bajt).", + "path_hexPrefixExample": "A1,F2,3C (każdy węzeł używa pierwszego bajtu swojego klucza publicznego)", + "path_labelHexPrefixes": "Ścieżka (prefiksy hex)", + "path_helperMaxHops": "Maksymalnie 64 skoki. Każdy prefiks ma 2 znaki szesnastkowe (1 bajt).", "path_selectFromContacts": "Albo wybierz z kontaktów:", "path_noRepeatersFound": "Nie znaleziono repeaterów ani serwerów pokoi.", "path_customPathsRequire": "Dostosowane ścieżki wymagają pośrednich skoków, które mogą przekazywać wiadomości.", @@ -877,23 +880,23 @@ }, "path_tooLong": "Ścieżka jest zbyt długa. Dozwolonych skoków wynosi 64.", "path_setPath": "Ustaw Ścieżkę", - "repeater_management": "Zarządzanie Powtórzami", + "repeater_management": "Zarządzanie powtarzaczami", "repeater_managementTools": "Narzędzia Zarządzania", "repeater_status": "Status", "repeater_statusSubtitle": "Wyświetl status powtarzacza, statystyki i sąsiadów.", - "repeater_telemetry": "Telemetry", + "repeater_telemetry": "Telemetria", "repeater_telemetrySubtitle": "Wyświetl dane telemetryczne z czujników i statystyki systemu", "repeater_cli": "CLI", - "repeater_cliSubtitle": "Wyślij polecenia do powielacza", + "repeater_cliSubtitle": "Wyślij polecenia do powtarzacza", "repeater_settings": "Ustawienia", "repeater_settingsSubtitle": "Skonfiguruj parametry powtarzacza", "repeater_statusTitle": "Status powtarzacza", "repeater_routingMode": "Tryb routingu", "repeater_autoUseSavedPath": "Automatycznie (użyj zapisanej ścieżki)", - "repeater_forceFloodMode": "Wymusz Tryb Powodowany", + "repeater_forceFloodMode": "Wymuś tryb zalewowy", "repeater_pathManagement": "Zarządzanie ścieżkami", "repeater_refresh": "Odśwież", - "repeater_statusRequestTimeout": "Życzenie statusu timed out.", + "repeater_statusRequestTimeout": "Przekroczono czas oczekiwania na status.", "repeater_errorLoadingStatus": "Błąd podczas ładowania statusu: {error}", "@repeater_errorLoadingStatus": { "placeholders": { @@ -905,7 +908,7 @@ "repeater_systemInformation": "Informacje o systemie", "repeater_battery": "Bateria", "repeater_clockAtLogin": "Godzina (przy logowaniu)", - "repeater_uptime": "Dostępność", + "repeater_uptime": "Czas pracy", "repeater_queueLength": "Długość kolejki", "repeater_debugFlags": "Opcje debugowania", "repeater_radioStatistics": "Statystyki Radia", @@ -935,7 +938,7 @@ } } }, - "repeater_packetTxTotal": "Razem: {total}, Powodzenie: {flood}, Bezpośrednio: {direct}", + "repeater_packetTxTotal": "Razem: {total}, Zalew: {flood}, Bezpośrednio: {direct}", "@repeater_packetTxTotal": { "placeholders": { "total": { @@ -949,7 +952,7 @@ } } }, - "repeater_packetRxTotal": "Razem: {total}, Powodzenie: {flood}, Bezpośrednio: {direct}", + "repeater_packetRxTotal": "Razem: {total}, Zalew: {flood}, Bezpośrednio: {direct}", "@repeater_packetRxTotal": { "placeholders": { "total": { @@ -963,7 +966,7 @@ } } }, - "repeater_duplicatesFloodDirect": "Powodzie: {flood}, Bezpośrednie: {direct}", + "repeater_duplicatesFloodDirect": "Zalew: {flood}, Bezpośrednie: {direct}", "@repeater_duplicatesFloodDirect": { "placeholders": { "flood": { @@ -982,14 +985,14 @@ } } }, - "repeater_settingsTitle": "Ustawienia Powtórki", + "repeater_settingsTitle": "Ustawienia powtarzacza", "repeater_basicSettings": "Podstawowe Ustawienia", "repeater_repeaterName": "Nazwa Powtórnika", "repeater_repeaterNameHelper": "Wyświetl nazwę tego powtarzacza", "repeater_adminPassword": "Hasło Administracyjne", - "repeater_adminPasswordHelper": "Pełny dostęp hasło", + "repeater_adminPasswordHelper": "Hasło z pełnym dostępem", "repeater_guestPassword": "Hasło gościa", - "repeater_guestPasswordHelper": "Dostęp tylko do odczytu hasło", + "repeater_guestPasswordHelper": "Hasło tylko do odczytu", "repeater_radioSettings": "Ustawienia radia", "repeater_frequencyMhz": "Częstotliwość (MHz)", "repeater_frequencyHelper": "300-2500 MHz", @@ -997,7 +1000,7 @@ "repeater_txPowerHelper": "1-30 dBm", "repeater_bandwidth": "Przepustowość", "repeater_spreadingFactor": "Współczynnik rozpraszania", - "repeater_codingRate": "Stawka kodowania", + "repeater_codingRate": "Współczynnik kodowania", "repeater_locationSettings": "Ustawienia Lokalizacji", "repeater_latitude": "Szerokość", "repeater_latitudeHelper": "Stopnie dziesiętne (np. 37.7749)", @@ -1005,10 +1008,10 @@ "repeater_longitudeHelper": "Stopnie dziesiętne (np. -122,4194)", "repeater_features": "Funkcje", "repeater_packetForwarding": "Przekierowanie pakietów", - "repeater_packetForwardingSubtitle": "Włącz repeater, aby przekazywać pakiety.", + "repeater_packetForwardingSubtitle": "Włącz powtarzacz, aby przekazywać pakiety.", "repeater_guestAccess": "Dostęp dla gości", "repeater_guestAccessSubtitle": "Umożliw dostęp tylko do odczytu dla gości.", - "repeater_privacyMode": "Tryb Prywatności", + "repeater_privacyMode": "Tryb prywatności", "repeater_privacyModeSubtitle": "Ukryj imię/lokalizację w rozgłoszeniach", "repeater_advertisementSettings": "Ustawienia rozgłoszeń", "repeater_localAdvertInterval": "Interwał rozgłoszenia lokalnego", @@ -1038,7 +1041,7 @@ "repeater_regenerateIdentityKeySubtitle": "Wygeneruj nową parę kluczy publicznych/prywatnych", "repeater_regenerateIdentityKeyConfirm": "Zostanie wygenerowana nowa tożsamość dla powtarzacza. Kontynuować?", "repeater_eraseFileSystem": "Wyczyść System Plików", - "repeater_eraseFileSystemSubtitle": "Sformatuj system plików powielacza", + "repeater_eraseFileSystemSubtitle": "Sformatuj system plików powtarzacza", "repeater_eraseFileSystemConfirm": "OSTRZEŻENIE: To spowoduje usunięcie wszystkich danych z powtarzacza. Nie da się tego cofnąć!", "repeater_eraseSerialOnly": "Usunięcie jest dostępne tylko przez konsolę szeregową.", "repeater_commandSent": "Polecenie wysłane: {command}", @@ -1068,13 +1071,13 @@ } }, "repeater_refreshBasicSettings": "Odśwież Podstawowe Ustawienia", - "repeater_refreshRadioSettings": "Odśwież Ustawienia Radio", + "repeater_refreshRadioSettings": "Odśwież ustawienia radia", "repeater_refreshTxPower": "Odśwież moc TX", "repeater_refreshLocationSettings": "Odśwież Ustawienia Lokalizacji", "repeater_refreshPacketForwarding": "Odśwież trasowanie pakietów", "repeater_refreshGuestAccess": "Odśwież dostęp gościa", "repeater_refreshPrivacyMode": "Odśwież Tryb Prywatności", - "repeater_refreshAdvertisementSettings": "Odśwież Ustawienia Reklamy", + "repeater_refreshAdvertisementSettings": "Odśwież ustawienia rozgłoszeń", "repeater_refreshed": "{label} odświeżone", "@repeater_refreshed": { "placeholders": { @@ -1101,7 +1104,7 @@ "repeater_previousCommand": "Poprzednia komenda", "repeater_nextCommand": "Następna komenda", "repeater_enterCommandFirst": "Wprowadź najpierw polecenie", - "repeater_cliCommandFrameTitle": "Określony Wyraz Polecenia CLI", + "repeater_cliCommandFrameTitle": "Ramka polecenia CLI", "repeater_cliCommandError": "Błąd: {error}", "@repeater_cliCommandError": { "placeholders": { @@ -1110,12 +1113,12 @@ } } }, - "repeater_cliQuickGetName": "Pobierz imię", - "repeater_cliQuickGetRadio": "Uzyskaj Radio", + "repeater_cliQuickGetName": "Pobierz nazwę", + "repeater_cliQuickGetRadio": "Pobierz radio", "repeater_cliQuickGetTx": "Pobierz TX", "repeater_cliQuickNeighbors": "Sąsiedzi", "repeater_cliQuickVersion": "Wersja", - "repeater_cliQuickAdvertise": "Reklama", + "repeater_cliQuickAdvertise": "Rozgłoś", "repeater_cliQuickClock": "Godzina", "repeater_cliHelpAdvert": "Wysyła pakiet rozgłoszeniowy", "repeater_cliHelpReboot": "Zresetuj urządzenie. (Uwaga, może pojawić się 'Timeout', co jest normalne)", @@ -1127,9 +1130,9 @@ "repeater_cliHelpSetTx": "Ustawia moc transmisji LoRa w dBm. (zrestartuj, aby zastosować)", "repeater_cliHelpSetRepeat": "Włącza lub wyłącza rolę powtarzacza dla tego węzła.", "repeater_cliHelpSetAllowReadOnly": "(Serwer pokoju) Jeśli 'włączone', to logowanie z pustym hasłem będzie dozwolone, ale nie można publikować w pokoju (tylko czytać).", - "repeater_cliHelpSetFloodMax": "Ustawia maksymalną liczbę skoków pakietu powrotnego (jeśli >= max, pakiet nie jest przekierowywany)", + "repeater_cliHelpSetFloodMax": "Ustawia maksymalną liczbę skoków pakietu zalewowego (jeśli >= max, pakiet nie jest przekierowywany)", "repeater_cliHelpSetIntThresh": "Ustawia Próg Interferencji (w dB). Domyślnie wynosi 14. Ustaw na 0, aby wyłączyć wykrywanie zakłóceń kanału.", - "repeater_cliHelpSetAgcResetInterval": "Ustawia interwał do zresetowania Automatycznego Sterownika Głośności. Ustaw na 0, aby wyłączyć.", + "repeater_cliHelpSetAgcResetInterval": "Ustawia interwał do zresetowania automatycznego wzmocnienia (AGC). Ustaw na 0, aby wyłączyć.", "repeater_cliHelpSetMultiAcks": "Włącza lub wyłącza funkcję 'podwójnych potwierdzeń'.", "repeater_cliHelpSetAdvertInterval": "Ustawia interwał timera w minutach do wysyłania pakietu rozgłoszenia lokalnego (bezpośredniego). Ustaw na 0, aby wyłączyć.", "repeater_cliHelpSetFloodAdvertInterval": "Ustawia interwał timera w godzinach do wysłania pakietu rozgłoszeniowego typu \"flood\". Ustaw na 0, aby wyłączyć.", @@ -1149,19 +1152,19 @@ "repeater_cliHelpSetAdcMultiplier": "Ustawia niestandardowy współczynnik do korekty zgłaszanego napięcia baterii (obsługa tylko na wybranych płytach).", "repeater_cliHelpTempRadio": "Ustawia tymczasowe parametry radia na podany czas trwania w minutach, a następnie powraca do oryginalnych parametrów radia. (nie zapisuje zmian w preferencjach).", "repeater_cliHelpSetPerm": "Modyfikuje ACL. Usuwa dopasowaną wpis (z prefiksem pubkey), jeśli \"permissions\" wynosi zero. Dodaje nowy wpis, jeśli pubkey-hex ma pełną długość i nie znajduje się obecnie w ACL. Aktualizuje wpis, dopasowując prefiks pubkey. Bit uprawnień zależy od roli firmware, ale dolne 2 bity to: 0 (Gość), 1 (tylko odczyt), 2 (odczyt i zapis), 3 (administrator).", - "repeater_cliHelpGetBridgeType": "Uzyskano typ mostu: brak, rs232, espnow", + "repeater_cliHelpGetBridgeType": "Pobiera typ mostka: brak, rs232, espnow", "repeater_cliHelpLogStart": "Rozpoczyna się logowanie pakietów do systemu plików.", "repeater_cliHelpLogStop": "Zatrzymuje logowanie pakietów do systemu plików.", "repeater_cliHelpLogErase": "Usuwa logi pakietów z systemu plików.", "repeater_cliHelpNeighbors": "Wyświetla listę innych węzłów powtarzających się, które usłyszano dzięki rozgłoszeniom zero-hop. Każda linia to: id-prefix-hex:timestamp:snr-times-4", "repeater_cliHelpNeighborRemove": "Usuwa pierwszy pasujący wpis (z prefiksem pubkey (hex)) z listy sąsiadów.", - "repeater_cliHelpRegion": "(tylko seria) Wyświetla wszystkie zdefiniowane regiony i aktualne uprawnienia do powodzi.", - "repeater_cliHelpRegionLoad": "ZAPOMNIJ: to jest specjalne wywołanie wielokomendowe. Każda następna komenda jest nazwą regionu (wcięta spacjami, aby wskazywać hierarchię nadrzędną, z minimum jedną spacją). Zakończona wysłaniem pustej linii/komendy.", + "repeater_cliHelpRegion": "(tylko port szeregowy) Wyświetla wszystkie zdefiniowane regiony i aktualne uprawnienia do zalewu.", + "repeater_cliHelpRegionLoad": "UWAGA: to jest specjalne wywołanie wielokomendowe. Każda następna komenda jest nazwą regionu (wcięta spacjami, aby wskazywać hierarchię nadrzędną, z minimum jedną spacją). Zakończona wysłaniem pustej linii/komendy.", "repeater_cliHelpRegionGet": "Wyszukuje region o podanej nazwie prefiksu (lub \"\" dla zakresu globalnego). Odpowiada \"-> region-name (parent-name) 'F'\"", "repeater_cliHelpRegionPut": "Dodaje lub aktualizuje definicję regionu z podaną nazwą.", "repeater_cliHelpRegionRemove": "Usuwa definicję regionu o podanej nazwie. (musi się dokładnie zgadzać i nie może mieć podregionów).", - "repeater_cliHelpRegionAllowf": "Ustawia uprawnienia 'P'łytkowe dla podanego regionu. ('' dla zakresu globalnego/starszego)", - "repeater_cliHelpRegionDenyf": "Usuwa uprawnienie 'Pływające' dla podanej strefy. (ZALECANE: na tym etapie NIE zaleca się używania tego na globalnym/starszym zakresie!!).", + "repeater_cliHelpRegionAllowf": "Ustawia uprawnienia 'F' (zalewowe) dla podanego regionu. ('' dla zakresu globalnego/starszego)", + "repeater_cliHelpRegionDenyf": "Usuwa uprawnienie 'F' (zalewowe) dla podanej strefy. (ZALECANE: na tym etapie NIE zaleca się używania tego na globalnym/starszym zakresie!!).", "repeater_cliHelpRegionHome": "Odpowiada z aktualnej 'home' region. (Uwaga: nie zostało jeszcze zastosowane, zarezerwowane na przyszłość).", "repeater_cliHelpRegionHomeSet": "Ustawia region 'domowe'.", "repeater_cliHelpRegionSave": "Zapisuje listę/mapę regionów do pamięci.", @@ -1172,7 +1175,7 @@ "repeater_cliHelpGpsAdvert": "Udostępnia konfigurację rozgłoszeń lokalizacji węzła:\n- brak: nie uwzględniaj lokalizacji w rozgłoszeniach\n- udostępnia: udostępnia lokalizację GPS (z SensorManager)\n- ustawienia: rozgłaszaj lokalizację przechowywaną w ustawieniach", "repeater_cliHelpGpsAdvertSet": "Ustawia konfigurację rozgłoszeń lokalizacji.", "repeater_commandsListTitle": "Lista poleceń", - "repeater_commandsListNote": "ZAPAMIĘTAJ: dla różnych poleceń \"set ...\" istnieje również polecenie \"get ...\".", + "repeater_commandsListNote": "UWAGA: dla różnych poleceń \"set ...\" istnieje również polecenie \"get ...\".", "repeater_general": "Ogólne", "repeater_settingsCategory": "Ustawienia", "repeater_bridge": "Most", @@ -1182,9 +1185,9 @@ "repeater_regionNote": "Wprowadzono komendy regionalne w celu zarządzania definicjami i uprawnieniami regionów.", "repeater_gpsManagement": "Zarządzanie GPS", "repeater_gpsNote": "Polecenie GPS zostało wprowadzone w celu zarządzania tematami związanymi z lokalizacją.", - "telemetry_receivedData": "Otrzymano Dane Telemetrii", - "telemetry_requestTimeout": "Życzenie o danych telemetrycznych nie udało się.", - "telemetry_errorLoading": "Błąd podczas ładowania telemetry: {error}", + "telemetry_receivedData": "Odebrane dane telemetrii", + "telemetry_requestTimeout": "Przekroczono czas oczekiwania na telemetrię.", + "telemetry_errorLoading": "Błąd podczas ładowania telemetrii: {error}", "@telemetry_errorLoading": { "placeholders": { "error": { @@ -1205,7 +1208,7 @@ "telemetry_voltageLabel": "Napięcie", "telemetry_mcuTemperatureLabel": "Temperatura MCU", "telemetry_temperatureLabel": "Temperatura", - "telemetry_currentLabel": "Obecny", + "telemetry_currentLabel": "Prąd", "telemetry_batteryValue": "{percent}% / {volts}V", "@telemetry_batteryValue": { "placeholders": { @@ -1217,7 +1220,7 @@ } } }, - "telemetry_voltageValue": "{volts}W", + "telemetry_voltageValue": "{volts}V", "@telemetry_voltageValue": { "placeholders": { "volts": { @@ -1246,8 +1249,8 @@ }, "channelPath_title": "Ścieżka pakietu", "channelPath_viewMap": "Wyświetl mapę", - "channelPath_otherObservedPaths": "Inne Zauważone Ścieżki", - "channelPath_repeaterHops": "Skoki Powtórki", + "channelPath_otherObservedPaths": "Inne zaobserwowane ścieżki", + "channelPath_repeaterHops": "Skoki powtarzaczy", "channelPath_noHopDetails": "Szczegóły dotyczące tego pakietu nie zostały podane.", "channelPath_messageDetails": "Szczegóły wiadomości", "channelPath_senderLabel": "Nadawca", @@ -1255,7 +1258,7 @@ "channelPath_repeatsLabel": "Powtórzenia", "channelPath_pathLabel": "Ścieżka {index}", "channelPath_observedLabel": "Obserwowane", - "channelPath_observedPathTitle": "Obserwowany ścieżka {index} • {hops}", + "channelPath_observedPathTitle": "Obserwowana ścieżka {index} • {hops}", "@channelPath_observedPathTitle": { "placeholders": { "index": { @@ -1312,7 +1315,7 @@ } }, "channelPath_mapTitle": "Mapa ścieżek", - "channelPath_noRepeaterLocations": "Brak dostępnych lokalizacji powtarzaczy dla tego ścieżki.", + "channelPath_noRepeaterLocations": "Brak dostępnych lokalizacji powtarzaczy dla tej ścieżki.", "channelPath_primaryPath": "Ścieżka {index} (Główna)", "@channelPath_primaryPath": { "placeholders": { From 35e296f1cd2396aac97b432ad8cc2a3b71a18d95 Mon Sep 17 00:00:00 2001 From: thesebas Date: Wed, 18 Feb 2026 20:02:20 +0100 Subject: [PATCH 13/25] Fix rebase merge error --- lib/l10n/app_localizations_pl.dart | 10 ++++++++++ lib/l10n/app_pl.arb | 13 +++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index f2dcda2..33737d3 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -1622,6 +1622,16 @@ class AppLocalizationsPl extends AppLocalizations { @override String get map_showSharedMarkers => 'Pokaż udostępnione znaczniki.'; + @override + String get map_showGuessedLocations => + 'Pokaż przypuszczalne lokalizacje węzłów'; + + @override + String get map_showDiscoveryContacts => 'Pokaż odkryte kontakty'; + + @override + String get map_guessedLocation => 'Przypuszczalna lokalizacja'; + @override String get map_lastSeenTime => 'Ostatni raz widziany'; diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 5c66b65..9704a30 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -139,9 +139,6 @@ "settings_infoContactsCount": "Liczba kontaktów", "settings_infoChannelCount": "Liczba kanałów", "settings_presets": "Presety", - "settings_preset915Mhz": "915 MHz", - "settings_preset868Mhz": "868 MHz", - "settings_preset433Mhz": "433 MHz", "settings_frequency": "Częstotliwość (MHz)", "settings_frequencyHelper": "300,0 - 2500,0", "settings_frequencyInvalid": "Nieprawidłowa częstotliwość (300-2500 MHz)", @@ -1813,7 +1810,7 @@ "contactsSettings_autoAddRepeatersSubtitle": "Zezwól na automatyczne dodawanie odkrytych repeaterów przez towarzysza.", "contactsSettings_autoAddRoomServersTitle": "Automatycznie dodaj serwery pokojowe", "contactsSettings_autoAddUsersTitle": "Automatycznie dodaj użytkowników", - "settings_contactSettings": "Ustawienia kontaktowe", + "settings_contactSettings": "Ustawienia kontaktów", "contactsSettings_otherTitle": "Inne ustawienia związane z kontaktami", "contactsSettings_autoAddTitle": "Automatyczne odnajdywanie", "contactsSettings_autoAddRoomServersSubtitle": "Zezwól towarzyszowi na automatyczne dodawanie znalezionych serwerów pokojowych.", @@ -1831,8 +1828,8 @@ "common_deleteAll": "Usuń wszystko", "discoveredContacts_deleteContactAllContent": "Czy na pewno chcesz usunąć wszystkie znalezione kontakty?", "discoveredContacts_deleteContactAll": "Usuń wszystkie odkryte kontakty", - "map_guessedLocation": "Wydana lokalizacja", - "map_showGuessedLocations": "Wyświetl lokalizacje zgadanych węzłów", + "map_guessedLocation": "Przypuszczalna lokalizacja", + "map_showGuessedLocations": "Pokaż przypuszczalne lokalizacje węzłów", "usbScreenSubtitle": "Wybierz wykryty urządzenie szeregowe i podłącz je bezpośrednio do swojego węzła MeshCore.", "usbScreenTitle": "Połącz przez USB", "connectionChoiceUsbLabel": "USB", @@ -1891,8 +1888,8 @@ "tcpErrorUnsupported": "Transport protokoł TCP nie jest obsługiwany na tym urządzeniu.", "tcpErrorTimedOut": "Połączenie TCP zakończyło się bez powodzenia.", "tcpConnectionFailed": "Błąd połączenia TCP: {error}", - "map_showDiscoveryContacts": "Pokaż kontakty odkrywania", - "map_setAsMyLocation": "Ustaw jako moje lokalizację", + "map_showDiscoveryContacts": "Pokaż odkryte kontakty", + "map_setAsMyLocation": "Ustaw jako moją lokalizację", "@path_routeWeight": { "placeholders": { "weight": { From 53cd3f4461a179269e043c71df31dd67a6606cb8 Mon Sep 17 00:00:00 2001 From: thesebas Date: Mon, 16 Mar 2026 15:36:57 +0100 Subject: [PATCH 14/25] Some additional label adjustments --- lib/l10n/app_localizations_pl.dart | 69 +++++++++++++++--------------- lib/l10n/app_pl.arb | 68 ++++++++++++++--------------- 2 files changed, 69 insertions(+), 68 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 33737d3..7b2183a 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -152,7 +152,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get tcpErrorUnsupported => - 'Transport protokoł TCP nie jest obsługiwany na tym urządzeniu.'; + 'Transport TCP nie jest obsługiwany na tej platformie.'; @override String get tcpErrorTimedOut => @@ -168,7 +168,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get usbScreenSubtitle => - 'Wybierz wykryty urządzenie szeregowe i podłącz je bezpośrednio do swojego węzła MeshCore.'; + 'Wybierz wykryte urządzenie szeregowe i połącz się bezpośrednio ze swoim węzłem MeshCore.'; @override String get usbScreenStatus => 'Wybierz urządzenie USB'; @@ -469,7 +469,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get settings_aboutDescription => - 'Otwarty kod źródłowy klient Flutter dla urządzeń do sieci mesh LoRa MeshCore.'; + 'Otwartoźródłowy klient Flutter dla urządzeń MeshCore LoRa do sieci mesh.'; @override String get settings_aboutOpenMeteoAttribution => @@ -800,7 +800,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get appSettings_mapTimeFilter => 'Filtrowanie Czasu Mapy'; + String get appSettings_mapTimeFilter => 'Filtr czasu mapy'; @override String get appSettings_showNodesDiscoveredWithin => 'Pokaż węzły odkryte w:'; @@ -833,7 +833,7 @@ class AppLocalizationsPl extends AppLocalizations { String get appSettings_unitsImperial => 'Imperialne (ft / mi)'; @override - String get appSettings_noAreaSelected => 'Nie zaznaczono żadnej powierzchni.'; + String get appSettings_noAreaSelected => 'Nie wybrano żadnego obszaru.'; @override String appSettings_areaSelectedZoom(int minZoom, int maxZoom) { @@ -848,7 +848,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get appSettings_appDebugLoggingSubtitle => - 'Loguj wiadomości debugowania aplikacji w celu rozwiązywania problemów.'; + 'Rejestruj komunikaty debugowania aplikacji w celu diagnozowania problemów.'; @override String get appSettings_appDebugLoggingEnabled => @@ -911,7 +911,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String contacts_removeConfirm(String contactName) { - return 'Usuń $contactName z kontaktów?'; + return 'Usunąć $contactName z kontaktów?'; } @override @@ -1009,7 +1009,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get channels_hashtagChannel => 'Kanał z hashtagami'; + String get channels_hashtagChannel => 'Kanał hashtagów'; @override String get channels_public => 'Publiczny'; @@ -1075,7 +1075,8 @@ class AppLocalizationsPl extends AppLocalizations { String get channels_enterChannelName => 'Proszę podać nazwę kanału.'; @override - String get channels_pskMustBe32Hex => 'PSK musi mieć 32 znaki szesnastkowe.'; + String get channels_pskMustBe32Hex => + 'PSK musi składać się z 32 znaków szesnastkowych.'; @override String channels_channelAdded(String name) { @@ -1280,7 +1281,7 @@ class AppLocalizationsPl extends AppLocalizations { String get debugLog_rawLogRx => 'Surowy log RX'; @override - String get debugLog_noBleActivity => 'Brak aktywności BLE jeszcze.'; + String get debugLog_noBleActivity => 'Brak aktywności BLE.'; @override String debugFrame_length(int count) { @@ -1302,7 +1303,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String debugFrame_timestamp(int timestamp) { - return '- Timestamp: $timestamp'; + return '- Znacznik czasu: $timestamp'; } @override @@ -1379,7 +1380,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get chat_noPathHistoryYet => - 'Brak jeszcze historii ścieżek.\nWyślij wiadomość, aby odkryć ścieżki.'; + 'Brak historii ścieżek.\nWyślij wiadomość, aby odkryć ścieżki.'; @override String get chat_pathActions => 'Działania ścieżki:'; @@ -1407,7 +1408,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get chat_floodModeEnabled => - 'Tryb powodziowy włączony. Włącz ponownie za pomocą ikony routingu w pasku narzędzi.'; + 'Tryb zalewowy włączony. Przełącz z powrotem ikoną routingu w pasku aplikacji.'; @override String get chat_fullPath => 'Pełna ścieżka'; @@ -1523,7 +1524,7 @@ class AppLocalizationsPl extends AppLocalizations { String get map_chat => 'Rozmowa'; @override - String get map_repeater => 'Powtórzacz'; + String get map_repeater => 'Powtarzacz'; @override String get map_room => 'Pokój'; @@ -1657,22 +1658,22 @@ class AppLocalizationsPl extends AppLocalizations { String get map_pathTraceCancelled => 'Śledzenie ścieżki anulowano.'; @override - String get mapCache_title => 'Bufor Map Offline'; + String get mapCache_title => 'Pamięć podręczna map offline'; @override String get mapCache_selectAreaFirst => - 'Wybierz obszar do wstępnego pobrania.'; + 'Najpierw wybierz obszar do zapisania w pamięci podręcznej.'; @override String get mapCache_noTilesToDownload => - 'Brak dostępnych płytek do pobrania dla tego obszaru.'; + 'Brak kafelków do pobrania dla tego obszaru.'; @override - String get mapCache_downloadTilesTitle => 'Pobierz płytki'; + String get mapCache_downloadTilesTitle => 'Pobierz kafelki'; @override String mapCache_downloadTilesPrompt(int count) { - return 'Pobierz $count płytek do użytku offline?'; + return 'Pobrać $count kafelków do użytku offline?'; } @override @@ -1680,12 +1681,12 @@ class AppLocalizationsPl extends AppLocalizations { @override String mapCache_cachedTiles(int count) { - return 'Zapisano $count płytek w pamięci podręcznej'; + return 'Zapisano w pamięci podręcznej $count kafelków'; } @override String mapCache_cachedTilesWithFailed(int downloaded, int failed) { - return 'Zapisano $downloaded płytek ($failed nieudane)'; + return 'Zapisano w pamięci podręcznej $downloaded kafelków ($failed nieudanych)'; } @override @@ -1694,14 +1695,14 @@ class AppLocalizationsPl extends AppLocalizations { @override String get mapCache_clearOfflineCachePrompt => - 'Usuń wszystkie tymczasowe kafelki mapy?'; + 'Usunąć wszystkie zapisane kafelki mapy?'; @override String get mapCache_offlineCacheCleared => - 'Pamięć podręczna offline została wyczyszczona'; + 'Wyczyszczono pamięć podręczną offline'; @override - String get mapCache_noAreaSelected => 'Nie zaznaczono żadnej powierzchni.'; + String get mapCache_noAreaSelected => 'Nie wybrano żadnego obszaru.'; @override String get mapCache_cacheArea => 'Obszar pamięci podręcznej'; @@ -1710,11 +1711,11 @@ class AppLocalizationsPl extends AppLocalizations { String get mapCache_useCurrentView => 'Użyj aktualnego widoku'; @override - String get mapCache_zoomRange => 'Zakres powiększenia'; + String get mapCache_zoomRange => 'Zakres przybliżenia'; @override String mapCache_estimatedTiles(int count) { - return 'Szacunkowa liczba płytek: $count'; + return 'Szacowana liczba kafelków: $count'; } @override @@ -1819,7 +1820,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get login_repeaterDescription => - 'Wprowadź hasło do powtarzacza, aby uzyskać dostęp do ustawień i statusu.'; + 'Wprowadź hasło do powtarzacza, aby uzyskać dostęp do ustawień i stanu.'; @override String get login_roomDescription => @@ -1855,7 +1856,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get login_failedMessage => - 'Logowanie nie powiodło się. Hasło jest nieprawidłowe albo repeater jest nieosiągalny.'; + 'Logowanie nie powiodło się. Hasło jest nieprawidłowe albo powtarzacz jest nieosiągalny.'; @override String get common_reload => 'Odśwież'; @@ -1907,7 +1908,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get path_noRepeatersFound => - 'Nie znaleziono repeaterów ani serwerów pokoi.'; + 'Nie znaleziono powtarzaczy ani serwerów pokoi.'; @override String get path_customPathsRequire => @@ -2080,7 +2081,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_basicSettings => 'Podstawowe Ustawienia'; @override - String get repeater_repeaterName => 'Nazwa Powtórnika'; + String get repeater_repeaterName => 'Nazwa powtarzacza'; @override String get repeater_repeaterNameHelper => 'Wyświetl nazwę tego powtarzacza'; @@ -2404,7 +2405,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpSetGuestPassword => - 'Ustawia/aktualizuje hasło gościa. (dla repeaterów, loginy gości mogą wysyłać żądanie \"Get Stats\")'; + 'Ustawia/aktualizuje hasło gościa. (dla powtarzaczy loginy gości mogą wysyłać żądanie \"Get Stats\")'; @override String get repeater_cliHelpSetName => 'Ustawia nazwę rozgłoszenia.'; @@ -3152,10 +3153,10 @@ class AppLocalizationsPl extends AppLocalizations { String get contacts_ping => 'Pingować'; @override - String get contacts_repeaterPathTrace => 'Śledzenie ścieżki do repeatera'; + String get contacts_repeaterPathTrace => 'Śledzenie ścieżki do powtarzacza'; @override - String get contacts_repeaterPing => 'Repeater pingowy'; + String get contacts_repeaterPing => 'Ping powtarzacza'; @override String get contacts_roomPathTrace => @@ -3353,7 +3354,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get contactsSettings_autoAddRepeatersSubtitle => - 'Zezwól na automatyczne dodawanie odkrytych repeaterów przez towarzysza.'; + 'Zezwól towarzyszowi na automatyczne dodawanie odkrytych powtarzaczy.'; @override String get contactsSettings_autoAddRoomServersTitle => diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 9704a30..3911ace 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -130,7 +130,7 @@ } }, "settings_aboutLegalese": "Projekt MeshCore Open Source 2026", - "settings_aboutDescription": "Otwarty kod źródłowy klient Flutter dla urządzeń do sieci mesh LoRa MeshCore.", + "settings_aboutDescription": "Otwartoźródłowy klient Flutter dla urządzeń MeshCore LoRa do sieci mesh.", "settings_infoName": "Nazwa", "settings_infoId": "ID", "settings_infoStatus": "Status", @@ -229,7 +229,7 @@ } } }, - "appSettings_mapTimeFilter": "Filtrowanie Czasu Mapy", + "appSettings_mapTimeFilter": "Filtr czasu mapy", "appSettings_showNodesDiscoveredWithin": "Pokaż węzły odkryte w:", "appSettings_allTime": "Cały czas", "appSettings_lastHour": "Ostatnia godzina", @@ -237,7 +237,7 @@ "appSettings_last24Hours": "Ostatnie 24 godziny", "appSettings_lastWeek": "Ostatni tydzień", "appSettings_offlineMapCache": "Pamięć podręczna map offline", - "appSettings_noAreaSelected": "Nie zaznaczono żadnej powierzchni.", + "appSettings_noAreaSelected": "Nie wybrano żadnego obszaru.", "appSettings_areaSelectedZoom": "Wybrany obszar (skala {minZoom}-{maxZoom})", "@appSettings_areaSelectedZoom": { "placeholders": { @@ -251,7 +251,7 @@ }, "appSettings_debugCard": "Debug", "appSettings_appDebugLogging": "Logowanie Debugowania Aplikacji", - "appSettings_appDebugLoggingSubtitle": "Loguj wiadomości debugowania aplikacji w celu rozwiązywania problemów.", + "appSettings_appDebugLoggingSubtitle": "Rejestruj komunikaty debugowania aplikacji w celu diagnozowania problemów.", "appSettings_appDebugLoggingEnabled": "Logowanie debugowania aplikacji włączone", "appSettings_appDebugLoggingDisabled": "Logowanie debugowania aplikacji wyłączone.", "contacts_title": "Kontakty", @@ -261,7 +261,7 @@ "contacts_noUnreadContacts": "Brak nieprzeczytanych kontaktów", "contacts_noContactsFound": "Brak znalezionych kontaktów ani grup.", "contacts_deleteContact": "Usuń Kontakt", - "contacts_removeConfirm": "Usuń {contactName} z kontaktów?", + "contacts_removeConfirm": "Usunąć {contactName} z kontaktów?", "@contacts_removeConfirm": { "placeholders": { "contactName": { @@ -337,7 +337,7 @@ } } }, - "channels_hashtagChannel": "Kanał z hashtagami", + "channels_hashtagChannel": "Kanał hashtagów", "channels_public": "Publiczny", "channels_private": "Prywatny", "channels_publicChannel": "Kanał publiczny", @@ -370,7 +370,7 @@ "channels_pskHex": "PSK (Hex)", "channels_generateRandomPsk": "Wygeneruj losowy klucz PSK", "channels_enterChannelName": "Proszę podać nazwę kanału.", - "channels_pskMustBe32Hex": "PSK musi mieć 32 znaki szesnastkowe.", + "channels_pskMustBe32Hex": "PSK musi składać się z 32 znaków szesnastkowych.", "channels_channelAdded": "Kanał \"{name}\" dodany", "@channels_channelAdded": { "placeholders": { @@ -478,7 +478,7 @@ "debugLog_enableInSettings": "Włącz logowanie debugowania aplikacji w ustawieniach", "debugLog_frames": "Ramki", "debugLog_rawLogRx": "Surowy log RX", - "debugLog_noBleActivity": "Brak aktywności BLE jeszcze.", + "debugLog_noBleActivity": "Brak aktywności BLE.", "debugFrame_length": "Długość ramy: {count} bajtów", "@debugFrame_length": { "placeholders": { @@ -504,7 +504,7 @@ } } }, - "debugFrame_timestamp": "- Timestamp: {timestamp}", + "debugFrame_timestamp": "- Znacznik czasu: {timestamp}", "@debugFrame_timestamp": { "placeholders": { "timestamp": { @@ -560,7 +560,7 @@ }, "chat_successes": "Sukcesy", "chat_removePath": "Usuń ścieżkę", - "chat_noPathHistoryYet": "Brak jeszcze historii ścieżek.\nWyślij wiadomość, aby odkryć ścieżki.", + "chat_noPathHistoryYet": "Brak historii ścieżek.\nWyślij wiadomość, aby odkryć ścieżki.", "chat_pathActions": "Działania ścieżki:", "chat_setCustomPath": "Ustaw ścieżkę niestandardową", "chat_setCustomPathSubtitle": "Ręcznie określ trasę.", @@ -568,7 +568,7 @@ "chat_clearPathSubtitle": "Wymuś ponowne wyznaczenie trasy przy następnym wysłaniu", "chat_pathCleared": "Ścieżka wyczyszczona. Następna wiadomość odnajdzie trasę.", "chat_floodModeSubtitle": "Użyj przełącznika routingu w pasku narzędzi.", - "chat_floodModeEnabled": "Tryb powodziowy włączony. Włącz ponownie za pomocą ikony routingu w pasku narzędzi.", + "chat_floodModeEnabled": "Tryb zalewowy włączony. Przełącz z powrotem ikoną routingu w pasku aplikacji.", "chat_fullPath": "Pełna ścieżka", "chat_pathDetailsNotAvailable": "Szczegóły ścieżki jeszcze niedostępne. Spróbuj wysłać wiadomość, aby odświeżyć.", "chat_pathSetHops": "Ścieżka ustawiona: {hopCount} {hopCount, plural, =1{hop} other{hops}} - {status}", @@ -642,7 +642,7 @@ } }, "map_chat": "Rozmowa", - "map_repeater": "Powtórzacz", + "map_repeater": "Powtarzacz", "map_room": "Pokój", "map_sensor": "Czujnik", "map_pinDm": "Pinezka (DM)", @@ -684,11 +684,11 @@ "map_sharedPin": "Udostępniona pinezka", "map_joinRoom": "Dołącz do pokoju", "map_manageRepeater": "Zarządzaj powtarzaczem", - "mapCache_title": "Bufor Map Offline", - "mapCache_selectAreaFirst": "Wybierz obszar do wstępnego pobrania.", - "mapCache_noTilesToDownload": "Brak dostępnych płytek do pobrania dla tego obszaru.", - "mapCache_downloadTilesTitle": "Pobierz płytki", - "mapCache_downloadTilesPrompt": "Pobierz {count} płytek do użytku offline?", + "mapCache_title": "Pamięć podręczna map offline", + "mapCache_selectAreaFirst": "Najpierw wybierz obszar do zapisania w pamięci podręcznej.", + "mapCache_noTilesToDownload": "Brak kafelków do pobrania dla tego obszaru.", + "mapCache_downloadTilesTitle": "Pobierz kafelki", + "mapCache_downloadTilesPrompt": "Pobrać {count} kafelków do użytku offline?", "@mapCache_downloadTilesPrompt": { "placeholders": { "count": { @@ -697,7 +697,7 @@ } }, "mapCache_downloadAction": "Pobierz", - "mapCache_cachedTiles": "Zapisano {count} płytek w pamięci podręcznej", + "mapCache_cachedTiles": "Zapisano w pamięci podręcznej {count} kafelków", "@mapCache_cachedTiles": { "placeholders": { "count": { @@ -705,7 +705,7 @@ } } }, - "mapCache_cachedTilesWithFailed": "Zapisano {downloaded} płytek ({failed} nieudane)", + "mapCache_cachedTilesWithFailed": "Zapisano w pamięci podręcznej {downloaded} kafelków ({failed} nieudanych)", "@mapCache_cachedTilesWithFailed": { "placeholders": { "downloaded": { @@ -717,13 +717,13 @@ } }, "mapCache_clearOfflineCacheTitle": "Wyczyść pamięć podręczną offline", - "mapCache_clearOfflineCachePrompt": "Usuń wszystkie tymczasowe kafelki mapy?", - "mapCache_offlineCacheCleared": "Pamięć podręczna offline została wyczyszczona", - "mapCache_noAreaSelected": "Nie zaznaczono żadnej powierzchni.", + "mapCache_clearOfflineCachePrompt": "Usunąć wszystkie zapisane kafelki mapy?", + "mapCache_offlineCacheCleared": "Wyczyszczono pamięć podręczną offline", + "mapCache_noAreaSelected": "Nie wybrano żadnego obszaru.", "mapCache_cacheArea": "Obszar pamięci podręcznej", "mapCache_useCurrentView": "Użyj aktualnego widoku", - "mapCache_zoomRange": "Zakres powiększenia", - "mapCache_estimatedTiles": "Szacunkowa liczba płytek: {count}", + "mapCache_zoomRange": "Zakres przybliżenia", + "mapCache_estimatedTiles": "Szacowana liczba kafelków: {count}", "@mapCache_estimatedTiles": { "placeholders": { "count": { @@ -812,7 +812,7 @@ "login_enterPassword": "Wprowadź hasło", "login_savePassword": "Zapisz hasło", "login_savePasswordSubtitle": "Hasło będzie bezpiecznie przechowywane na tym urządzeniu.", - "login_repeaterDescription": "Wprowadź hasło do powtarzacza, aby uzyskać dostęp do ustawień i statusu.", + "login_repeaterDescription": "Wprowadź hasło do powtarzacza, aby uzyskać dostęp do ustawień i stanu.", "login_roomDescription": "Wprowadź hasło do pokoju, aby uzyskać dostęp do ustawień i statusu.", "login_routing": "Trasowanie", "login_routingMode": "Tryb routingu", @@ -839,7 +839,7 @@ } } }, - "login_failedMessage": "Logowanie nie powiodło się. Hasło jest nieprawidłowe albo repeater jest nieosiągalny.", + "login_failedMessage": "Logowanie nie powiodło się. Hasło jest nieprawidłowe albo powtarzacz jest nieosiągalny.", "common_reload": "Odśwież", "common_clear": "Wyczyść", "path_currentPath": "Aktualna ścieżka: {path}", @@ -865,7 +865,7 @@ "path_labelHexPrefixes": "Ścieżka (prefiksy hex)", "path_helperMaxHops": "Maksymalnie 64 skoki. Każdy prefiks ma 2 znaki szesnastkowe (1 bajt).", "path_selectFromContacts": "Albo wybierz z kontaktów:", - "path_noRepeatersFound": "Nie znaleziono repeaterów ani serwerów pokoi.", + "path_noRepeatersFound": "Nie znaleziono powtarzaczy ani serwerów pokoi.", "path_customPathsRequire": "Dostosowane ścieżki wymagają pośrednich skoków, które mogą przekazywać wiadomości.", "path_invalidHexPrefixes": "Nieprawidłowe prefiksy szesnastkowe: {prefixes}", "@path_invalidHexPrefixes": { @@ -984,7 +984,7 @@ }, "repeater_settingsTitle": "Ustawienia powtarzacza", "repeater_basicSettings": "Podstawowe Ustawienia", - "repeater_repeaterName": "Nazwa Powtórnika", + "repeater_repeaterName": "Nazwa powtarzacza", "repeater_repeaterNameHelper": "Wyświetl nazwę tego powtarzacza", "repeater_adminPassword": "Hasło Administracyjne", "repeater_adminPasswordHelper": "Hasło z pełnym dostępem", @@ -1133,7 +1133,7 @@ "repeater_cliHelpSetMultiAcks": "Włącza lub wyłącza funkcję 'podwójnych potwierdzeń'.", "repeater_cliHelpSetAdvertInterval": "Ustawia interwał timera w minutach do wysyłania pakietu rozgłoszenia lokalnego (bezpośredniego). Ustaw na 0, aby wyłączyć.", "repeater_cliHelpSetFloodAdvertInterval": "Ustawia interwał timera w godzinach do wysłania pakietu rozgłoszeniowego typu \"flood\". Ustaw na 0, aby wyłączyć.", - "repeater_cliHelpSetGuestPassword": "Ustawia/aktualizuje hasło gościa. (dla repeaterów, loginy gości mogą wysyłać żądanie \"Get Stats\")", + "repeater_cliHelpSetGuestPassword": "Ustawia/aktualizuje hasło gościa. (dla powtarzaczy loginy gości mogą wysyłać żądanie \"Get Stats\")", "repeater_cliHelpSetName": "Ustawia nazwę rozgłoszenia.", "repeater_cliHelpSetLat": "Ustawia współrzędną geograficzną (w stopniach dziesiętnych) mapy rozgłoszeń.", "repeater_cliHelpSetLon": "Ustawia współrzędną długościową mapy rozgłoszeń. (stopnie dziesiętne)", @@ -1552,11 +1552,11 @@ "pathTrace_notAvailable": "Ścieżka śledzenia niedostępna.", "contacts_pathTrace": "Śledzenie Ścieżek", "contacts_ping": "Pingować", - "contacts_repeaterPathTrace": "Śledzenie ścieżki do repeatera", + "contacts_repeaterPathTrace": "Śledzenie ścieżki do powtarzacza", "contacts_roomPathTrace": "Śledzenie ścieżki do serwera pokojowego", "contacts_roomPing": "Pinguj serwer pokoju", "pathTrace_refreshTooltip": "Odśwież ścieżkę.", - "contacts_repeaterPing": "Repeater pingowy", + "contacts_repeaterPing": "Ping powtarzacza", "contacts_pathTraceTo": "Śledź trasę do {name}", "contacts_chatTraceRoute": "Śledź trasę promienia", "appSettings_languageRu": "Rosyjski", @@ -1807,7 +1807,7 @@ "settings_contactSettingsSubtitle": "Ustawienia dotyczące sposobu dodawania kontaktów", "contactsSettings_autoAddUsersSubtitle": "Pozwól towarzyszowi automatycznie dodawać znalezione użytkowników.", "contactsSettings_autoAddRepeatersTitle": "Automatyczne dodawanie powtarzalników", - "contactsSettings_autoAddRepeatersSubtitle": "Zezwól na automatyczne dodawanie odkrytych repeaterów przez towarzysza.", + "contactsSettings_autoAddRepeatersSubtitle": "Zezwól towarzyszowi na automatyczne dodawanie odkrytych powtarzaczy.", "contactsSettings_autoAddRoomServersTitle": "Automatycznie dodaj serwery pokojowe", "contactsSettings_autoAddUsersTitle": "Automatycznie dodaj użytkowników", "settings_contactSettings": "Ustawienia kontaktów", @@ -1830,7 +1830,7 @@ "discoveredContacts_deleteContactAll": "Usuń wszystkie odkryte kontakty", "map_guessedLocation": "Przypuszczalna lokalizacja", "map_showGuessedLocations": "Pokaż przypuszczalne lokalizacje węzłów", - "usbScreenSubtitle": "Wybierz wykryty urządzenie szeregowe i podłącz je bezpośrednio do swojego węzła MeshCore.", + "usbScreenSubtitle": "Wybierz wykryte urządzenie szeregowe i połącz się bezpośrednio ze swoim węzłem MeshCore.", "usbScreenTitle": "Połącz przez USB", "connectionChoiceUsbLabel": "USB", "connectionChoiceBluetoothLabel": "Bluetooth", @@ -1885,7 +1885,7 @@ "tcpStatus_connectingTo": "Połączenie z {endpoint}...", "tcpErrorHostRequired": "Wymagana jest adresa IP.", "tcpErrorPortInvalid": "Numer portu musi mieścić się w zakresie od 1 do 65535.", - "tcpErrorUnsupported": "Transport protokoł TCP nie jest obsługiwany na tym urządzeniu.", + "tcpErrorUnsupported": "Transport TCP nie jest obsługiwany na tej platformie.", "tcpErrorTimedOut": "Połączenie TCP zakończyło się bez powodzenia.", "tcpConnectionFailed": "Błąd połączenia TCP: {error}", "map_showDiscoveryContacts": "Pokaż odkryte kontakty", From dc57f9b9c0543d7bc611600e453b3925437b603e Mon Sep 17 00:00:00 2001 From: thesebas Date: Thu, 19 Mar 2026 09:56:00 +0100 Subject: [PATCH 15/25] fix missing labels --- lib/l10n/app_localizations_pl.dart | 2 +- lib/l10n/app_pl.arb | 40 +++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 7b2183a..44c9d1f 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -876,7 +876,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String contacts_searchContacts(int number, String str) { - return 'Wyszukaj kontakty...'; + return 'Wyszukaj $number$str kontakty...'; } @override diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 3911ace..dd96934 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -257,7 +257,17 @@ "contacts_title": "Kontakty", "contacts_noContacts": "Brak jeszcze kontaktów.", "contacts_contactsWillAppear": "Kontakty będą wyświetlane, gdy urządzenia nadają rozgłoszenia.", - "contacts_searchContacts": "Wyszukaj kontakty...", + "contacts_searchContacts": "Wyszukaj {number}{str} kontakty...", + "@contacts_searchContacts": { + "placeholders": { + "number": { + "type": "int" + }, + "str": { + "type": "String" + } + } + }, "contacts_noUnreadContacts": "Brak nieprzeczytanych kontaktów", "contacts_noContactsFound": "Brak znalezionych kontaktów ani grup.", "contacts_deleteContact": "Usuń Kontakt", @@ -1579,9 +1589,37 @@ "contacts_zeroHopContactAdvertFailed": "Nie udało się wysłać kontaktu.", "notification_activityTitle": "Aktywność MeshCore", "notification_messagesCount": "{count} {count, plural, =1{wiadomość} few{wiadomości} many{wiadomości} other{wiadomości}}", + "@notification_messagesCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, "notification_channelMessagesCount": "{count} {count, plural, =1{wiadomość kanału} few{wiadomości kanału} many{wiadomości kanału} other{wiadomości kanału}}", + "@notification_channelMessagesCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, "notification_newNodesCount": "{count} {count, plural, =1{nowy węzeł} few{nowe węzły} many{nowych węzłów} other{nowych węzłów}}", + "@notification_newNodesCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, "notification_newTypeDiscovered": "Nowy {contactType} wykryty", + "@notification_newTypeDiscovered": { + "placeholders": { + "contactType": { + "type": "String" + } + } + }, "notification_receivedNewMessage": "Otrzymano nową wiadomość", "settings_gpxExportContacts": "Eksportuj towarzyszy do GPX", "settings_gpxExportRepeaters": "Eksportuj powtórki / serwer pokojowy do GPX", From 5140ff383d6544298f12305ad87f760bdd06687a Mon Sep 17 00:00:00 2001 From: thesebas Date: Thu, 19 Mar 2026 10:03:16 +0100 Subject: [PATCH 16/25] fix plural form of the label --- lib/l10n/app_localizations_pl.dart | 10 +++++++++- lib/l10n/app_pl.arb | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 44c9d1f..0dd44af 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -876,7 +876,15 @@ class AppLocalizationsPl extends AppLocalizations { @override String contacts_searchContacts(int number, String str) { - return 'Wyszukaj $number$str kontakty...'; + String _temp0 = intl.Intl.pluralLogic( + number, + locale: localeName, + other: 'kontaktu', + many: 'kontaktów', + few: 'kontakty', + one: 'kontakt', + ); + return 'Wyszukaj $number$str $_temp0...'; } @override diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index dd96934..fb3b33e 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -257,7 +257,7 @@ "contacts_title": "Kontakty", "contacts_noContacts": "Brak jeszcze kontaktów.", "contacts_contactsWillAppear": "Kontakty będą wyświetlane, gdy urządzenia nadają rozgłoszenia.", - "contacts_searchContacts": "Wyszukaj {number}{str} kontakty...", + "contacts_searchContacts": "Wyszukaj {number}{str} {number, plural, one{kontakt} few{kontakty} many{kontaktów} other{kontaktu}}...", "@contacts_searchContacts": { "placeholders": { "number": { From bd030153c12da511f6f934c073c7f578f6d213ca Mon Sep 17 00:00:00 2001 From: thesebas Date: Sun, 22 Mar 2026 21:58:36 +0100 Subject: [PATCH 17/25] update new labels --- lib/l10n/app_localizations_pl.dart | 22 +++++++++++----------- lib/l10n/app_pl.arb | 20 ++++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 0dd44af..85c1767 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -698,42 +698,42 @@ class AppLocalizationsPl extends AppLocalizations { 'Automatyczne obracanie tras wyłączone'; @override - String get appSettings_maxRouteWeight => - 'Maksymalny dopuszczalny ciężar pojazdu'; + String get appSettings_maxRouteWeight => 'Maksymalna waga ścieżki'; @override String get appSettings_maxRouteWeightSubtitle => - 'Maksymalna waga, jaką ścieżka może zgromadzić dzięki udanym dostawom.'; + 'Maksymalna waga, jaką ścieżka może osiągnąć dzięki udanym dostarczeniom'; @override - String get appSettings_initialRouteWeight => 'Początkowa waga trasy'; + String get appSettings_initialRouteWeight => 'Początkowa waga ścieżki'; @override String get appSettings_initialRouteWeightSubtitle => - 'Początkowa waga dla nowych, odkrytych ścieżek'; + 'Waga początkowa dla nowo odkrytych ścieżek'; @override - String get appSettings_routeWeightSuccessIncrement => 'Wzrost wagi sukcesu'; + String get appSettings_routeWeightSuccessIncrement => + 'Przyrost wagi po sukcesie'; @override String get appSettings_routeWeightSuccessIncrementSubtitle => - 'Waga dodana do ścieżki po pomyślnym dostarczeniu'; + 'Waga dodawana do ścieżki po udanym dostarczeniu'; @override String get appSettings_routeWeightFailureDecrement => - 'Zmniejszenie wagi kary'; + 'Spadek wagi po niepowodzeniu'; @override String get appSettings_routeWeightFailureDecrementSubtitle => - 'Waga usunięta z trasy po nieudanej dostawie'; + 'Waga odejmowana od ścieżki po nieudanym dostarczeniu'; @override String get appSettings_maxMessageRetries => - 'Maksymalna liczba prób wysłania wiadomości'; + 'Maksymalna liczba ponowień wiadomości'; @override String get appSettings_maxMessageRetriesSubtitle => - 'Liczba prób ponownego wysłania wiadomości przed oznaczaniem jej jako nieudanej'; + 'Liczba prób ponowienia przed oznaczeniem wiadomości jako nieudanej'; @override String path_routeWeight(String weight, String max) { diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index fb3b33e..3ce551d 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -1938,15 +1938,15 @@ } } }, - "appSettings_initialRouteWeight": "Początkowa waga trasy", - "appSettings_maxRouteWeight": "Maksymalny dopuszczalny ciężar pojazdu", - "appSettings_initialRouteWeightSubtitle": "Początkowa waga dla nowych, odkrytych ścieżek", - "appSettings_maxRouteWeightSubtitle": "Maksymalna waga, jaką ścieżka może zgromadzić dzięki udanym dostawom.", - "appSettings_routeWeightSuccessIncrement": "Wzrost wagi sukcesu", - "appSettings_routeWeightSuccessIncrementSubtitle": "Waga dodana do ścieżki po pomyślnym dostarczeniu", - "appSettings_routeWeightFailureDecrement": "Zmniejszenie wagi kary", - "appSettings_routeWeightFailureDecrementSubtitle": "Waga usunięta z trasy po nieudanej dostawie", - "appSettings_maxMessageRetries": "Maksymalna liczba prób wysłania wiadomości", - "appSettings_maxMessageRetriesSubtitle": "Liczba prób ponownego wysłania wiadomości przed oznaczaniem jej jako nieudanej", + "appSettings_initialRouteWeight": "Początkowa waga ścieżki", + "appSettings_maxRouteWeight": "Maksymalna waga ścieżki", + "appSettings_initialRouteWeightSubtitle": "Waga początkowa dla nowo odkrytych ścieżek", + "appSettings_maxRouteWeightSubtitle": "Maksymalna waga, jaką ścieżka może osiągnąć dzięki udanym dostarczeniom", + "appSettings_routeWeightSuccessIncrement": "Przyrost wagi po sukcesie", + "appSettings_routeWeightSuccessIncrementSubtitle": "Waga dodawana do ścieżki po udanym dostarczeniu", + "appSettings_routeWeightFailureDecrement": "Spadek wagi po niepowodzeniu", + "appSettings_routeWeightFailureDecrementSubtitle": "Waga odejmowana od ścieżki po nieudanym dostarczeniu", + "appSettings_maxMessageRetries": "Maksymalna liczba ponowień wiadomości", + "appSettings_maxMessageRetriesSubtitle": "Liczba prób ponowienia przed oznaczeniem wiadomości jako nieudanej", "path_routeWeight": "{weight}/{max}" } From 630606acdcca1f0a71334590e54541deb6f696d1 Mon Sep 17 00:00:00 2001 From: Winston Lowe Date: Mon, 23 Mar 2026 08:14:46 -0700 Subject: [PATCH 18/25] Update byte skipping logic and improve clarity in MeshCoreConnector and ChannelMessage --- lib/connector/meshcore_connector.dart | 3 ++- lib/models/channel_message.dart | 2 +- lib/services/message_retry_service.dart | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 5dc660e..04e9583 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -4323,7 +4323,8 @@ class MeshCoreConnector extends ChangeNotifier { routeType == _routeTransportFlood || routeType == _routeTransportDirect; if (hasTransport) { - reader.skipBytes(2); // Skip reserved bytes in transport header + // Skip reserved bytes in transport header made up of two u16 fields + reader.skipBytes(4); } final pathLenRaw = reader.readByte(); final pathByteLen = _decodePathByteLen(pathLenRaw); diff --git a/lib/models/channel_message.dart b/lib/models/channel_message.dart index 98a8f1d..0f0a5b4 100644 --- a/lib/models/channel_message.dart +++ b/lib/models/channel_message.dart @@ -129,11 +129,11 @@ class ChannelMessage { if (code == respCodeChannelMsgRecvV3) { reader.skipBytes(1); // Skip SNR final flags = reader.readByte(); + final hasPath = (flags & 0x01) != 0; reader.skipBytes(1); // Skip reserved byte channelIdx = reader.readByte(); pathLen = reader.readByte(); txtType = reader.readByte(); - final hasPath = (flags & 0x01) != 0; if (hasPath && pathLen > 0) { reader.rewind(); // Rewind to read path length again for pathBytes pathBytes = reader.readBytes(pathLen); diff --git a/lib/services/message_retry_service.dart b/lib/services/message_retry_service.dart index 94f3caf..c8e89aa 100644 --- a/lib/services/message_retry_service.dart +++ b/lib/services/message_retry_service.dart @@ -678,7 +678,7 @@ class MessageRetryService extends ChangeNotifier { } } - String? getContactKeyForAckHash(Uint8List ackHash) { + String? getContactKeyForAckHash(int ackHash) { for (var entry in _pendingMessages.entries) { final message = entry.value; if (message.expectedAckHash != null && From 58252b8a40dcbd1e0377515f3c118309c0f7dcc4 Mon Sep 17 00:00:00 2001 From: Winston Lowe Date: Mon, 23 Mar 2026 10:14:30 -0700 Subject: [PATCH 19/25] fix: Correct return type of _manualAckHash and improve hash computation --- test/services/retry_and_protocol_test.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/services/retry_and_protocol_test.dart b/test/services/retry_and_protocol_test.dart index 48d4cfb..b6c53b6 100644 --- a/test/services/retry_and_protocol_test.dart +++ b/test/services/retry_and_protocol_test.dart @@ -14,7 +14,7 @@ import 'package:meshcore_open/services/message_retry_service.dart'; /// Replicates the SHA-256 computation from [MessageRetryService.computeExpectedAckHash] /// so tests can cross-check without calling the real implementation twice. -Uint8List _manualAckHash( +int _manualAckHash( int timestampSeconds, int attemptMasked, // already masked to 0x03 String text, @@ -35,7 +35,8 @@ Uint8List _manualAckHash( buffer.setRange(offset, offset + senderPubKey.length, senderPubKey); final hash = sha256.convert(buffer); - return Uint8List.fromList(hash.bytes.sublist(0, 4)); + final bytes = Uint8List.fromList(hash.bytes.sublist(0, 4)); + return (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; } Uint8List _makeKey(int seed) { From 7eff1df6e21c8e24c0738c07ccbc8b4c47babc09 Mon Sep 17 00:00:00 2001 From: thesebas Date: Mon, 23 Mar 2026 18:47:18 +0100 Subject: [PATCH 20/25] use correct word for repeater --- lib/l10n/app_localizations_pl.dart | 87 +++++++++++++++--------------- lib/l10n/app_pl.arb | 86 ++++++++++++++--------------- 2 files changed, 86 insertions(+), 87 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 85c1767..06efab3 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -768,11 +768,11 @@ class AppLocalizationsPl extends AppLocalizations { String get appSettings_mapDisplay => 'Wyświetlanie mapy'; @override - String get appSettings_showRepeaters => 'Pokaż powtarzacze'; + String get appSettings_showRepeaters => 'Pokaż przekaźniki'; @override String get appSettings_showRepeatersSubtitle => - 'Wyświetl węzły powtarzaczy na mapie'; + 'Wyświetl węzły przekaźników na mapie'; @override String get appSettings_showChatNodes => 'Pokaż Węzły Rozmowy'; @@ -899,7 +899,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String contacts_searchRepeaters(int number, String str) { - return 'Wyszukaj $number$str powtórników...'; + return 'Wyszukaj $number$str przekaźników...'; } @override @@ -923,7 +923,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get contacts_manageRepeater => 'Zarządzaj powtarzaczem'; + String get contacts_manageRepeater => 'Zarządzaj przekaźnikiem'; @override String get contacts_manageRoom => 'Zarządzaj Serwerem Pokoju'; @@ -1532,7 +1532,7 @@ class AppLocalizationsPl extends AppLocalizations { String get map_chat => 'Rozmowa'; @override - String get map_repeater => 'Powtarzacz'; + String get map_repeater => 'Przekaźnik'; @override String get map_room => 'Pokój'; @@ -1611,7 +1611,7 @@ class AppLocalizationsPl extends AppLocalizations { String get map_chatNodes => 'Węzły czatu'; @override - String get map_repeaters => 'Powtarzacze'; + String get map_repeaters => 'Przekaźniki'; @override String get map_otherNodes => 'Inne węzły'; @@ -1651,7 +1651,7 @@ class AppLocalizationsPl extends AppLocalizations { String get map_joinRoom => 'Dołącz do pokoju'; @override - String get map_manageRepeater => 'Zarządzaj powtarzaczem'; + String get map_manageRepeater => 'Zarządzaj przekaźnikiem'; @override String get map_tapToAdd => 'Kliknij na węzły, aby dodać je do ścieżki.'; @@ -1808,7 +1808,7 @@ class AppLocalizationsPl extends AppLocalizations { 'Czy na pewno chcesz się odłączyć od tego urządzenia?'; @override - String get login_repeaterLogin => 'Logowanie do powtarzacza'; + String get login_repeaterLogin => 'Logowanie do przekaźnika'; @override String get login_roomLogin => 'Logowanie do pokoju'; @@ -1828,7 +1828,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get login_repeaterDescription => - 'Wprowadź hasło do powtarzacza, aby uzyskać dostęp do ustawień i stanu.'; + 'Wprowadź hasło do przekaźnika, aby uzyskać dostęp do ustawień i stanu.'; @override String get login_roomDescription => @@ -1864,7 +1864,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get login_failedMessage => - 'Logowanie nie powiodło się. Hasło jest nieprawidłowe albo powtarzacz jest nieosiągalny.'; + 'Logowanie nie powiodło się. Hasło jest nieprawidłowe albo przekaźnik jest nieosiągalny.'; @override String get common_reload => 'Odśwież'; @@ -1916,7 +1916,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get path_noRepeatersFound => - 'Nie znaleziono powtarzaczy ani serwerów pokoi.'; + 'Nie znaleziono przekaźników ani serwerów pokoi.'; @override String get path_customPathsRequire => @@ -1935,7 +1935,7 @@ class AppLocalizationsPl extends AppLocalizations { String get path_setPath => 'Ustaw Ścieżkę'; @override - String get repeater_management => 'Zarządzanie powtarzaczami'; + String get repeater_management => 'Zarządzanie przekaźnikami'; @override String get room_management => 'Zarządzanie Serwerem Pokoju'; @@ -1948,7 +1948,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_statusSubtitle => - 'Wyświetl status powtarzacza, statystyki i sąsiadów.'; + 'Wyświetl status przekaźnika, statystyki i sąsiadów.'; @override String get repeater_telemetry => 'Telemetria'; @@ -1961,7 +1961,7 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_cli => 'CLI'; @override - String get repeater_cliSubtitle => 'Wyślij polecenia do powtarzacza'; + String get repeater_cliSubtitle => 'Wyślij polecenia do przekaźnika'; @override String get repeater_neighbors => 'Sąsiedzi'; @@ -1974,10 +1974,10 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_settings => 'Ustawienia'; @override - String get repeater_settingsSubtitle => 'Skonfiguruj parametry powtarzacza'; + String get repeater_settingsSubtitle => 'Skonfiguruj parametry przekaźnika'; @override - String get repeater_statusTitle => 'Status powtarzacza'; + String get repeater_statusTitle => 'Status przekaźnika'; @override String get repeater_routingMode => 'Tryb routingu'; @@ -2083,16 +2083,16 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get repeater_settingsTitle => 'Ustawienia powtarzacza'; + String get repeater_settingsTitle => 'Ustawienia przekaźnika'; @override String get repeater_basicSettings => 'Podstawowe Ustawienia'; @override - String get repeater_repeaterName => 'Nazwa powtarzacza'; + String get repeater_repeaterName => 'Nazwa przekaźnika'; @override - String get repeater_repeaterNameHelper => 'Wyświetl nazwę tego powtarzacza'; + String get repeater_repeaterNameHelper => 'Wyświetl nazwę tego przekaźnika'; @override String get repeater_adminPassword => 'Hasło Administracyjne'; @@ -2153,7 +2153,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_packetForwardingSubtitle => - 'Włącz powtarzacz, aby przekazywać pakiety.'; + 'Włącz przekaźnik, aby przekazywać pakiety.'; @override String get repeater_guestAccess => 'Dostęp dla gości'; @@ -2196,15 +2196,14 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_dangerZone => 'Strefa Zagrożeń'; @override - String get repeater_rebootRepeater => 'Zrestartuj Powtarzacz'; + String get repeater_rebootRepeater => 'Zrestartuj Przekaźnik'; @override - String get repeater_rebootRepeaterSubtitle => - 'Zrestartuj urządzenie powtarzające.'; + String get repeater_rebootRepeaterSubtitle => 'Zrestartuj przekaźnik.'; @override String get repeater_rebootRepeaterConfirm => - 'Czy na pewno chcesz zrestartować ten powtarzacz?'; + 'Czy na pewno chcesz zrestartować ten przekaźnik?'; @override String get repeater_regenerateIdentityKey => 'Wygeneruj klucz tożsamości'; @@ -2215,18 +2214,18 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_regenerateIdentityKeyConfirm => - 'Zostanie wygenerowana nowa tożsamość dla powtarzacza. Kontynuować?'; + 'Zostanie wygenerowana nowa tożsamość dla przekaźnika. Kontynuować?'; @override String get repeater_eraseFileSystem => 'Wyczyść System Plików'; @override String get repeater_eraseFileSystemSubtitle => - 'Sformatuj system plików powtarzacza'; + 'Sformatuj system plików przekaźnika'; @override String get repeater_eraseFileSystemConfirm => - 'OSTRZEŻENIE: To spowoduje usunięcie wszystkich danych z powtarzacza. Nie da się tego cofnąć!'; + 'OSTRZEŻENIE: To spowoduje usunięcie wszystkich danych z przekaźnika. Nie da się tego cofnąć!'; @override String get repeater_eraseSerialOnly => @@ -2290,7 +2289,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get repeater_cliTitle => 'Powtarzacz CLI'; + String get repeater_cliTitle => 'Przekaźnik CLI'; @override String get repeater_debugNextCommand => 'Debug Następną Komendę'; @@ -2381,7 +2380,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpSetRepeat => - 'Włącza lub wyłącza rolę powtarzacza dla tego węzła.'; + 'Włącza lub wyłącza rolę przekaźnika dla tego węzła.'; @override String get repeater_cliHelpSetAllowReadOnly => @@ -2413,7 +2412,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpSetGuestPassword => - 'Ustawia/aktualizuje hasło gościa. (dla powtarzaczy loginy gości mogą wysyłać żądanie \"Get Stats\")'; + 'Ustawia/aktualizuje hasło gościa. (dla przekaźników loginy gości mogą wysyłać żądanie \"Get Stats\")'; @override String get repeater_cliHelpSetName => 'Ustawia nazwę rozgłoszenia.'; @@ -2579,11 +2578,11 @@ class AppLocalizationsPl extends AppLocalizations { String get repeater_logging => 'Rejestrowanie'; @override - String get repeater_neighborsRepeaterOnly => 'Sąsiedzi (tylko powtarzacz)'; + String get repeater_neighborsRepeaterOnly => 'Sąsiedzi (tylko przekaźnik)'; @override String get repeater_regionManagementRepeaterOnly => - 'Zarządzanie Regionem (tylko Powtarzacz)'; + 'Zarządzanie Regionem (tylko Przekaźnik)'; @override String get repeater_regionNote => @@ -2664,7 +2663,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get neighbors_repeatersNeighbors => 'Powtarzacze Sąsiedzi'; + String get neighbors_repeatersNeighbors => 'Przekaźniki Sąsiedzi'; @override String get neighbors_noData => 'Brak danych dotyczących sąsiadów.'; @@ -2689,7 +2688,7 @@ class AppLocalizationsPl extends AppLocalizations { String get channelPath_otherObservedPaths => 'Inne zaobserwowane ścieżki'; @override - String get channelPath_repeaterHops => 'Skoki powtarzaczy'; + String get channelPath_repeaterHops => 'Skoki przekaźników'; @override String get channelPath_noHopDetails => @@ -2757,7 +2756,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get channelPath_noRepeaterLocations => - 'Brak dostępnych lokalizacji powtarzaczy dla tej ścieżki.'; + 'Brak dostępnych lokalizacji przekaźników dla tej ścieżki.'; @override String channelPath_primaryPath(int index) { @@ -2780,7 +2779,7 @@ class AppLocalizationsPl extends AppLocalizations { 'Brak dostępnych szczegółów hopa dla tego pakietu.'; @override - String get channelPath_unknownRepeater => 'Nieznany Powtarzacz'; + String get channelPath_unknownRepeater => 'Nieznany Przekaźnik'; @override String get community_title => 'Społeczność'; @@ -2986,7 +2985,7 @@ class AppLocalizationsPl extends AppLocalizations { String get listFilter_users => 'Użytkownicy'; @override - String get listFilter_repeaters => 'Powtarzacze'; + String get listFilter_repeaters => 'Przekaźniki'; @override String get listFilter_roomServers => 'Serwery pokoju'; @@ -3161,10 +3160,10 @@ class AppLocalizationsPl extends AppLocalizations { String get contacts_ping => 'Pingować'; @override - String get contacts_repeaterPathTrace => 'Śledzenie ścieżki do powtarzacza'; + String get contacts_repeaterPathTrace => 'Śledzenie ścieżki do przekaźnika'; @override - String get contacts_repeaterPing => 'Ping powtarzacza'; + String get contacts_repeaterPing => 'Ping przekaźnika'; @override String get contacts_roomPathTrace => @@ -3280,11 +3279,11 @@ class AppLocalizationsPl extends AppLocalizations { @override String get settings_gpxExportRepeaters => - 'Eksportuj powtórki / serwer pokojowy do GPX'; + 'Eksportuj przekaźniki / serwer pokojowy do GPX'; @override String get settings_gpxExportRepeatersSubtitle => - 'Eksportuje powtarzacze / roomserver z lokalizacją do pliku GPX.'; + 'Eksportuje przekaźniki / roomserver z lokalizacją do pliku GPX.'; @override String get settings_gpxExportContacts => 'Eksportuj towarzyszy do GPX'; @@ -3316,7 +3315,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get settings_gpxExportRepeatersRoom => - 'Lokalizacje serwerów powtarzających i pomieszczeń'; + 'Lokalizacje przekaźników i serwerów pokojowych'; @override String get settings_gpxExportChat => 'Lokalizacje towarzyszy'; @@ -3358,11 +3357,11 @@ class AppLocalizationsPl extends AppLocalizations { @override String get contactsSettings_autoAddRepeatersTitle => - 'Automatyczne dodawanie powtarzalników'; + 'Automatyczne dodawanie przekaźników'; @override String get contactsSettings_autoAddRepeatersSubtitle => - 'Zezwól towarzyszowi na automatyczne dodawanie odkrytych powtarzaczy.'; + 'Zezwól towarzyszowi na automatyczne dodawanie odkrytych przekaźników.'; @override String get contactsSettings_autoAddRoomServersTitle => diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 3ce551d..20a444b 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -213,8 +213,8 @@ "appSettings_batteryLifepo4": "LiFePO4 (2,6-3,65 V)", "appSettings_batteryLipo": "LiPo (3,0-4,2V)", "appSettings_mapDisplay": "Wyświetlanie mapy", - "appSettings_showRepeaters": "Pokaż powtarzacze", - "appSettings_showRepeatersSubtitle": "Wyświetl węzły powtarzaczy na mapie", + "appSettings_showRepeaters": "Pokaż przekaźniki", + "appSettings_showRepeatersSubtitle": "Wyświetl węzły przekaźników na mapie", "appSettings_showChatNodes": "Pokaż Węzły Rozmowy", "appSettings_showChatNodesSubtitle": "Wyświetl węzły czatu na mapie", "appSettings_showOtherNodes": "Pokaż inne węzły", @@ -279,7 +279,7 @@ } } }, - "contacts_manageRepeater": "Zarządzaj powtarzaczem", + "contacts_manageRepeater": "Zarządzaj przekaźnikiem", "contacts_roomLogin": "Logowanie do pokoju", "contacts_openChat": "Otwórz czat", "contacts_editGroup": "Edytuj Grupę", @@ -652,7 +652,7 @@ } }, "map_chat": "Rozmowa", - "map_repeater": "Powtarzacz", + "map_repeater": "Przekaźnik", "map_room": "Pokój", "map_sensor": "Czujnik", "map_pinDm": "Pinezka (DM)", @@ -683,7 +683,7 @@ "map_filterNodes": "Filtruj Węzły", "map_nodeTypes": "Typy węzłów", "map_chatNodes": "Węzły czatu", - "map_repeaters": "Powtarzacze", + "map_repeaters": "Przekaźniki", "map_otherNodes": "Inne węzły", "map_keyPrefix": "Prefiks klucza", "map_filterByKeyPrefix": "Filtruj po prefiksie klucza", @@ -693,7 +693,7 @@ "map_lastSeenTime": "Ostatni raz widziany", "map_sharedPin": "Udostępniona pinezka", "map_joinRoom": "Dołącz do pokoju", - "map_manageRepeater": "Zarządzaj powtarzaczem", + "map_manageRepeater": "Zarządzaj przekaźnikiem", "mapCache_title": "Pamięć podręczna map offline", "mapCache_selectAreaFirst": "Najpierw wybierz obszar do zapisania w pamięci podręcznej.", "mapCache_noTilesToDownload": "Brak kafelków do pobrania dla tego obszaru.", @@ -816,13 +816,13 @@ "time_allTime": "Cały czas", "dialog_disconnect": "Odłącz", "dialog_disconnectConfirm": "Czy na pewno chcesz się odłączyć od tego urządzenia?", - "login_repeaterLogin": "Logowanie do powtarzacza", + "login_repeaterLogin": "Logowanie do przekaźnika", "login_roomLogin": "Logowanie do pokoju", "login_password": "Hasło", "login_enterPassword": "Wprowadź hasło", "login_savePassword": "Zapisz hasło", "login_savePasswordSubtitle": "Hasło będzie bezpiecznie przechowywane na tym urządzeniu.", - "login_repeaterDescription": "Wprowadź hasło do powtarzacza, aby uzyskać dostęp do ustawień i stanu.", + "login_repeaterDescription": "Wprowadź hasło do przekaźnika, aby uzyskać dostęp do ustawień i stanu.", "login_roomDescription": "Wprowadź hasło do pokoju, aby uzyskać dostęp do ustawień i statusu.", "login_routing": "Trasowanie", "login_routingMode": "Tryb routingu", @@ -849,7 +849,7 @@ } } }, - "login_failedMessage": "Logowanie nie powiodło się. Hasło jest nieprawidłowe albo powtarzacz jest nieosiągalny.", + "login_failedMessage": "Logowanie nie powiodło się. Hasło jest nieprawidłowe albo przekaźnik jest nieosiągalny.", "common_reload": "Odśwież", "common_clear": "Wyczyść", "path_currentPath": "Aktualna ścieżka: {path}", @@ -875,7 +875,7 @@ "path_labelHexPrefixes": "Ścieżka (prefiksy hex)", "path_helperMaxHops": "Maksymalnie 64 skoki. Każdy prefiks ma 2 znaki szesnastkowe (1 bajt).", "path_selectFromContacts": "Albo wybierz z kontaktów:", - "path_noRepeatersFound": "Nie znaleziono powtarzaczy ani serwerów pokoi.", + "path_noRepeatersFound": "Nie znaleziono przekaźników ani serwerów pokoi.", "path_customPathsRequire": "Dostosowane ścieżki wymagają pośrednich skoków, które mogą przekazywać wiadomości.", "path_invalidHexPrefixes": "Nieprawidłowe prefiksy szesnastkowe: {prefixes}", "@path_invalidHexPrefixes": { @@ -887,17 +887,17 @@ }, "path_tooLong": "Ścieżka jest zbyt długa. Dozwolonych skoków wynosi 64.", "path_setPath": "Ustaw Ścieżkę", - "repeater_management": "Zarządzanie powtarzaczami", + "repeater_management": "Zarządzanie przekaźnikami", "repeater_managementTools": "Narzędzia Zarządzania", "repeater_status": "Status", - "repeater_statusSubtitle": "Wyświetl status powtarzacza, statystyki i sąsiadów.", + "repeater_statusSubtitle": "Wyświetl status przekaźnika, statystyki i sąsiadów.", "repeater_telemetry": "Telemetria", "repeater_telemetrySubtitle": "Wyświetl dane telemetryczne z czujników i statystyki systemu", "repeater_cli": "CLI", - "repeater_cliSubtitle": "Wyślij polecenia do powtarzacza", + "repeater_cliSubtitle": "Wyślij polecenia do przekaźnika", "repeater_settings": "Ustawienia", - "repeater_settingsSubtitle": "Skonfiguruj parametry powtarzacza", - "repeater_statusTitle": "Status powtarzacza", + "repeater_settingsSubtitle": "Skonfiguruj parametry przekaźnika", + "repeater_statusTitle": "Status przekaźnika", "repeater_routingMode": "Tryb routingu", "repeater_autoUseSavedPath": "Automatycznie (użyj zapisanej ścieżki)", "repeater_forceFloodMode": "Wymuś tryb zalewowy", @@ -992,10 +992,10 @@ } } }, - "repeater_settingsTitle": "Ustawienia powtarzacza", + "repeater_settingsTitle": "Ustawienia przekaźnika", "repeater_basicSettings": "Podstawowe Ustawienia", - "repeater_repeaterName": "Nazwa powtarzacza", - "repeater_repeaterNameHelper": "Wyświetl nazwę tego powtarzacza", + "repeater_repeaterName": "Nazwa przekaźnika", + "repeater_repeaterNameHelper": "Wyświetl nazwę tego przekaźnika", "repeater_adminPassword": "Hasło Administracyjne", "repeater_adminPasswordHelper": "Hasło z pełnym dostępem", "repeater_guestPassword": "Hasło gościa", @@ -1015,7 +1015,7 @@ "repeater_longitudeHelper": "Stopnie dziesiętne (np. -122,4194)", "repeater_features": "Funkcje", "repeater_packetForwarding": "Przekierowanie pakietów", - "repeater_packetForwardingSubtitle": "Włącz powtarzacz, aby przekazywać pakiety.", + "repeater_packetForwardingSubtitle": "Włącz przekaźnik, aby przekazywać pakiety.", "repeater_guestAccess": "Dostęp dla gości", "repeater_guestAccessSubtitle": "Umożliw dostęp tylko do odczytu dla gości.", "repeater_privacyMode": "Tryb prywatności", @@ -1041,15 +1041,15 @@ }, "repeater_encryptedAdvertInterval": "Interwał Zaszyfrowanego Rozgłoszenia", "repeater_dangerZone": "Strefa Zagrożeń", - "repeater_rebootRepeater": "Zrestartuj Powtarzacz", - "repeater_rebootRepeaterSubtitle": "Zrestartuj urządzenie powtarzające.", - "repeater_rebootRepeaterConfirm": "Czy na pewno chcesz zrestartować ten powtarzacz?", + "repeater_rebootRepeater": "Zrestartuj Przekaźnik", + "repeater_rebootRepeaterSubtitle": "Zrestartuj przekaźnik.", + "repeater_rebootRepeaterConfirm": "Czy na pewno chcesz zrestartować ten przekaźnik?", "repeater_regenerateIdentityKey": "Wygeneruj klucz tożsamości", "repeater_regenerateIdentityKeySubtitle": "Wygeneruj nową parę kluczy publicznych/prywatnych", - "repeater_regenerateIdentityKeyConfirm": "Zostanie wygenerowana nowa tożsamość dla powtarzacza. Kontynuować?", + "repeater_regenerateIdentityKeyConfirm": "Zostanie wygenerowana nowa tożsamość dla przekaźnika. Kontynuować?", "repeater_eraseFileSystem": "Wyczyść System Plików", - "repeater_eraseFileSystemSubtitle": "Sformatuj system plików powtarzacza", - "repeater_eraseFileSystemConfirm": "OSTRZEŻENIE: To spowoduje usunięcie wszystkich danych z powtarzacza. Nie da się tego cofnąć!", + "repeater_eraseFileSystemSubtitle": "Sformatuj system plików przekaźnika", + "repeater_eraseFileSystemConfirm": "OSTRZEŻENIE: To spowoduje usunięcie wszystkich danych z przekaźnika. Nie da się tego cofnąć!", "repeater_eraseSerialOnly": "Usunięcie jest dostępne tylko przez konsolę szeregową.", "repeater_commandSent": "Polecenie wysłane: {command}", "@repeater_commandSent": { @@ -1101,7 +1101,7 @@ } } }, - "repeater_cliTitle": "Powtarzacz CLI", + "repeater_cliTitle": "Przekaźnik CLI", "repeater_debugNextCommand": "Debug Następną Komendę", "repeater_commandHelp": "Pomoc", "repeater_clearHistory": "Wyczyść historię", @@ -1135,7 +1135,7 @@ "repeater_cliHelpClearStats": "Resetuje różne wskaźniki statystyk do zera.", "repeater_cliHelpSetAf": "Ustawia czynnik czasu powietrznego.", "repeater_cliHelpSetTx": "Ustawia moc transmisji LoRa w dBm. (zrestartuj, aby zastosować)", - "repeater_cliHelpSetRepeat": "Włącza lub wyłącza rolę powtarzacza dla tego węzła.", + "repeater_cliHelpSetRepeat": "Włącza lub wyłącza rolę przekaźnika dla tego węzła.", "repeater_cliHelpSetAllowReadOnly": "(Serwer pokoju) Jeśli 'włączone', to logowanie z pustym hasłem będzie dozwolone, ale nie można publikować w pokoju (tylko czytać).", "repeater_cliHelpSetFloodMax": "Ustawia maksymalną liczbę skoków pakietu zalewowego (jeśli >= max, pakiet nie jest przekierowywany)", "repeater_cliHelpSetIntThresh": "Ustawia Próg Interferencji (w dB). Domyślnie wynosi 14. Ustaw na 0, aby wyłączyć wykrywanie zakłóceń kanału.", @@ -1143,7 +1143,7 @@ "repeater_cliHelpSetMultiAcks": "Włącza lub wyłącza funkcję 'podwójnych potwierdzeń'.", "repeater_cliHelpSetAdvertInterval": "Ustawia interwał timera w minutach do wysyłania pakietu rozgłoszenia lokalnego (bezpośredniego). Ustaw na 0, aby wyłączyć.", "repeater_cliHelpSetFloodAdvertInterval": "Ustawia interwał timera w godzinach do wysłania pakietu rozgłoszeniowego typu \"flood\". Ustaw na 0, aby wyłączyć.", - "repeater_cliHelpSetGuestPassword": "Ustawia/aktualizuje hasło gościa. (dla powtarzaczy loginy gości mogą wysyłać żądanie \"Get Stats\")", + "repeater_cliHelpSetGuestPassword": "Ustawia/aktualizuje hasło gościa. (dla przekaźników loginy gości mogą wysyłać żądanie \"Get Stats\")", "repeater_cliHelpSetName": "Ustawia nazwę rozgłoszenia.", "repeater_cliHelpSetLat": "Ustawia współrzędną geograficzną (w stopniach dziesiętnych) mapy rozgłoszeń.", "repeater_cliHelpSetLon": "Ustawia współrzędną długościową mapy rozgłoszeń. (stopnie dziesiętne)", @@ -1187,8 +1187,8 @@ "repeater_settingsCategory": "Ustawienia", "repeater_bridge": "Most", "repeater_logging": "Rejestrowanie", - "repeater_neighborsRepeaterOnly": "Sąsiedzi (tylko powtarzacz)", - "repeater_regionManagementRepeaterOnly": "Zarządzanie Regionem (tylko Powtarzacz)", + "repeater_neighborsRepeaterOnly": "Sąsiedzi (tylko przekaźnik)", + "repeater_regionManagementRepeaterOnly": "Zarządzanie Regionem (tylko Przekaźnik)", "repeater_regionNote": "Wprowadzono komendy regionalne w celu zarządzania definicjami i uprawnieniami regionów.", "repeater_gpsManagement": "Zarządzanie GPS", "repeater_gpsNote": "Polecenie GPS zostało wprowadzone w celu zarządzania tematami związanymi z lokalizacją.", @@ -1257,7 +1257,7 @@ "channelPath_title": "Ścieżka pakietu", "channelPath_viewMap": "Wyświetl mapę", "channelPath_otherObservedPaths": "Inne zaobserwowane ścieżki", - "channelPath_repeaterHops": "Skoki powtarzaczy", + "channelPath_repeaterHops": "Skoki przekaźników", "channelPath_noHopDetails": "Szczegóły dotyczące tego pakietu nie zostały podane.", "channelPath_messageDetails": "Szczegóły wiadomości", "channelPath_senderLabel": "Nadawca", @@ -1322,7 +1322,7 @@ } }, "channelPath_mapTitle": "Mapa ścieżek", - "channelPath_noRepeaterLocations": "Brak dostępnych lokalizacji powtarzaczy dla tej ścieżki.", + "channelPath_noRepeaterLocations": "Brak dostępnych lokalizacji przekaźników dla tej ścieżki.", "channelPath_primaryPath": "Ścieżka {index} (Główna)", "@channelPath_primaryPath": { "placeholders": { @@ -1352,7 +1352,7 @@ } }, "channelPath_noHopDetailsAvailable": "Brak dostępnych szczegółów hopa dla tego pakietu.", - "channelPath_unknownRepeater": "Nieznany Powtarzacz", + "channelPath_unknownRepeater": "Nieznany Przekaźnik", "listFilter_tooltip": "Filtruj i sortuj", "listFilter_sortBy": "Sortuj po", "listFilter_latestMessages": "Najnowsze wiadomości", @@ -1361,7 +1361,7 @@ "listFilter_filters": "Filtry", "listFilter_all": "Wszystko", "listFilter_users": "Użytkownicy", - "listFilter_repeaters": "Powtarzacze", + "listFilter_repeaters": "Przekaźniki", "listFilter_roomServers": "Serwery pokoju", "listFilter_unreadOnly": "Tylko nieprzeczytane", "listFilter_newGroup": "Nowa grupa", @@ -1377,7 +1377,7 @@ "neighbors_receivedData": "Otrzymano dane sąsiedztwa", "neighbors_requestTimedOut": "Sąsiedzi proszą o wyłączenie timingu.", "neighbors_errorLoading": "Błąd podczas ładowania sąsiadów: {error}", - "neighbors_repeatersNeighbors": "Powtarzacze Sąsiedzi", + "neighbors_repeatersNeighbors": "Przekaźniki Sąsiedzi", "neighbors_noData": "Brak danych dotyczących sąsiadów.", "channels_joinPrivateChannelDesc": "Ręcznie wprowadź klucz tajny.", "channels_createPrivateChannel": "Utwórz Prywatny Kanał", @@ -1562,11 +1562,11 @@ "pathTrace_notAvailable": "Ścieżka śledzenia niedostępna.", "contacts_pathTrace": "Śledzenie Ścieżek", "contacts_ping": "Pingować", - "contacts_repeaterPathTrace": "Śledzenie ścieżki do powtarzacza", + "contacts_repeaterPathTrace": "Śledzenie ścieżki do przekaźnika", "contacts_roomPathTrace": "Śledzenie ścieżki do serwera pokojowego", "contacts_roomPing": "Pinguj serwer pokoju", "pathTrace_refreshTooltip": "Odśwież ścieżkę.", - "contacts_repeaterPing": "Ping powtarzacza", + "contacts_repeaterPing": "Ping przekaźnika", "contacts_pathTraceTo": "Śledź trasę do {name}", "contacts_chatTraceRoute": "Śledź trasę promienia", "appSettings_languageRu": "Rosyjski", @@ -1622,12 +1622,12 @@ }, "notification_receivedNewMessage": "Otrzymano nową wiadomość", "settings_gpxExportContacts": "Eksportuj towarzyszy do GPX", - "settings_gpxExportRepeaters": "Eksportuj powtórki / serwer pokojowy do GPX", - "settings_gpxExportRepeatersSubtitle": "Eksportuje powtarzacze / roomserver z lokalizacją do pliku GPX.", + "settings_gpxExportRepeaters": "Eksportuj przekaźniki / serwer pokojowy do GPX", + "settings_gpxExportRepeatersSubtitle": "Eksportuje przekaźniki / roomserver z lokalizacją do pliku GPX.", "settings_gpxExportSuccess": "Pomyślnie wyeksportowano plik GPX.", "settings_gpxExportNotAvailable": "Nie obsługiwane na Twoim urządzeniu/systemie operacyjnym", "settings_gpxExportError": "Wystąpił błąd podczas eksportowania.", - "settings_gpxExportRepeatersRoom": "Lokalizacje serwerów powtarzających i pomieszczeń", + "settings_gpxExportRepeatersRoom": "Lokalizacje przekaźników i serwerów pokojowych", "settings_gpxExportContactsSubtitle": "Eksportuje towarzyszy z lokalizacją do pliku GPX.", "settings_gpxExportAll": "Eksportuj wszystkie kontakty do GPX", "settings_gpxExportAllSubtitle": "Eksportuje wszystkie kontakty z lokalizacją do pliku GPX.", @@ -1840,12 +1840,12 @@ "contacts_searchFavorites": "Wyszukaj {number}{str} ulubione...", "contacts_searchRoomServers": "Wyszukaj {number}{str} serwerów Room...", "contacts_searchUsers": "Wyszukaj {number}{str} Użytkowników...", - "contacts_searchRepeaters": "Wyszukaj {number}{str} powtórników...", + "contacts_searchRepeaters": "Wyszukaj {number}{str} przekaźników...", "contactsSettings_title": "Ustawienia kontaktów", "settings_contactSettingsSubtitle": "Ustawienia dotyczące sposobu dodawania kontaktów", "contactsSettings_autoAddUsersSubtitle": "Pozwól towarzyszowi automatycznie dodawać znalezione użytkowników.", - "contactsSettings_autoAddRepeatersTitle": "Automatyczne dodawanie powtarzalników", - "contactsSettings_autoAddRepeatersSubtitle": "Zezwól towarzyszowi na automatyczne dodawanie odkrytych powtarzaczy.", + "contactsSettings_autoAddRepeatersTitle": "Automatyczne dodawanie przekaźników", + "contactsSettings_autoAddRepeatersSubtitle": "Zezwól towarzyszowi na automatyczne dodawanie odkrytych przekaźników.", "contactsSettings_autoAddRoomServersTitle": "Automatycznie dodaj serwery pokojowe", "contactsSettings_autoAddUsersTitle": "Automatycznie dodaj użytkowników", "settings_contactSettings": "Ustawienia kontaktów", From fc7283f07663ed8742a8df76c335f0b8a61e3e4d Mon Sep 17 00:00:00 2001 From: Winston Lowe Date: Mon, 23 Mar 2026 11:18:37 -0700 Subject: [PATCH 21/25] Update lib/l10n/app_bg.arb Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/l10n/app_bg.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/l10n/app_bg.arb b/lib/l10n/app_bg.arb index 13788b8..545ec9d 100644 --- a/lib/l10n/app_bg.arb +++ b/lib/l10n/app_bg.arb @@ -1892,7 +1892,7 @@ "map_setAsMyLocation": "Задайте като моя местоположение", "@path_routeWeight": { "placeholders": { - "value": { + "weight": { "type": "String" }, "max": { From 0228c3862165bf91a3243f2b80572445674e91f9 Mon Sep 17 00:00:00 2001 From: Winston Lowe Date: Mon, 23 Mar 2026 11:24:33 -0700 Subject: [PATCH 22/25] fix: Update battery voltage reading and adjust path length handling in ChannelMessage --- lib/connector/meshcore_connector.dart | 2 +- lib/models/channel_message.dart | 6 ++---- lib/screens/settings_screen.dart | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 04e9583..ee9533e 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -3043,7 +3043,7 @@ class MeshCoreConnector extends ChangeNotifier { try { final reader = BufferReader(frame); reader.skipBytes(1); - _batteryMillivolts = reader.readInt16LE(); + _batteryMillivolts = reader.readUInt16LE(); _storageUsedKb = reader.readUInt32LE(); _storageTotalKb = reader.readUInt32LE(); final volts = (_batteryMillivolts! / 1000.0).toStringAsFixed(2); diff --git a/lib/models/channel_message.dart b/lib/models/channel_message.dart index 0f0a5b4..7c09089 100644 --- a/lib/models/channel_message.dart +++ b/lib/models/channel_message.dart @@ -132,17 +132,15 @@ class ChannelMessage { final hasPath = (flags & 0x01) != 0; reader.skipBytes(1); // Skip reserved byte channelIdx = reader.readByte(); - pathLen = reader.readByte(); + pathLen = reader.readInt8(); txtType = reader.readByte(); if (hasPath && pathLen > 0) { reader.rewind(); // Rewind to read path length again for pathBytes pathBytes = reader.readBytes(pathLen); - } else { - pathLen = 0; } } else { channelIdx = reader.readByte(); - pathLen = reader.readByte(); + pathLen = reader.readInt8(); txtType = reader.readByte(); } final timestampRaw = reader.readUInt32LE(); diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index cc61143..46d6352 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -975,7 +975,6 @@ void _privacySettings(BuildContext context, MeshCoreConnector connector) { value: advertLocPolicy, onChanged: (value) { setDialogState(() => advertLocPolicy = value); - advertLocPolicy = value; }, ), const SizedBox(height: 8), From 5f475fce4d6dc8fabe14e52843670b971de750af Mon Sep 17 00:00:00 2001 From: thesebas Date: Mon, 23 Mar 2026 22:53:09 +0100 Subject: [PATCH 23/25] use correct translation for Advert in another few places --- lib/l10n/app_localizations_pl.dart | 15 ++++++++------- lib/l10n/app_pl.arb | 14 +++++++------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 06efab3..37b0107 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -3194,13 +3194,13 @@ class AppLocalizationsPl extends AppLocalizations { 'Kontakt nie został zaimportowany.'; @override - String get contacts_zeroHopAdvert => 'Reklama Zero Hop'; + String get contacts_zeroHopAdvert => 'Rozgłoszenie zero-hop'; @override - String get contacts_floodAdvert => 'Reklama powodziowa'; + String get contacts_floodAdvert => 'Rozgłoszenie zalewowe'; @override - String get contacts_copyAdvertToClipboard => 'Kopiuj ogłoszenie do schowka'; + String get contacts_copyAdvertToClipboard => 'Kopiuj rozgłoszenie do schowka'; @override String get contacts_addContactFromClipboard => 'Dodaj kontakt z schowka'; @@ -3210,22 +3210,23 @@ class AppLocalizationsPl extends AppLocalizations { @override String get contacts_ShareContactZeroHop => - 'Udostępnij kontakt przez ogłoszenie'; + 'Udostępnij kontakt przez rozgłoszenie'; @override String get contacts_zeroHopContactAdvertSent => - 'Wysłano kontakt przez ogłoszenie.'; + 'Wysłano kontakt przez rozgłoszenie.'; @override String get contacts_zeroHopContactAdvertFailed => 'Nie udało się wysłać kontaktu.'; @override - String get contacts_contactAdvertCopied => 'Reklama skopiowana do schowka.'; + String get contacts_contactAdvertCopied => + 'Rozgłoszenie skopiowano do schowka.'; @override String get contacts_contactAdvertCopyFailed => - 'Kopiowanie ogłoszenia do schowka nie powiodło się.'; + 'Kopiowanie rozgłoszenia do schowka nie powiodło się.'; @override String get notification_activityTitle => 'Aktywność MeshCore'; diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 20a444b..8466a8b 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -1574,17 +1574,17 @@ "appSettings_enableMessageTracing": "Włącz śledzenie wiadomości", "appSettings_enableMessageTracingSubtitle": "Pokaż szczegółowe metadane trasowania i czasu dla wiadomości", "contacts_contactImportFailed": "Kontakt nie został zaimportowany.", - "contacts_zeroHopAdvert": "Reklama Zero Hop", - "contacts_floodAdvert": "Reklama powodziowa", - "contacts_copyAdvertToClipboard": "Kopiuj ogłoszenie do schowka", + "contacts_zeroHopAdvert": "Rozgłoszenie zero-hop", + "contacts_floodAdvert": "Rozgłoszenie zalewowe", + "contacts_copyAdvertToClipboard": "Kopiuj rozgłoszenie do schowka", "contacts_clipboardEmpty": "Schowek jest pusty.", "contacts_invalidAdvertFormat": "Nieprawidłowe dane kontaktowe", "contacts_addContactFromClipboard": "Dodaj kontakt z schowka", "contacts_contactImported": "Kontakt został zaimportowany.", - "contacts_zeroHopContactAdvertSent": "Wysłano kontakt przez ogłoszenie.", - "contacts_contactAdvertCopied": "Reklama skopiowana do schowka.", - "contacts_contactAdvertCopyFailed": "Kopiowanie ogłoszenia do schowka nie powiodło się.", - "contacts_ShareContactZeroHop": "Udostępnij kontakt przez ogłoszenie", + "contacts_zeroHopContactAdvertSent": "Wysłano kontakt przez rozgłoszenie.", + "contacts_contactAdvertCopied": "Rozgłoszenie skopiowano do schowka.", + "contacts_contactAdvertCopyFailed": "Kopiowanie rozgłoszenia do schowka nie powiodło się.", + "contacts_ShareContactZeroHop": "Udostępnij kontakt przez rozgłoszenie", "contacts_ShareContact": "Kopiuj kontakt do schowka", "contacts_zeroHopContactAdvertFailed": "Nie udało się wysłać kontaktu.", "notification_activityTitle": "Aktywność MeshCore", From 9b1f1e19948fb616b25fbd5e5f717a2b614a4473 Mon Sep 17 00:00:00 2001 From: thesebas Date: Mon, 23 Mar 2026 23:07:00 +0100 Subject: [PATCH 24/25] make the 'lastSeen' labels shorter to not break the contacts list layout --- lib/l10n/app_localizations_pl.dart | 12 ++++++------ lib/l10n/app_pl.arb | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 37b0107..0457b28 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -973,27 +973,27 @@ class AppLocalizationsPl extends AppLocalizations { String get contacts_noMembers => 'Brak członków'; @override - String get contacts_lastSeenNow => 'Widziano przed chwilą'; + String get contacts_lastSeenNow => 'niedawno'; @override String contacts_lastSeenMinsAgo(int minutes) { - return 'Widziano $minutes min temu'; + return '~ $minutes min'; } @override - String get contacts_lastSeenHourAgo => 'Ostatni raz widziany 1 godzinę temu'; + String get contacts_lastSeenHourAgo => '~ 1 godz.'; @override String contacts_lastSeenHoursAgo(int hours) { - return 'Widziano $hours godz. temu'; + return '~ $hours godz.'; } @override - String get contacts_lastSeenDayAgo => 'Ostatni raz widziany 1 dzień temu'; + String get contacts_lastSeenDayAgo => '~ 1 dzień'; @override String contacts_lastSeenDaysAgo(int days) { - return 'Widziano $days dni temu'; + return '~ $days dni'; } @override diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 8466a8b..fe41c20 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -307,8 +307,8 @@ "contacts_filterContacts": "Filtruj kontakty...", "contacts_noContactsMatchFilter": "Brak pasujących kontaktów do Twojego filtra", "contacts_noMembers": "Brak członków", - "contacts_lastSeenNow": "Widziano przed chwilą", - "contacts_lastSeenMinsAgo": "Widziano {minutes} min temu", + "contacts_lastSeenNow": "niedawno", + "contacts_lastSeenMinsAgo": "~ {minutes} min", "@contacts_lastSeenMinsAgo": { "placeholders": { "minutes": { @@ -316,8 +316,8 @@ } } }, - "contacts_lastSeenHourAgo": "Ostatni raz widziany 1 godzinę temu", - "contacts_lastSeenHoursAgo": "Widziano {hours} godz. temu", + "contacts_lastSeenHourAgo": "~ 1 godz.", + "contacts_lastSeenHoursAgo": "~ {hours} godz.", "@contacts_lastSeenHoursAgo": { "placeholders": { "hours": { @@ -325,8 +325,8 @@ } } }, - "contacts_lastSeenDayAgo": "Ostatni raz widziany 1 dzień temu", - "contacts_lastSeenDaysAgo": "Widziano {days} dni temu", + "contacts_lastSeenDayAgo": "~ 1 dzień", + "contacts_lastSeenDaysAgo": "~ {days} dni", "@contacts_lastSeenDaysAgo": { "placeholders": { "days": { From f63bc4b7874c77fa48a12923cd15f3e577267f68 Mon Sep 17 00:00:00 2001 From: thesebas Date: Mon, 23 Mar 2026 23:11:51 +0100 Subject: [PATCH 25/25] some minor adjsts --- lib/l10n/app_localizations_pl.dart | 4 ++-- lib/l10n/app_pl.arb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 0457b28..6fb41dc 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -2490,7 +2490,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_cliHelpNeighbors => - 'Wyświetla listę innych węzłów powtarzających się, które usłyszano dzięki rozgłoszeniom zero-hop. Każda linia to: id-prefix-hex:timestamp:snr-times-4'; + 'Wyświetla listę innych węzłów przekaźnikowych usłyszanych przez rozgłoszenia zero-hop. Każda linia to: id-prefix-hex:timestamp:snr-times-4'; @override String get repeater_cliHelpNeighborRemove => @@ -2663,7 +2663,7 @@ class AppLocalizationsPl extends AppLocalizations { } @override - String get neighbors_repeatersNeighbors => 'Przekaźniki Sąsiedzi'; + String get neighbors_repeatersNeighbors => 'Sąsiedzi przekaźników'; @override String get neighbors_noData => 'Brak danych dotyczących sąsiadów.'; diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index fe41c20..9cf0afc 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -1163,7 +1163,7 @@ "repeater_cliHelpLogStart": "Rozpoczyna się logowanie pakietów do systemu plików.", "repeater_cliHelpLogStop": "Zatrzymuje logowanie pakietów do systemu plików.", "repeater_cliHelpLogErase": "Usuwa logi pakietów z systemu plików.", - "repeater_cliHelpNeighbors": "Wyświetla listę innych węzłów powtarzających się, które usłyszano dzięki rozgłoszeniom zero-hop. Każda linia to: id-prefix-hex:timestamp:snr-times-4", + "repeater_cliHelpNeighbors": "Wyświetla listę innych węzłów przekaźnikowych usłyszanych przez rozgłoszenia zero-hop. Każda linia to: id-prefix-hex:timestamp:snr-times-4", "repeater_cliHelpNeighborRemove": "Usuwa pierwszy pasujący wpis (z prefiksem pubkey (hex)) z listy sąsiadów.", "repeater_cliHelpRegion": "(tylko port szeregowy) Wyświetla wszystkie zdefiniowane regiony i aktualne uprawnienia do zalewu.", "repeater_cliHelpRegionLoad": "UWAGA: to jest specjalne wywołanie wielokomendowe. Każda następna komenda jest nazwą regionu (wcięta spacjami, aby wskazywać hierarchię nadrzędną, z minimum jedną spacją). Zakończona wysłaniem pustej linii/komendy.", @@ -1377,7 +1377,7 @@ "neighbors_receivedData": "Otrzymano dane sąsiedztwa", "neighbors_requestTimedOut": "Sąsiedzi proszą o wyłączenie timingu.", "neighbors_errorLoading": "Błąd podczas ładowania sąsiadów: {error}", - "neighbors_repeatersNeighbors": "Przekaźniki Sąsiedzi", + "neighbors_repeatersNeighbors": "Sąsiedzi przekaźników", "neighbors_noData": "Brak danych dotyczących sąsiadów.", "channels_joinPrivateChannelDesc": "Ręcznie wprowadź klucz tajny.", "channels_createPrivateChannel": "Utwórz Prywatny Kanał",